|
|
|
import os |
|
import anthropic |
|
import streamlit as st |
|
import logging |
|
import time |
|
import json |
|
from datetime import datetime, timezone |
|
|
|
|
|
from ..utils.widget_utils import generate_unique_key |
|
from ..database.current_situation_mongo_db import store_current_situation_result |
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
TEXT_TYPES = { |
|
'es': { |
|
'academic_article': 'artículo académico', |
|
'university_work': 'trabajo universitario', |
|
'general_communication': 'comunicación general' |
|
}, |
|
'en': { |
|
'academic_article': 'academic article', |
|
'university_work': 'university work', |
|
'general_communication': 'general communication' |
|
}, |
|
'fr': { |
|
'academic_article': 'article académique', |
|
'university_work': 'travail universitaire', |
|
'general_communication': 'communication générale' |
|
} |
|
} |
|
|
|
|
|
recommendation_cache = {} |
|
|
|
def get_recommendation_cache_key(text, metrics, text_type, lang_code): |
|
""" |
|
Generate a cache key for recommendations. |
|
""" |
|
|
|
text_hash = hash(text[:1000]) |
|
metrics_hash = hash(json.dumps(metrics, sort_keys=True)) |
|
return f"{text_hash}_{metrics_hash}_{text_type}_{lang_code}" |
|
|
|
def format_metrics_for_claude(metrics, lang_code, text_type): |
|
""" |
|
Format metrics in a way that's readable for Claude |
|
""" |
|
formatted_metrics = {} |
|
for key, value in metrics.items(): |
|
if isinstance(value, (int, float)): |
|
formatted_metrics[key] = round(value, 2) |
|
else: |
|
formatted_metrics[key] = value |
|
|
|
|
|
text_type_label = TEXT_TYPES.get(lang_code, {}).get(text_type, text_type) |
|
formatted_metrics['text_type'] = text_type_label |
|
|
|
return formatted_metrics |
|
|
|
def generate_claude_recommendations(text, metrics, text_type, lang_code): |
|
""" |
|
Generate personalized recommendations using Claude API. |
|
""" |
|
try: |
|
api_key = os.environ.get("ANTHROPIC_API_KEY") |
|
if not api_key: |
|
logger.error("Claude API key not found in environment variables") |
|
return get_fallback_recommendations(lang_code) |
|
|
|
|
|
cache_key = get_recommendation_cache_key(text, metrics, text_type, lang_code) |
|
if cache_key in recommendation_cache: |
|
logger.info("Using cached recommendations") |
|
return recommendation_cache[cache_key] |
|
|
|
|
|
formatted_metrics = format_metrics_for_claude(metrics, lang_code, text_type) |
|
|
|
|
|
if lang_code == 'es': |
|
system_prompt = """Eres un asistente especializado en análisis de textos académicos y comunicación escrita. |
|
Tu tarea es analizar el texto del usuario y proporcionar recomendaciones personalizadas. |
|
Usa un tono constructivo y específico. Sé claro y directo con tus sugerencias. |
|
""" |
|
user_prompt = f"""Por favor, analiza este texto de tipo '{formatted_metrics['text_type']}' |
|
y proporciona recomendaciones personalizadas para mejorarlo. |
|
|
|
MÉTRICAS DE ANÁLISIS: |
|
{json.dumps(formatted_metrics, indent=2, ensure_ascii=False)} |
|
|
|
TEXTO A ANALIZAR: |
|
{text[:2000]} # Limitamos el texto para evitar exceder tokens |
|
|
|
Proporciona tu análisis con el siguiente formato: |
|
1. Un resumen breve (2-3 frases) del análisis general |
|
2. 3-4 recomendaciones específicas y accionables (cada una de 1-2 frases) |
|
3. Un ejemplo concreto de mejora tomado del propio texto del usuario |
|
4. Una sugerencia sobre qué herramienta de AIdeaText usar (Análisis Morfosintáctico, Análisis Semántico o Análisis del Discurso) |
|
|
|
Tu respuesta debe ser concisa y no exceder los 300 palabras.""" |
|
else: |
|
|
|
system_prompt = """You are an assistant specialized in analyzing academic texts and written communication. |
|
Your task is to analyze the user's text and provide personalized recommendations. |
|
Use a constructive and specific tone. Be clear and direct with your suggestions. |
|
""" |
|
user_prompt = f"""Please analyze this text of type '{formatted_metrics['text_type']}' |
|
and provide personalized recommendations to improve it. |
|
|
|
ANALYSIS METRICS: |
|
{json.dumps(formatted_metrics, indent=2, ensure_ascii=False)} |
|
|
|
TEXT TO ANALYZE: |
|
{text[:2000]} # Limiting text to avoid exceeding tokens |
|
|
|
Provide your analysis with the following format: |
|
1. A brief summary (2-3 sentences) of the general analysis |
|
2. 3-4 specific and actionable recommendations (each 1-2 sentences) |
|
3. A concrete example of improvement taken from the user's own text |
|
4. A suggestion about which AIdeaText tool to use (Morphosyntactic Analysis, Semantic Analysis or Discourse Analysis) |
|
|
|
Your response should be concise and not exceed 300 words.""" |
|
|
|
|
|
client = anthropic.Anthropic(api_key=api_key) |
|
|
|
|
|
start_time = time.time() |
|
response = client.messages.create( |
|
model="claude-3-5-sonnet-20241022", |
|
max_tokens=1024, |
|
temperature=0.7, |
|
system=system_prompt, |
|
messages=[ |
|
{"role": "user", "content": user_prompt} |
|
] |
|
) |
|
logger.info(f"Claude API call completed in {time.time() - start_time:.2f} seconds") |
|
|
|
|
|
recommendations = response.content[0].text |
|
|
|
|
|
recommendation_cache[cache_key] = recommendations |
|
|
|
return recommendations |
|
except Exception as e: |
|
logger.error(f"Error generating recommendations with Claude: {str(e)}") |
|
return get_fallback_recommendations(lang_code) |
|
|
|
def get_fallback_recommendations(lang_code): |
|
""" |
|
Return fallback recommendations if Claude API fails |
|
""" |
|
if lang_code == 'es': |
|
return """ |
|
**Análisis General** |
|
Tu texto presenta una estructura básica adecuada, pero hay áreas que pueden mejorarse para mayor claridad y cohesión. |
|
|
|
**Recomendaciones**: |
|
- Intenta variar tu vocabulario para evitar repeticiones innecesarias |
|
- Considera revisar la longitud de tus oraciones para mantener un mejor ritmo |
|
- Asegúrate de establecer conexiones claras entre las ideas principales |
|
- Revisa la consistencia en el uso de tiempos verbales |
|
|
|
**Herramienta recomendada**: |
|
Te sugerimos utilizar el Análisis Morfosintáctico para identificar patrones en tu estructura de oraciones. |
|
""" |
|
else: |
|
return """ |
|
**General Analysis** |
|
Your text presents an adequate basic structure, but there are areas that can be improved for better clarity and cohesion. |
|
|
|
**Recommendations**: |
|
- Try to vary your vocabulary to avoid unnecessary repetition |
|
- Consider reviewing the length of your sentences to maintain a better rhythm |
|
- Make sure to establish clear connections between main ideas |
|
- Check consistency in the use of verb tenses |
|
|
|
**Recommended tool**: |
|
We suggest using Morphosyntactic Analysis to identify patterns in your sentence structure. |
|
""" |
|
|
|
|
|
|
|
|
|
def store_recommendations(username, text, metrics, text_type, recommendations): |
|
""" |
|
Store the recommendations in the database |
|
""" |
|
try: |
|
|
|
from ..database.claude_recommendations_mongo_db import store_claude_recommendation |
|
|
|
|
|
result = store_claude_recommendation( |
|
username=username, |
|
text=text, |
|
metrics=metrics, |
|
text_type=text_type, |
|
recommendations=recommendations |
|
) |
|
|
|
logger.info(f"Recommendations stored successfully: {result}") |
|
return result |
|
except Exception as e: |
|
logger.error(f"Error storing recommendations: {str(e)}") |
|
return False |
|
|
|
|
|
|
|
|
|
def display_personalized_recommendations(text, metrics, text_type, lang_code, t): |
|
""" |
|
Display personalized recommendations based on text analysis |
|
""" |
|
try: |
|
|
|
recommendations = generate_claude_recommendations(text, metrics, text_type, lang_code) |
|
|
|
|
|
st.markdown("### 📝 " + t.get('recommendations_title', 'Personalized Recommendations')) |
|
|
|
with st.container(): |
|
st.markdown(f""" |
|
<div style="padding: 20px; border-radius: 10px; |
|
background-color: #f8f9fa; margin-bottom: 20px;"> |
|
{recommendations} |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.info("💡 **" + t.get('assistant_prompt', 'For further improvement:') + "** " + |
|
t.get('assistant_message', 'Open the virtual assistant (powered by Claude AI) in the upper left corner by clicking the arrow next to the logo.')) |
|
|
|
|
|
col1, col2, col3 = st.columns([1,1,1]) |
|
with col2: |
|
if st.button( |
|
t.get('save_button', 'Save Analysis'), |
|
key=generate_unique_key("claude_recommendations", "save"), |
|
type="primary", |
|
use_container_width=True |
|
): |
|
if 'username' in st.session_state: |
|
success = store_recommendations( |
|
st.session_state.username, |
|
text, |
|
metrics, |
|
text_type, |
|
recommendations |
|
) |
|
if success: |
|
st.success(t.get('save_success', 'Analysis saved successfully')) |
|
else: |
|
st.error(t.get('save_error', 'Error saving analysis')) |
|
else: |
|
st.error(t.get('login_required', 'Please log in to save analysis')) |
|
|
|
except Exception as e: |
|
logger.error(f"Error displaying recommendations: {str(e)}") |
|
st.error(t.get('recommendations_error', 'Error generating recommendations. Please try again later.')) |