AIdeaText commited on
Commit
1d6e4b4
·
verified ·
1 Parent(s): 8ef3761

Update modules/studentact/claude_recommendations.py

Browse files
modules/studentact/claude_recommendations.py CHANGED
@@ -1,266 +1,303 @@
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
-
189
- #######################################
190
-
191
- def store_recommendations(username, text, metrics, text_type, recommendations):
192
- """
193
- Store the recommendations in the database
194
- """
195
- try:
196
- # Importar la función de almacenamiento de recomendaciones
197
- from ..database.claude_recommendations_mongo_db import store_claude_recommendation
198
-
199
- # Guardar usando la nueva función especializada
200
- result = store_claude_recommendation(
201
- username=username,
202
- text=text,
203
- metrics=metrics,
204
- text_type=text_type,
205
- recommendations=recommendations
206
- )
207
-
208
- logger.info(f"Recommendations stored successfully: {result}")
209
- return result
210
- except Exception as e:
211
- logger.error(f"Error storing recommendations: {str(e)}")
212
- return False
213
-
214
-
215
- ##########################################
216
- ##########################################
217
- def display_personalized_recommendations(text, metrics, text_type, lang_code, t):
218
- """
219
- Display personalized recommendations based on text analysis
220
- """
221
- try:
222
- # Generate recommendations
223
- recommendations = generate_claude_recommendations(text, metrics, text_type, lang_code)
224
-
225
- # Format and display recommendations in a nice container
226
- st.markdown("### 📝 " + t.get('recommendations_title', 'Personalized Recommendations'))
227
-
228
- with st.container():
229
- st.markdown(f"""
230
- <div style="padding: 20px; border-radius: 10px;
231
- background-color: #f8f9fa; margin-bottom: 20px;">
232
- {recommendations}
233
- </div>
234
- """, unsafe_allow_html=True)
235
-
236
- # Add prompt to use assistant
237
- st.info("💡 **" + t.get('assistant_prompt', 'For further improvement:') + "** " +
238
- 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.'))
239
-
240
- # Add save button
241
- col1, col2, col3 = st.columns([1,1,1])
242
- with col2:
243
- if st.button(
244
- t.get('save_button', 'Save Analysis'),
245
- key=generate_unique_key("claude_recommendations", "save"),
246
- type="primary",
247
- use_container_width=True
248
- ):
249
- if 'username' in st.session_state:
250
- success = store_recommendations(
251
- st.session_state.username,
252
- text,
253
- metrics,
254
- text_type,
255
- recommendations
256
- )
257
- if success:
258
- st.success(t.get('save_success', 'Analysis saved successfully'))
259
- else:
260
- st.error(t.get('save_error', 'Error saving analysis'))
261
- else:
262
- st.error(t.get('login_required', 'Please log in to save analysis'))
263
-
264
- except Exception as e:
265
- logger.error(f"Error displaying recommendations: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  st.error(t.get('recommendations_error', 'Error generating recommendations. Please try again later.'))
 
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
+ 'uk': {
29
+ 'academic_article': 'академічна стаття',
30
+ 'university_work': 'університетська робота',
31
+ 'general_communication': 'загальна комунікація'
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
+ elif lang_code == 'uk':
129
+ system_prompt = """Ви - асистент, який спеціалізується на аналізі академічних текстів та письмовій комунікації.
130
+ Ваше завдання - проаналізувати текст користувача та надати персоналізовані рекомендації.
131
+ Використовуйте конструктивний та конкретний тон. Будьте ясними та прямими у ваших пропозиціях.
132
+ """
133
+ user_prompt = f"""Будь ласка, проаналізуйте цей текст типу '{formatted_metrics['text_type']}'
134
+ та надайте персоналізовані рекомендації для його покращення.
135
+
136
+ МЕТРИКИ АНАЛІЗУ:
137
+ {json.dumps(formatted_metrics, indent=2, ensure_ascii=False)}
138
+
139
+ ТЕКСТ ДЛЯ АНАЛІЗУ:
140
+ {text[:2000]}
141
+
142
+ Надайте свій аналіз у такому форматі:
143
+ 1. Короткий підсумок (2-3 речення) загального аналізу
144
+ 2. 3-4 конкретні та дієві рекомендації (кожна по 1-2 речення)
145
+ 3. Конкретний приклад покращення, взятий з власного тексту користувача
146
+ 4. Пропозиція щодо використання інструмента AIdeaText (Морфосинтаксичний аналіз, Семантичний аналіз або Аналіз дискурсу)
147
+
148
+ Ваша відповідь має бути стислою та не перевищувати 300 слів."""
149
+
150
+ # Initialize Claude client
151
+ client = anthropic.Anthropic(api_key=api_key)
152
+
153
+ # Call Claude API
154
+ start_time = time.time()
155
+ response = client.messages.create(
156
+ model="claude-3-5-sonnet-20241022",
157
+ max_tokens=1024,
158
+ temperature=0.7,
159
+ system=system_prompt,
160
+ messages=[
161
+ {"role": "user", "content": user_prompt}
162
+ ]
163
+ )
164
+ logger.info(f"Claude API call completed in {time.time() - start_time:.2f} seconds")
165
+
166
+ # Extract recommendations
167
+ recommendations = response.content[0].text
168
+
169
+ # Cache the result
170
+ recommendation_cache[cache_key] = recommendations
171
+
172
+ return recommendations
173
+ except Exception as e:
174
+ logger.error(f"Error generating recommendations with Claude: {str(e)}")
175
+ return get_fallback_recommendations(lang_code)
176
+
177
+ ##################################################################################
178
+ ##################################################################################
179
+ def get_fallback_recommendations(lang_code):
180
+ """
181
+ Return fallback recommendations if Claude API fails
182
+ """
183
+ if lang_code == 'es':
184
+ return """
185
+ **Análisis General**
186
+ Tu texto presenta una estructura básica adecuada, pero hay áreas que pueden mejorarse para mayor claridad y cohesión.
187
+
188
+ **Recomendaciones**:
189
+ - Intenta variar tu vocabulario para evitar repeticiones innecesarias
190
+ - Considera revisar la longitud de tus oraciones para mantener un mejor ritmo
191
+ - Asegúrate de establecer conexiones claras entre las ideas principales
192
+ - Revisa la consistencia en el uso de tiempos verbales
193
+
194
+ **Herramienta recomendada**:
195
+ Te sugerimos utilizar el Análisis Morfosintáctico para identificar patrones en tu estructura de oraciones.
196
+ """
197
+ else:
198
+ return """
199
+ **General Analysis**
200
+ Your text presents an adequate basic structure, but there are areas that can be improved for better clarity and cohesion.
201
+
202
+ **Recommendations**:
203
+ - Try to vary your vocabulary to avoid unnecessary repetition
204
+ - Consider reviewing the length of your sentences to maintain a better rhythm
205
+ - Make sure to establish clear connections between main ideas
206
+ - Check consistency in the use of verb tenses
207
+
208
+ **Recommended tool**:
209
+ We suggest using Morphosyntactic Analysis to identify patterns in your sentence structure.
210
+ """
211
+
212
+ elif lang_code == 'uk':
213
+ return """
214
+ **Загальний аналіз**
215
+ Ваш текст має адекватну базову структуру, але є області, які можна покращити для кращої ясності та зв'язності.
216
+
217
+ **Рекомендації**:
218
+ - Спробуйте урізноманітнити словниковий запас, щоб уникнути непотрібних повторень
219
+ - Розгляньте перегляд довжини ваших речень для підтримки кращого ритму
220
+ - Переконайтеся, що встановлюєте чіткі зв'язки між основними ідеями
221
+ - Перевірте послідовність у використанні дієслівних часів
222
+
223
+ **Рекомендований інструмент**:
224
+ Ми пропонуємо використовувати Морфосинтаксичний аналіз для виявлення закономірностей у структурі ваших речень.
225
+ """
226
+ #######################################
227
+ #######################################
228
+ def store_recommendations(username, text, metrics, text_type, recommendations):
229
+ """
230
+ Store the recommendations in the database
231
+ """
232
+ try:
233
+ # Importar la función de almacenamiento de recomendaciones
234
+ from ..database.claude_recommendations_mongo_db import store_claude_recommendation
235
+
236
+ # Guardar usando la nueva función especializada
237
+ result = store_claude_recommendation(
238
+ username=username,
239
+ text=text,
240
+ metrics=metrics,
241
+ text_type=text_type,
242
+ recommendations=recommendations
243
+ )
244
+
245
+ logger.info(f"Recommendations stored successfully: {result}")
246
+ return result
247
+ except Exception as e:
248
+ logger.error(f"Error storing recommendations: {str(e)}")
249
+ return False
250
+
251
+
252
+ ##########################################
253
+ ##########################################
254
+ def display_personalized_recommendations(text, metrics, text_type, lang_code, t):
255
+ """
256
+ Display personalized recommendations based on text analysis
257
+ """
258
+ try:
259
+ # Generate recommendations
260
+ recommendations = generate_claude_recommendations(text, metrics, text_type, lang_code)
261
+
262
+ # Format and display recommendations in a nice container
263
+ st.markdown("### 📝 " + t.get('recommendations_title', 'Personalized Recommendations'))
264
+
265
+ with st.container():
266
+ st.markdown(f"""
267
+ <div style="padding: 20px; border-radius: 10px;
268
+ background-color: #f8f9fa; margin-bottom: 20px;">
269
+ {recommendations}
270
+ </div>
271
+ """, unsafe_allow_html=True)
272
+
273
+ # Add prompt to use assistant
274
+ st.info("💡 **" + t.get('assistant_prompt', 'For further improvement:') + "** " +
275
+ 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.'))
276
+
277
+ # Add save button
278
+ col1, col2, col3 = st.columns([1,1,1])
279
+ with col2:
280
+ if st.button(
281
+ t.get('save_button', 'Save Analysis'),
282
+ key=generate_unique_key("claude_recommendations", "save"),
283
+ type="primary",
284
+ use_container_width=True
285
+ ):
286
+ if 'username' in st.session_state:
287
+ success = store_recommendations(
288
+ st.session_state.username,
289
+ text,
290
+ metrics,
291
+ text_type,
292
+ recommendations
293
+ )
294
+ if success:
295
+ st.success(t.get('save_success', 'Analysis saved successfully'))
296
+ else:
297
+ st.error(t.get('save_error', 'Error saving analysis'))
298
+ else:
299
+ st.error(t.get('login_required', 'Please log in to save analysis'))
300
+
301
+ except Exception as e:
302
+ logger.error(f"Error displaying recommendations: {str(e)}")
303
  st.error(t.get('recommendations_error', 'Error generating recommendations. Please try again later.'))