|
|
|
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.')) |