| |
| 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' |
| }, |
| 'uk': { |
| 'academic_article': 'академічна стаття', |
| 'university_work': 'університетська робота', |
| 'general_communication': 'загальна комунікація' |
| } |
| } |
|
|
| |
| 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.""" |
| |
| elif lang_code == 'uk': |
| system_prompt = """Ви - асистент, який спеціалізується на аналізі академічних текстів та письмовій комунікації. |
| Ваше завдання - проаналізувати текст користувача та надати персоналізовані рекомендації. |
| Використовуйте конструктивний та конкретний тон. Будьте ясними та прямими у ваших пропозиціях. |
| """ |
| user_prompt = f"""Будь ласка, проаналізуйте цей текст типу '{formatted_metrics['text_type']}' |
| та надайте персоналізовані рекомендації для його покращення. |
| |
| МЕТРИКИ АНАЛІЗУ: |
| {json.dumps(formatted_metrics, indent=2, ensure_ascii=False)} |
| |
| ТЕКСТ ДЛЯ АНАЛІЗУ: |
| {text[:2000]} |
| |
| Надайте свій аналіз у такому форматі: |
| 1. Короткий підсумок (2-3 речення) загального аналізу |
| 2. 3-4 конкретні та дієві рекомендації (кожна по 1-2 речення) |
| 3. Конкретний приклад покращення, взятий з власного тексту користувача |
| 4. Пропозиція щодо використання інструмента AIdeaText (Морфосинтаксичний аналіз, Семантичний аналіз або Аналіз дискурсу) |
| |
| Ваша відповідь має бути стислою та не перевищувати 300 слів.""" |
| |
| 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. |
| """ |
| |
| elif lang_code == 'uk': |
| return """ |
| **Загальний аналіз** |
| Ваш текст має адекватну базову структуру, але є області, які можна покращити для кращої ясності та зв'язності. |
| |
| **Рекомендації**: |
| - Спробуйте урізноманітнити словниковий запас, щоб уникнути непотрібних повторень |
| - Розгляньте перегляд довжини ваших речень для підтримки кращого ритму |
| - Переконайтеся, що встановлюєте чіткі зв'язки між основними ідеями |
| - Перевірте послідовність у використанні дієслівних часів |
| |
| **Рекомендований інструмент**: |
| Ми пропонуємо використовувати Морфосинтаксичний аналіз для виявлення закономірностей у структурі ваших речень. |
| """ |
| |
| 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.')) |