Spaces:
Sleeping
Sleeping
| # 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.')) |