AIdeaText commited on
Commit
9b6cc0b
·
verified ·
1 Parent(s): bb6efdb

Update modules/chatbot/chat_process.py

Browse files
Files changed (1) hide show
  1. modules/chatbot/chat_process.py +153 -72
modules/chatbot/chat_process.py CHANGED
@@ -1,16 +1,29 @@
1
  # modules/chatbot/chat_process.py
2
  import os
3
- import anthropic
 
4
  import logging
 
5
  from typing import Generator
 
6
 
7
  logger = logging.getLogger(__name__)
8
 
9
  class ChatProcessor:
10
  def __init__(self):
11
- """Inicializa el procesador de chat con la API de Claude"""
12
- self.client = anthropic.Anthropic(
13
- api_key=os.environ.get("ANTHROPIC_API_KEY")
 
 
 
 
 
 
 
 
 
 
14
  )
15
  self.conversation_history = []
16
  self.semantic_context = None
@@ -23,10 +36,11 @@ class ChatProcessor:
23
  raise ValueError("Texto y métricas son requeridos")
24
 
25
  self.semantic_context = {
26
- 'full_text': text, # Texto completo del documento
27
  'key_concepts': metrics.get('key_concepts', []),
28
  'concept_centrality': metrics.get('concept_centrality', {}),
29
  'graph_available': graph_data is not None,
 
30
  'language': lang_code
31
  }
32
  self.current_lang = lang_code
@@ -43,48 +57,48 @@ class ChatProcessor:
43
 
44
  prompts = {
45
  'en': f"""You are a semantic analysis expert. The user analyzed a research article.
46
- Full text available (abbreviated for context).
47
- Key concepts: {top_concepts}
48
- Graph available: {self.semantic_context['graph_available']}
49
-
50
- Your tasks:
51
- 1. Answer questions about concepts and their relationships
52
- 2. Explain the semantic network structure
53
- 3. Suggest text improvements
54
- 4. Provide insights based on concept centrality""",
55
 
56
  'es': f"""Eres un experto en análisis semántico. El usuario analizó un artículo de investigación.
57
- Texto completo disponible (abreviado para contexto).
58
- Conceptos clave: {top_concepts}
59
- Gráfico disponible: {self.semantic_context['graph_available']}
60
-
61
- Tus tareas:
62
- 1. Responder preguntas sobre conceptos y sus relaciones
63
- 2. Explicar la estructura de la red semántica
64
- 3. Sugerir mejorias al texto
65
- 4. Proporcionar insights basados en centralidad de conceptos""",
66
 
67
  'pt': f"""Você é um especialista em análise semântica. O usuário analisou um artigo de pesquisa.
68
- Texto completo disponível (abreviado para contexto).
69
- Conceitos-chave: {top_concepts}
70
- Gráfico disponível: {self.semantic_context['graph_available']}
71
-
72
- Suas tarefas:
73
- 1. Responder perguntas sobre conceitos e suas relações
74
- 2. Explicar a estrutura da rede semântica
75
- 3. Sugerir melhorias no texto
76
- 4. Fornecer insights com base na centralidade dos conceitos""",
77
 
78
  'fr': f"""Vous êtes un expert en analyse sémantique. L'utilisateur a analysé un article de recherche.
79
- Texte complet disponible (abrégé pour le contexte).
80
- Concepts clés: {top_concepts}
81
- Graphique disponible: {self.semantic_context['graph_available']}
82
-
83
- Vos tâches:
84
- 1. Répondre aux questions sur les concepts et leurs relations
85
- 2. Expliquer la structure du réseau sémantique
86
- 3. Suggérer des améliorations de texte
87
- 4. Fournir des insights basés sur la centralité des concepts"""
88
  }
89
 
90
  return prompts.get(self.current_lang, prompts['en'])
@@ -93,8 +107,38 @@ class ChatProcessor:
93
  """Limpia caracteres especiales del texto generado"""
94
  return text.replace("\u2588", "").replace("▌", "").strip()
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  def process_chat_input(self, message: str, lang_code: str) -> Generator[str, None, None]:
97
- """Procesa el mensaje con todo el contexto disponible"""
98
  try:
99
  if not self.semantic_context:
100
  yield "Error: Contexto semántico no configurado. Recargue el análisis."
@@ -105,36 +149,73 @@ class ChatProcessor:
105
  self.current_lang = lang_code
106
  logger.info(f"Idioma cambiado a: {lang_code}")
107
 
108
- # Construir historial de mensajes
109
- messages = [
110
- {
111
- "role": "user",
112
- "content": f"Documento analizado (extracto):\n{self.semantic_context['full_text'][:2000]}..."
113
- },
114
- *self.conversation_history,
115
- {"role": "user", "content": message}
116
- ]
117
-
118
- # Llamar a Claude con streaming
119
- with self.client.messages.stream(
120
- model="claude-sonnet-4-5-20250929",
121
- max_tokens=4000,
122
- temperature=0.7,
123
- system=self._get_system_prompt(),
124
- messages=messages
125
- ) as stream:
126
- full_response = ""
127
- for chunk in stream.text_stream:
128
- cleaned_chunk = self.clean_generated_text(chunk)
129
- full_response += cleaned_chunk
130
- yield cleaned_chunk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
- # Guardar respuesta en historial
133
- self.conversation_history.extend([
134
- {"role": "user", "content": message},
135
- {"role": "assistant", "content": full_response}
136
- ])
137
- logger.info("Respuesta generada y guardada en historial")
138
 
139
  except Exception as e:
140
  logger.error(f"Error en process_chat_input: {str(e)}", exc_info=True)
@@ -144,4 +225,4 @@ class ChatProcessor:
144
  'pt': "Erro ao processar mensagem. Recarregue a análise.",
145
  'fr': "Erreur lors du traitement du message. Veuillez recharger l'analyse."
146
  }
147
- yield error_messages.get(self.current_lang, "Processing error")
 
1
  # modules/chatbot/chat_process.py
2
  import os
3
+ import json
4
+ import boto3
5
  import logging
6
+ import base64
7
  from typing import Generator
8
+ from botocore.config import Config
9
 
10
  logger = logging.getLogger(__name__)
11
 
12
  class ChatProcessor:
13
  def __init__(self):
14
+ """Inicializa el procesador de chat con AWS Bedrock (Jamba 1.5 Large)"""
15
+ # Configurar cliente de Bedrock
16
+ self.bedrock = boto3.client(
17
+ 'bedrock-runtime',
18
+ region_name=os.environ.get("AWS_REGION", "us-east-1"),
19
+ aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
20
+ aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
21
+ config=Config(
22
+ retries={
23
+ 'max_attempts': 3,
24
+ 'mode': 'adaptive'
25
+ }
26
+ )
27
  )
28
  self.conversation_history = []
29
  self.semantic_context = None
 
36
  raise ValueError("Texto y métricas son requeridos")
37
 
38
  self.semantic_context = {
39
+ 'full_text': text,
40
  'key_concepts': metrics.get('key_concepts', []),
41
  'concept_centrality': metrics.get('concept_centrality', {}),
42
  'graph_available': graph_data is not None,
43
+ 'graph_data': graph_data, # Guardamos el grafo para usarlo en el chat
44
  'language': lang_code
45
  }
46
  self.current_lang = lang_code
 
57
 
58
  prompts = {
59
  'en': f"""You are a semantic analysis expert. The user analyzed a research article.
60
+ Full text available (abbreviated for context).
61
+ Key concepts: {top_concepts}
62
+ Graph available: {self.semantic_context['graph_available']}
63
+
64
+ Your tasks:
65
+ 1. Answer questions about concepts and their relationships
66
+ 2. Explain the semantic network structure
67
+ 3. Suggest text improvements
68
+ 4. Provide insights based on concept centrality""",
69
 
70
  'es': f"""Eres un experto en análisis semántico. El usuario analizó un artículo de investigación.
71
+ Texto completo disponible (abreviado para contexto).
72
+ Conceptos clave: {top_concepts}
73
+ Gráfico disponible: {self.semantic_context['graph_available']}
74
+
75
+ Tus tareas:
76
+ 1. Responder preguntas sobre conceptos y sus relaciones
77
+ 2. Explicar la estructura de la red semántica
78
+ 3. Sugerir mejoras al texto
79
+ 4. Proporcionar insights basados en centralidad de conceptos""",
80
 
81
  'pt': f"""Você é um especialista em análise semântica. O usuário analisou um artigo de pesquisa.
82
+ Texto completo disponível (abreviado para contexto).
83
+ Conceitos-chave: {top_concepts}
84
+ Gráfico disponível: {self.semantic_context['graph_available']}
85
+
86
+ Suas tarefas:
87
+ 1. Responder perguntas sobre conceitos e suas relações
88
+ 2. Explicar a estrutura da rede semântica
89
+ 3. Sugerir melhorias no texto
90
+ 4. Fornecer insights com base na centralidade dos conceitos""",
91
 
92
  'fr': f"""Vous êtes un expert en analyse sémantique. L'utilisateur a analysé un article de recherche.
93
+ Texte complet disponible (abrégé pour le contexte).
94
+ Concepts clés: {top_concepts}
95
+ Graphique disponible: {self.semantic_context['graph_available']}
96
+
97
+ Vos tâches:
98
+ 1. Répondre aux questions sur les concepts et leurs relations
99
+ 2. Expliquer la structure du réseau sémantique
100
+ 3. Suggérer des améliorations de texte
101
+ 4. Fournir des insights basés sur la centralité des concepts"""
102
  }
103
 
104
  return prompts.get(self.current_lang, prompts['en'])
 
107
  """Limpia caracteres especiales del texto generado"""
108
  return text.replace("\u2588", "").replace("▌", "").strip()
109
 
110
+ def _build_multimodal_content(self, message):
111
+ """Construye el contenido multimodal con texto + grafo si está disponible"""
112
+ content_parts = []
113
+
114
+ # 1. Añadir el texto del documento
115
+ if self.semantic_context and 'full_text' in self.semantic_context:
116
+ content_parts.append(
117
+ f"Documento analizado (extracto):\n{self.semantic_context['full_text'][:1500]}..."
118
+ )
119
+
120
+ # 2. Añadir conceptos clave
121
+ if self.semantic_context and 'key_concepts' in self.semantic_context:
122
+ concepts = self.semantic_context['key_concepts'][:5]
123
+ content_parts.append(f"Conceptos clave: {concepts}")
124
+
125
+ # 3. Añadir el grafo si está disponible (en base64)
126
+ if self.semantic_context and self.semantic_context.get('graph_available'):
127
+ graph_data = self.semantic_context.get('graph_data')
128
+ if graph_data:
129
+ # Si el grafo ya es base64, lo usamos directamente
130
+ if isinstance(graph_data, str) and graph_data.startswith('iVBOR'):
131
+ content_parts.append(f"![Grafo](data:image/png;base64,{graph_data})")
132
+ else:
133
+ content_parts.append("Grafo disponible para consultas visuales.")
134
+
135
+ # 4. Añadir el mensaje actual del usuario
136
+ content_parts.append(f"Pregunta del usuario: {message}")
137
+
138
+ return "\n\n".join(content_parts)
139
+
140
  def process_chat_input(self, message: str, lang_code: str) -> Generator[str, None, None]:
141
+ """Procesa el mensaje con todo el contexto disponible usando Jamba 1.5 en Bedrock"""
142
  try:
143
  if not self.semantic_context:
144
  yield "Error: Contexto semántico no configurado. Recargue el análisis."
 
149
  self.current_lang = lang_code
150
  logger.info(f"Idioma cambiado a: {lang_code}")
151
 
152
+ # Construir el contenido multimodal
153
+ user_content = self._build_multimodal_content(message)
154
+
155
+ # Construir mensajes para Jamba (formato específico)
156
+ messages = []
157
+
158
+ # Añadir system prompt
159
+ messages.append({
160
+ "role": "system",
161
+ "content": self._get_system_prompt()
162
+ })
163
+
164
+ # Añadir historial de conversación (últimos 4 intercambios para no exceder contexto)
165
+ for msg in self.conversation_history[-8:]: # 8 mensajes = 4 intercambios
166
+ messages.append(msg)
167
+
168
+ # Añadir mensaje actual del usuario
169
+ messages.append({
170
+ "role": "user",
171
+ "content": user_content
172
+ })
173
+
174
+ # Preparar el cuerpo de la petición para Jamba 1.5 Large
175
+ request_body = {
176
+ "messages": messages,
177
+ "max_tokens": 2000,
178
+ "temperature": 0.7,
179
+ "top_p": 0.9,
180
+ "stop": [],
181
+ "n": 1
182
+ }
183
+
184
+ # Llamar a Bedrock (sin streaming por ahora, Jamba no soporta streaming nativo)
185
+ response = self.bedrock.invoke_model(
186
+ modelId='ai21.jamba-1-5-large-v1:0',
187
+ contentType='application/json',
188
+ accept='application/json',
189
+ body=json.dumps(request_body)
190
+ )
191
+
192
+ # Procesar la respuesta
193
+ response_body = json.loads(response['body'].read())
194
+
195
+ # Extraer el texto de la respuesta (formato específico de Jamba)
196
+ if 'choices' in response_body and len(response_body['choices']) > 0:
197
+ full_response = response_body['choices'][0]['message']['content']
198
+ else:
199
+ full_response = "Lo siento, no pude generar una respuesta."
200
+
201
+ # Limpiar la respuesta
202
+ clean_response = self.clean_generated_text(full_response)
203
+
204
+ # Simular streaming para mantener compatibilidad con la interfaz
205
+ # Dividimos la respuesta en fragmentos para simular streaming
206
+ chunk_size = 50
207
+ for i in range(0, len(clean_response), chunk_size):
208
+ yield clean_response[i:i+chunk_size]
209
+
210
+ # Guardar respuesta en historial
211
+ self.conversation_history.append({"role": "user", "content": message})
212
+ self.conversation_history.append({"role": "assistant", "content": clean_response})
213
+
214
+ # Mantener historial manejable (últimos 20 mensajes)
215
+ if len(self.conversation_history) > 40:
216
+ self.conversation_history = self.conversation_history[-40:]
217
 
218
+ logger.info("Respuesta generada y guardada en historial")
 
 
 
 
 
219
 
220
  except Exception as e:
221
  logger.error(f"Error en process_chat_input: {str(e)}", exc_info=True)
 
225
  'pt': "Erro ao processar mensagem. Recarregue a análise.",
226
  'fr': "Erreur lors du traitement du message. Veuillez recharger l'analyse."
227
  }
228
+ yield error_messages.get(self.current_lang, "Processing error")