| | |
| | import os |
| | import anthropic |
| | import logging |
| | from typing import Generator |
| |
|
| | logger = logging.getLogger(__name__) |
| |
|
| | class ChatProcessor: |
| | def __init__(self): |
| | """Inicializa el procesador de chat con la API de Claude""" |
| | self.client = anthropic.Anthropic( |
| | api_key=os.environ.get("ANTHROPIC_API_KEY") |
| | ) |
| | self.conversation_history = [] |
| | self.semantic_context = None |
| | self.current_lang = 'en' |
| |
|
| | def set_semantic_context(self, text, metrics, graph_data, lang_code='en'): |
| | """Configura el contexto semántico completo para el chat""" |
| | if not text or not metrics: |
| | logger.error("Faltan datos esenciales para el contexto semántico") |
| | raise ValueError("Texto y métricas son requeridos") |
| | |
| | self.semantic_context = { |
| | 'full_text': text, |
| | 'key_concepts': metrics.get('key_concepts', []), |
| | 'concept_centrality': metrics.get('concept_centrality', {}), |
| | 'graph_available': graph_data is not None, |
| | 'language': lang_code |
| | } |
| | self.current_lang = lang_code |
| | self.conversation_history = [] |
| | logger.info("Contexto semántico configurado correctamente") |
| |
|
| | def _get_system_prompt(self): |
| | """Genera el prompt del sistema con todo el contexto necesario""" |
| | if not self.semantic_context: |
| | return "You are a helpful assistant." |
| | |
| | concepts = self.semantic_context['key_concepts'] |
| | top_concepts = ", ".join([f"{c[0]} ({c[1]:.2f})" for c in concepts[:5]]) |
| | |
| | prompts = { |
| | 'en': f"""You are a semantic analysis expert. The user analyzed a research article. |
| | Full text available (abbreviated for context). |
| | Key concepts: {top_concepts} |
| | Graph available: {self.semantic_context['graph_available']} |
| | |
| | Your tasks: |
| | 1. Answer questions about concepts and their relationships |
| | 2. Explain the semantic network structure |
| | 3. Suggest text improvements |
| | 4. Provide insights based on concept centrality |
| | 5. IMPORTANT FORMATTING: Always format citations or numerical references using brackets with a leading space (e.g., text [1], concept [2]).""", |
| | |
| | 'es': f"""Eres un experto en análisis semántico. El usuario analizó un artículo de investigación. |
| | Texto completo disponible (abreviado para contexto). |
| | Conceptos clave: {top_concepts} |
| | Gráfico disponible: {self.semantic_context['graph_available']} |
| | |
| | Tus tareas: |
| | 1. Responder preguntas sobre conceptos y sus relaciones |
| | 2. Explicar la estructura de la red semántica |
| | 3. Sugerir mejoras al texto |
| | 4. Proporcionar insights basados en centralidad de conceptos |
| | 5. FORMATO IMPORTANTE: Formatea siempre las citas o referencias numéricas usando corchetes y asegurando un espacio antes (por ejemplo: texto [1], concepto [2]).""", |
| | |
| | 'pt': f"""Você é um especialista em análise semântica. O usuário analisou um artigo de pesquisa. |
| | Texto completo disponível (abreviado para contexto). |
| | Conceitos-chave: {top_concepts} |
| | Gráfico disponível: {self.semantic_context['graph_available']} |
| | |
| | Suas tarefas: |
| | 1. Responder perguntas sobre conceitos e suas relações |
| | 2. Explicar a estrutura da rede semântica |
| | 3. Sugerir melhorias no texto |
| | 4. Fornecer insights com base na centralidade dos conceitos |
| | 5. FORMATAÇÃO IMPORTANTE: Formate sempre as citações ou referências numéricas usando colchetes e garantindo um espaço antes (por exemplo: texto [1], conceito [2]).""", |
| | |
| | 'fr': f"""Vous êtes un expert en analyse sémantique. L'utilisateur a analysé un article de recherche. |
| | Texte complet disponible (abrégé pour le contexte). |
| | Concepts clés: {top_concepts} |
| | Graphique disponible: {self.semantic_context['graph_available']} |
| | |
| | Vos tâches: |
| | 1. Répondre aux questions sur les concepts et leurs relations |
| | 2. Expliquer la structure du réseau sémantique |
| | 3. Suggérer des améliorations de texte |
| | 4. Fournir des insights basés sur la centralité des concepts |
| | 5. FORMATAGE IMPORTANT: Formatez toujours les citations ou les références numériques en utilisant des crochets et en assurant un espace avant (par exemple: texte [1], concept [2]).""" |
| | } |
| | |
| | return prompts.get(self.current_lang, prompts['en']) |
| |
|
| | def clean_generated_text(self, text): |
| | """Limpia caracteres especiales del texto generado SIN eliminar espacios en blanco de los extremos.""" |
| | |
| | return text.replace("\u2588", "").replace("▌", "") |
| |
|
| | def process_chat_input(self, message: str, lang_code: str) -> Generator[str, None, None]: |
| | """Procesa el mensaje con todo el contexto disponible""" |
| | try: |
| | if not self.semantic_context: |
| | yield "Error: Contexto semántico no configurado. Recargue el análisis." |
| | return |
| | |
| | if lang_code != self.current_lang: |
| | self.current_lang = lang_code |
| | logger.info(f"Idioma cambiado a: {lang_code}") |
| |
|
| | messages = [ |
| | { |
| | "role": "user", |
| | "content": f"Documento analizado (extracto):\n{self.semantic_context['full_text'][:2000]}..." |
| | }, |
| | *self.conversation_history, |
| | {"role": "user", "content": message} |
| | ] |
| |
|
| | with self.client.messages.stream( |
| | model="claude-sonnet-4-5-20250929", |
| | max_tokens=4000, |
| | temperature=0.7, |
| | system=self._get_system_prompt(), |
| | messages=messages |
| | ) as stream: |
| | full_response = "" |
| | for chunk in stream.text_stream: |
| | cleaned_chunk = self.clean_generated_text(chunk) |
| | full_response += cleaned_chunk |
| | yield cleaned_chunk |
| | |
| | |
| | self.conversation_history.extend([ |
| | {"role": "user", "content": message}, |
| | {"role": "assistant", "content": full_response.strip()} |
| | ]) |
| | logger.info("Respuesta generada y guardada en historial") |
| |
|
| | except Exception as e: |
| | logger.error(f"Error en process_chat_input: {str(e)}", exc_info=True) |
| | error_messages = { |
| | 'en': "Error processing message. Please reload the analysis.", |
| | 'es': "Error al procesar mensaje. Recargue el análisis.", |
| | 'pt': "Erro ao processar mensagem. Recarregue a análise.", |
| | 'fr': "Erreur lors du traitement du message. Veuillez recharger l'analyse." |
| | } |
| | yield error_messages.get(self.current_lang, "Processing error") |