v4 / modules /studentact /claude_recommendations.py
AIdeaText's picture
Upload 14 files
1fc131a verified
# modules/studentact/claude_recommendations.py
import os
import anthropic
import streamlit as st
import logging
import time
import json
from datetime import datetime, timezone
# Local imports
from ..utils.widget_utils import generate_unique_key
from ..database.current_situation_mongo_db import store_current_situation_result
logger = logging.getLogger(__name__)
# Define text types
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'
}
}
# Cache for recommendations to avoid redundant API calls
recommendation_cache = {}
def get_recommendation_cache_key(text, metrics, text_type, lang_code):
"""
Generate a cache key for recommendations.
"""
# Create a simple hash based on text content and metrics
text_hash = hash(text[:1000]) # Only use first 1000 chars for hashing
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
# Add context about what type of text this is
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)
# Check cache first
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]
# Format metrics for Claude
formatted_metrics = format_metrics_for_claude(metrics, lang_code, text_type)
# Determine language for prompt
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:
# Default to English
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."""
# Initialize Claude client
client = anthropic.Anthropic(api_key=api_key)
# Call Claude API
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")
# Extract recommendations
recommendations = response.content[0].text
# Cache the result
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:
# Importar la función de almacenamiento de recomendaciones
from ..database.claude_recommendations_mongo_db import store_claude_recommendation
# Guardar usando la nueva función especializada
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:
# Generate recommendations
recommendations = generate_claude_recommendations(text, metrics, text_type, lang_code)
# Format and display recommendations in a nice container
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)
# Add prompt to use assistant
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.'))
# Add save button
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.'))