AIdeaText commited on
Commit
2c8b792
verified
1 Parent(s): 7d5d48d

Update modules/studentact/claude_recommendations.py

Browse files
modules/studentact/claude_recommendations.py CHANGED
@@ -1,122 +1,259 @@
1
  # modules/studentact/claude_recommendations.py
2
-
3
- import anthropic
4
  import os
 
5
  import streamlit as st
6
  import logging
7
- from typing import Dict, Any
8
  import json
 
 
 
 
 
9
 
10
  logger = logging.getLogger(__name__)
11
 
12
- def generate_claude_recommendations(text: str, metrics: Dict[str, Any], text_type: str, lang_code: str):
13
- """
14
- Genera recomendaciones personalizadas utilizando la API de Claude.
15
-
16
- Args:
17
- text: El texto original que se analiz贸
18
- metrics: M茅tricas calculadas por el sistema
19
- text_type: Tipo de texto (academic_article, student_essay, general_communication)
20
- lang_code: C贸digo del idioma
21
-
22
- Returns:
23
- str: HTML formateado con recomendaciones personalizadas
24
- """
25
- # Diccionario para traducci贸n de tipos de texto
26
- text_type_names = {
27
  'academic_article': 'art铆culo acad茅mico',
28
- 'student_essay': 'trabajo universitario',
29
  'general_communication': 'comunicaci贸n general'
 
 
 
 
 
 
 
 
 
 
30
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- # Identificar las 谩reas m谩s d茅biles (ordenadas de peor a mejor)
33
- areas = ['vocabulary', 'structure', 'cohesion', 'clarity']
34
- scores = {area: metrics[area]['normalized_score'] for area in areas}
35
- weak_areas = sorted(areas, key=lambda x: scores[x])
36
 
 
 
 
 
 
 
37
  try:
38
  api_key = os.environ.get("ANTHROPIC_API_KEY")
39
  if not api_key:
40
- logger.warning("No se encontr贸 ANTHROPIC_API_KEY en las variables de entorno")
41
- return fallback_recommendations(weak_areas)
42
-
43
- client = anthropic.Anthropic(api_key=api_key)
44
 
45
- # Truncar el texto si es muy largo para evitar exceder l铆mites de tokens
46
- truncated_text = text[:1000] + "..." if len(text) > 1000 else text
 
 
 
47
 
48
- # Construir el prompt para Claude
49
- prompt = f"""
50
- Act煤a como un asistente experto en escritura acad茅mica y comunicaci贸n.
51
 
52
- Analiza el siguiente texto (clasificado como {text_type_names.get(text_type, text_type)}) y genera recomendaciones personalizadas para mejorarlo.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- Las m茅tricas de an谩lisis del texto son:
55
- - Vocabulario: {scores['vocabulary']:.2f}/1.00
56
- - Estructura: {scores['structure']:.2f}/1.00
57
- - Cohesi贸n: {scores['cohesion']:.2f}/1.00
58
- - Claridad: {scores['clarity']:.2f}/1.00
59
 
60
- 脕reas que necesitan mayor atenci贸n (de mayor a menor prioridad):
61
- {', '.join([area for area in weak_areas[:2]])}
 
 
 
 
 
 
 
 
 
 
62
 
63
- Texto analizado:
64
- "{truncated_text}"
65
 
66
- Por favor, proporciona:
67
- 1. Un breve resumen del an谩lisis (2-3 oraciones)
68
- 2. 3-4 recomendaciones espec铆ficas para mejorar el texto, identificando problemas concretos que has detectado
69
- 3. Un ejemplo concreto de c贸mo mejorar una frase del texto (si es posible)
70
 
71
- Utiliza un tono profesional pero amigable, y estructura tus recomendaciones en HTML simple usando etiquetas <h4>, <p>, <ul>, <li>, y <strong>.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  """
 
 
 
 
73
 
74
- # Llamada a la API de Claude
75
- try:
76
- message = client.messages.create(
77
- model="claude-3-5-sonnet-20241022",
78
- max_tokens=1000,
79
- temperature=0.3,
80
- system="Eres un asistente experto en an谩lisis textual que proporciona recomendaciones claras y 煤tiles para mejorar textos.",
81
- messages=[
82
- {"role": "user", "content": prompt}
83
- ]
84
- )
85
-
86
- # Obtener la respuesta
87
- response = message.content[0].text
88
-
89
- # Guardar en cach茅 para uso futuro
90
- cache_key = f"claude_recommendations_{text_type}_{weak_areas[0]}"
91
- st.session_state[cache_key] = response
92
-
93
- return response
94
-
95
- except Exception as api_error:
96
- logger.error(f"Error en la llamada a la API de Claude: {str(api_error)}")
97
- return fallback_recommendations(weak_areas)
98
 
99
- except Exception as e:
100
- logger.error(f"Error generando recomendaciones con Claude: {str(e)}")
101
- return fallback_recommendations(weak_areas)
102
 
103
- def fallback_recommendations(weak_areas):
104
- """Recomendaciones de respaldo en caso de fallo con la API"""
105
- area_names = {
106
- 'vocabulary': 'vocabulario',
107
- 'structure': 'estructura',
108
- 'cohesion': 'cohesi贸n',
109
- 'clarity': 'claridad'
110
- }
111
-
112
- return f"""
113
- <h4>Recomendaciones para mejorar tu texto</h4>
114
- <p>Hemos detectado que las 谩reas de <strong>{area_names.get(weak_areas[0], weak_areas[0])}</strong> y <strong>{area_names.get(weak_areas[1], weak_areas[1])}</strong>
115
- son las que m谩s podr铆an beneficiarse de mejoras.</p>
116
- <p>Para obtener recomendaciones m谩s detalladas, utiliza el asistente virtual de Claude AI
117
- ubicado en la esquina superior izquierda (presiona la flecha junto al logo).</p>
118
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  def display_personalized_recommendations(text, metrics, text_type, lang_code, t):
121
  """
122
- Muestra recomendaciones personalizadas
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # modules/studentact/claude_recommendations.py
 
 
2
  import os
3
+ import anthropic
4
  import streamlit as st
5
  import logging
6
+ import time
7
  import json
8
+ from datetime import datetime, timezone
9
+
10
+ # Local imports
11
+ from ..utils.widget_utils import generate_unique_key
12
+ from ..database.current_situation_mongo_db import store_current_situation_result
13
 
14
  logger = logging.getLogger(__name__)
15
 
16
+ # Define text types
17
+ TEXT_TYPES = {
18
+ 'es': {
 
 
 
 
 
 
 
 
 
 
 
 
19
  'academic_article': 'art铆culo acad茅mico',
20
+ 'university_work': 'trabajo universitario',
21
  'general_communication': 'comunicaci贸n general'
22
+ },
23
+ 'en': {
24
+ 'academic_article': 'academic article',
25
+ 'university_work': 'university work',
26
+ 'general_communication': 'general communication'
27
+ },
28
+ 'fr': {
29
+ 'academic_article': 'article acad茅mique',
30
+ 'university_work': 'travail universitaire',
31
+ 'general_communication': 'communication g茅n茅rale'
32
  }
33
+ }
34
+
35
+ # Cache for recommendations to avoid redundant API calls
36
+ recommendation_cache = {}
37
+
38
+ def get_recommendation_cache_key(text, metrics, text_type, lang_code):
39
+ """
40
+ Generate a cache key for recommendations.
41
+ """
42
+ # Create a simple hash based on text content and metrics
43
+ text_hash = hash(text[:1000]) # Only use first 1000 chars for hashing
44
+ metrics_hash = hash(json.dumps(metrics, sort_keys=True))
45
+ return f"{text_hash}_{metrics_hash}_{text_type}_{lang_code}"
46
+
47
+ def format_metrics_for_claude(metrics, lang_code, text_type):
48
+ """
49
+ Format metrics in a way that's readable for Claude
50
+ """
51
+ formatted_metrics = {}
52
+ for key, value in metrics.items():
53
+ if isinstance(value, (int, float)):
54
+ formatted_metrics[key] = round(value, 2)
55
+ else:
56
+ formatted_metrics[key] = value
57
 
58
+ # Add context about what type of text this is
59
+ text_type_label = TEXT_TYPES.get(lang_code, {}).get(text_type, text_type)
60
+ formatted_metrics['text_type'] = text_type_label
 
61
 
62
+ return formatted_metrics
63
+
64
+ def generate_claude_recommendations(text, metrics, text_type, lang_code):
65
+ """
66
+ Generate personalized recommendations using Claude API.
67
+ """
68
  try:
69
  api_key = os.environ.get("ANTHROPIC_API_KEY")
70
  if not api_key:
71
+ logger.error("Claude API key not found in environment variables")
72
+ return get_fallback_recommendations(lang_code)
 
 
73
 
74
+ # Check cache first
75
+ cache_key = get_recommendation_cache_key(text, metrics, text_type, lang_code)
76
+ if cache_key in recommendation_cache:
77
+ logger.info("Using cached recommendations")
78
+ return recommendation_cache[cache_key]
79
 
80
+ # Format metrics for Claude
81
+ formatted_metrics = format_metrics_for_claude(metrics, lang_code, text_type)
 
82
 
83
+ # Determine language for prompt
84
+ if lang_code == 'es':
85
+ system_prompt = """Eres un asistente especializado en an谩lisis de textos acad茅micos y comunicaci贸n escrita.
86
+ Tu tarea es analizar el texto del usuario y proporcionar recomendaciones personalizadas.
87
+ Usa un tono constructivo y espec铆fico. S茅 claro y directo con tus sugerencias.
88
+ """
89
+ user_prompt = f"""Por favor, analiza este texto de tipo '{formatted_metrics['text_type']}'
90
+ y proporciona recomendaciones personalizadas para mejorarlo.
91
+
92
+ M脡TRICAS DE AN脕LISIS:
93
+ {json.dumps(formatted_metrics, indent=2, ensure_ascii=False)}
94
+
95
+ TEXTO A ANALIZAR:
96
+ {text[:2000]} # Limitamos el texto para evitar exceder tokens
97
+
98
+ Proporciona tu an谩lisis con el siguiente formato:
99
+ 1. Un resumen breve (2-3 frases) del an谩lisis general
100
+ 2. 3-4 recomendaciones espec铆ficas y accionables (cada una de 1-2 frases)
101
+ 3. Un ejemplo concreto de mejora tomado del propio texto del usuario
102
+ 4. Una sugerencia sobre qu茅 herramienta de AIdeaText usar (An谩lisis Morfosint谩ctico, An谩lisis Sem谩ntico o An谩lisis del Discurso)
103
+
104
+ Tu respuesta debe ser concisa y no exceder los 300 palabras."""
105
+ else:
106
+ # Default to English
107
+ system_prompt = """You are an assistant specialized in analyzing academic texts and written communication.
108
+ Your task is to analyze the user's text and provide personalized recommendations.
109
+ Use a constructive and specific tone. Be clear and direct with your suggestions.
110
+ """
111
+ user_prompt = f"""Please analyze this text of type '{formatted_metrics['text_type']}'
112
+ and provide personalized recommendations to improve it.
113
+
114
+ ANALYSIS METRICS:
115
+ {json.dumps(formatted_metrics, indent=2, ensure_ascii=False)}
116
+
117
+ TEXT TO ANALYZE:
118
+ {text[:2000]} # Limiting text to avoid exceeding tokens
119
+
120
+ Provide your analysis with the following format:
121
+ 1. A brief summary (2-3 sentences) of the general analysis
122
+ 2. 3-4 specific and actionable recommendations (each 1-2 sentences)
123
+ 3. A concrete example of improvement taken from the user's own text
124
+ 4. A suggestion about which AIdeaText tool to use (Morphosyntactic Analysis, Semantic Analysis or Discourse Analysis)
125
+
126
+ Your response should be concise and not exceed 300 words."""
127
 
128
+ # Initialize Claude client
129
+ client = anthropic.Anthropic(api_key=api_key)
 
 
 
130
 
131
+ # Call Claude API
132
+ start_time = time.time()
133
+ response = client.messages.create(
134
+ model="claude-3-5-sonnet-20241022",
135
+ max_tokens=1024,
136
+ temperature=0.7,
137
+ system=system_prompt,
138
+ messages=[
139
+ {"role": "user", "content": user_prompt}
140
+ ]
141
+ )
142
+ logger.info(f"Claude API call completed in {time.time() - start_time:.2f} seconds")
143
 
144
+ # Extract recommendations
145
+ recommendations = response.content[0].text
146
 
147
+ # Cache the result
148
+ recommendation_cache[cache_key] = recommendations
 
 
149
 
150
+ return recommendations
151
+ except Exception as e:
152
+ logger.error(f"Error generating recommendations with Claude: {str(e)}")
153
+ return get_fallback_recommendations(lang_code)
154
+
155
+ def get_fallback_recommendations(lang_code):
156
+ """
157
+ Return fallback recommendations if Claude API fails
158
+ """
159
+ if lang_code == 'es':
160
+ return """
161
+ **An谩lisis General**
162
+ Tu texto presenta una estructura b谩sica adecuada, pero hay 谩reas que pueden mejorarse para mayor claridad y cohesi贸n.
163
+
164
+ **Recomendaciones**:
165
+ - Intenta variar tu vocabulario para evitar repeticiones innecesarias
166
+ - Considera revisar la longitud de tus oraciones para mantener un mejor ritmo
167
+ - Aseg煤rate de establecer conexiones claras entre las ideas principales
168
+ - Revisa la consistencia en el uso de tiempos verbales
169
+
170
+ **Herramienta recomendada**:
171
+ Te sugerimos utilizar el An谩lisis Morfosint谩ctico para identificar patrones en tu estructura de oraciones.
172
  """
173
+ else:
174
+ return """
175
+ **General Analysis**
176
+ Your text presents an adequate basic structure, but there are areas that can be improved for better clarity and cohesion.
177
 
178
+ **Recommendations**:
179
+ - Try to vary your vocabulary to avoid unnecessary repetition
180
+ - Consider reviewing the length of your sentences to maintain a better rhythm
181
+ - Make sure to establish clear connections between main ideas
182
+ - Check consistency in the use of verb tenses
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
+ **Recommended tool**:
185
+ We suggest using Morphosyntactic Analysis to identify patterns in your sentence structure.
186
+ """
187
 
188
+ def store_recommendations(username, text, metrics, text_type, recommendations):
189
+ """
190
+ Store the recommendations in the database
 
 
 
 
 
 
 
 
 
 
 
 
191
  """
192
+ try:
193
+ result_data = {
194
+ 'username': username,
195
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
196
+ 'text': text,
197
+ 'metrics': metrics,
198
+ 'text_type': text_type,
199
+ 'recommendations': recommendations,
200
+ 'analysis_type': 'current_situation_claude'
201
+ }
202
+
203
+ result = store_current_situation_result(result_data)
204
+ logger.info(f"Recommendations stored with ID: {result}")
205
+ return True
206
+ except Exception as e:
207
+ logger.error(f"Error storing recommendations: {str(e)}")
208
+ return False
209
 
210
  def display_personalized_recommendations(text, metrics, text_type, lang_code, t):
211
  """
212
+ Display personalized recommendations based on text analysis
213
+ """
214
+ try:
215
+ # Generate recommendations
216
+ recommendations = generate_claude_recommendations(text, metrics, text_type, lang_code)
217
+
218
+ # Format and display recommendations in a nice container
219
+ st.markdown("### 馃摑 " + t.get('recommendations_title', 'Personalized Recommendations'))
220
+
221
+ with st.container():
222
+ st.markdown(f"""
223
+ <div style="padding: 20px; border-radius: 10px;
224
+ background-color: #f8f9fa; margin-bottom: 20px;">
225
+ {recommendations}
226
+ </div>
227
+ """, unsafe_allow_html=True)
228
+
229
+ # Add prompt to use assistant
230
+ st.info("馃挕 **" + t.get('assistant_prompt', 'For further improvement:') + "** " +
231
+ 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.'))
232
+
233
+ # Add save button
234
+ col1, col2, col3 = st.columns([1,1,1])
235
+ with col2:
236
+ if st.button(
237
+ t.get('save_button', 'Save Analysis'),
238
+ key=generate_unique_key("claude_recommendations", "save"),
239
+ type="primary",
240
+ use_container_width=True
241
+ ):
242
+ if 'username' in st.session_state:
243
+ success = store_recommendations(
244
+ st.session_state.username,
245
+ text,
246
+ metrics,
247
+ text_type,
248
+ recommendations
249
+ )
250
+ if success:
251
+ st.success(t.get('save_success', 'Analysis saved successfully'))
252
+ else:
253
+ st.error(t.get('save_error', 'Error saving analysis'))
254
+ else:
255
+ st.error(t.get('login_required', 'Please log in to save analysis'))
256
+
257
+ except Exception as e:
258
+ logger.error(f"Error displaying recommendations: {str(e)}")
259
+ st.error(t.get('recommendations_error', 'Error generating recommendations. Please try again later.'))