sonika-langchain-bot 0.0.16__py3-none-any.whl → 0.0.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sonika-langchain-bot might be problematic. Click here for more details.

@@ -40,7 +40,6 @@ class LangChainBot:
40
40
  - Thread-based conversation persistence
41
41
  - Streaming responses
42
42
  - Backward compatibility with legacy APIs
43
- - Debug logging injection for production troubleshooting
44
43
  """
45
44
 
46
45
  def __init__(self,
@@ -61,7 +60,7 @@ class LangChainBot:
61
60
  tools (List[BaseTool], optional): Traditional LangChain tools to bind to the model
62
61
  mcp_servers (Dict[str, Any], optional): MCP server configurations for dynamic tool loading
63
62
  use_checkpointer (bool): Enable automatic conversation persistence using LangGraph checkpoints
64
- logger (logging.Logger, optional): Logger instance for debugging. If None, uses silent NullHandler
63
+ logger (Optional[logging.Logger]): Logger instance for error tracking (silent by default if not provided)
65
64
 
66
65
  Note:
67
66
  The instructions will be automatically enhanced with tool descriptions
@@ -72,17 +71,11 @@ class LangChainBot:
72
71
  if logger is None:
73
72
  self.logger.addHandler(logging.NullHandler())
74
73
 
75
- self.logger.info("="*80)
76
- self.logger.info("🚀 Inicializando LangChainBot")
77
- self.logger.info("="*80)
78
-
79
74
  # Core components
80
75
  self.language_model = language_model
81
76
  self.embeddings = embeddings
82
77
  self.base_instructions = instructions
83
78
 
84
- self.logger.debug(f"📋 Instrucciones base: {len(instructions)} caracteres")
85
-
86
79
  # Backward compatibility attributes
87
80
  self.chat_history: List[BaseMessage] = []
88
81
  self.vector_store = None
@@ -91,39 +84,25 @@ class LangChainBot:
91
84
  self.tools = tools or []
92
85
  self.mcp_client = None
93
86
 
94
- self.logger.info(f"🔧 Herramientas iniciales: {len(self.tools)}")
95
-
96
87
  # Initialize MCP servers if provided
97
88
  if mcp_servers:
98
- self.logger.info(f"🌐 Servidores MCP detectados: {len(mcp_servers)}")
99
89
  self._initialize_mcp(mcp_servers)
100
- else:
101
- self.logger.debug("⚪ Sin servidores MCP configurados")
102
90
 
103
91
  # Configure persistence layer
104
92
  self.checkpointer = MemorySaver() if use_checkpointer else None
105
- self.logger.debug(f"💾 Checkpointer: {'Habilitado' if use_checkpointer else 'Deshabilitado'}")
106
93
 
107
94
  # Prepare model with bound tools for native function calling
108
- self.logger.info("🤖 Preparando modelo con herramientas...")
109
95
  self.model_with_tools = self._prepare_model_with_tools()
110
96
 
111
97
  # Build modern instruction set with tool descriptions
112
- self.logger.info("📝 Construyendo instrucciones modernas...")
113
98
  self.instructions = self._build_modern_instructions()
114
- self.logger.debug(f"📋 Instrucciones finales: {len(self.instructions)} caracteres")
115
99
 
116
100
  # Create the LangGraph workflow
117
- self.logger.info("🔄 Creando workflow de LangGraph...")
118
101
  self.graph = self._create_modern_workflow()
119
102
 
120
103
  # Legacy compatibility attributes (maintained for API compatibility)
121
104
  self.conversation = None
122
105
  self.agent_executor = None
123
-
124
- self.logger.info("✅ LangChainBot inicializado correctamente")
125
- self.logger.info(f"📊 Resumen: {len(self.tools)} herramientas, {len(self.chat_history)} mensajes en historial")
126
- self.logger.info("="*80 + "\n")
127
106
 
128
107
  def _initialize_mcp(self, mcp_servers: Dict[str, Any]):
129
108
  """
@@ -146,56 +125,14 @@ class LangChainBot:
146
125
  MCP tools are automatically appended to the existing tools list and
147
126
  will be included in the model's tool binding process.
148
127
  """
149
- self.logger.info("="*80)
150
- self.logger.info("🌐 INICIALIZANDO MCP (Model Context Protocol)")
151
- self.logger.info("="*80)
152
-
153
128
  try:
154
- self.logger.info(f"📋 Servidores a inicializar: {len(mcp_servers)}")
155
-
156
- for server_name, server_config in mcp_servers.items():
157
- self.logger.info(f"\n🔌 Servidor: {server_name}")
158
- self.logger.debug(f" Command: {server_config.get('command')}")
159
- self.logger.debug(f" Args: {server_config.get('args')}")
160
- self.logger.debug(f" Transport: {server_config.get('transport')}")
161
-
162
- self.logger.info("\n🔄 Creando MultiServerMCPClient...")
163
129
  self.mcp_client = MultiServerMCPClient(mcp_servers)
164
- self.logger.info("✅ MultiServerMCPClient creado")
165
-
166
- self.logger.info("🔄 Obteniendo herramientas desde servidores MCP...")
167
130
  mcp_tools = asyncio.run(self.mcp_client.get_tools())
168
-
169
- self.logger.info(f"📥 Herramientas MCP recibidas: {len(mcp_tools)}")
170
-
171
- if mcp_tools:
172
- for i, tool in enumerate(mcp_tools, 1):
173
- tool_name = getattr(tool, 'name', 'Unknown')
174
- tool_desc = getattr(tool, 'description', 'Sin descripción')
175
- self.logger.debug(f" {i}. {tool_name}: {tool_desc[:100]}...")
176
-
177
131
  self.tools.extend(mcp_tools)
178
-
179
- self.logger.info(f"✅ MCP inicializado exitosamente")
180
- self.logger.info(f"📊 Total herramientas disponibles: {len(self.tools)}")
181
- self.logger.info(f" - Herramientas MCP: {len(mcp_tools)}")
182
- self.logger.info(f" - Herramientas previas: {len(self.tools) - len(mcp_tools)}")
183
- self.logger.info("="*80 + "\n")
184
-
185
132
  except Exception as e:
186
- self.logger.error("="*80)
187
- self.logger.error("❌ ERROR EN INICIALIZACIÓN MCP")
188
- self.logger.error("="*80)
189
- self.logger.error(f"Tipo de error: {type(e).__name__}")
190
- self.logger.error(f"Mensaje: {str(e)}")
133
+ self.logger.error(f"Error inicializando MCP: {e}")
191
134
  self.logger.exception("Traceback completo:")
192
- self.logger.error("="*80 + "\n")
193
-
194
135
  self.mcp_client = None
195
-
196
- # Mensaje de diagnóstico
197
- self.logger.warning("⚠️ Continuando sin MCP - solo herramientas locales disponibles")
198
- self.logger.warning(f" Herramientas disponibles: {len(self.tools)}")
199
136
 
200
137
  def _prepare_model_with_tools(self):
201
138
  """
@@ -208,31 +145,13 @@ class LangChainBot:
208
145
  The language model with tools bound, or the original model if no tools are available
209
146
  """
210
147
  if self.tools:
211
- self.logger.info(f"🔗 Vinculando {len(self.tools)} herramientas al modelo")
212
- try:
213
- bound_model = self.language_model.model.bind_tools(self.tools)
214
- self.logger.info("✅ Herramientas vinculadas correctamente")
215
- return bound_model
216
- except Exception as e:
217
- self.logger.error(f"❌ Error vinculando herramientas: {e}")
218
- self.logger.exception("Traceback:")
219
- return self.language_model.model
220
- else:
221
- self.logger.debug("⚪ Sin herramientas para vincular, usando modelo base")
222
- return self.language_model.model
148
+ return self.language_model.model.bind_tools(self.tools)
149
+ return self.language_model.model
223
150
 
224
151
  def _build_modern_instructions(self) -> str:
225
- """
226
- Build modern instructions with automatic tool documentation.
227
-
228
- Returns:
229
- str: Enhanced instructions with tool descriptions
230
- """
231
152
  instructions = self.base_instructions
232
153
 
233
154
  if self.tools:
234
- self.logger.info(f"📝 Generando documentación para {len(self.tools)} herramientas")
235
-
236
155
  tools_description = "\n\n# Available Tools\n\n"
237
156
 
238
157
  for tool in self.tools:
@@ -246,7 +165,7 @@ class LangChainBot:
246
165
  required = "**REQUIRED**" if field_info.is_required() else "*optional*"
247
166
  tools_description += f"- `{field_name}` ({field_info.annotation.__name__}, {required}): {field_info.description}\n"
248
167
 
249
- # Opción 2: args_schema es un dict (MCP Tools)
168
+ # Opción 2: args_schema es un dict (MCP Tools) ← NUEVO
250
169
  elif hasattr(tool, 'args_schema') and isinstance(tool.args_schema, dict):
251
170
  if 'properties' in tool.args_schema:
252
171
  tools_description += f"**Parameters:**\n"
@@ -276,7 +195,6 @@ class LangChainBot:
276
195
  "- Do NOT call tools with empty arguments\n")
277
196
 
278
197
  instructions += tools_description
279
- self.logger.info(f"✅ Documentación de herramientas agregada ({len(tools_description)} caracteres)")
280
198
 
281
199
  return instructions
282
200
 
@@ -293,14 +211,24 @@ class LangChainBot:
293
211
  Returns:
294
212
  StateGraph: Compiled LangGraph workflow ready for execution
295
213
  """
296
- self.logger.info("🔄 Construyendo workflow de LangGraph")
297
214
 
298
215
  def agent_node(state: ChatState) -> ChatState:
299
216
  """
300
217
  Main agent node responsible for generating responses and initiating tool calls.
301
- """
302
- self.logger.debug("🤖 Ejecutando agent_node")
303
218
 
219
+ This node:
220
+ 1. Extracts the latest user message from the conversation state
221
+ 2. Retrieves relevant context from processed files
222
+ 3. Constructs a complete message history for the model
223
+ 4. Invokes the model with tool binding for native function calling
224
+ 5. Returns updated state with the model's response
225
+
226
+ Args:
227
+ state (ChatState): Current conversation state
228
+
229
+ Returns:
230
+ ChatState: Updated state with agent response
231
+ """
304
232
  # Extract the most recent user message
305
233
  last_user_message = None
306
234
  for msg in reversed(state["messages"]):
@@ -309,15 +237,10 @@ class LangChainBot:
309
237
  break
310
238
 
311
239
  if not last_user_message:
312
- self.logger.warning("⚠️ No se encontró mensaje de usuario")
313
240
  return state
314
241
 
315
- self.logger.debug(f"💬 Mensaje usuario: {last_user_message[:100]}...")
316
-
317
242
  # Retrieve contextual information from processed files
318
243
  context = self._get_context(last_user_message)
319
- if context:
320
- self.logger.debug(f"📚 Contexto recuperado: {len(context)} caracteres")
321
244
 
322
245
  # Build system prompt with optional context
323
246
  system_content = self.instructions
@@ -334,33 +257,24 @@ class LangChainBot:
334
257
  elif isinstance(msg, AIMessage):
335
258
  messages.append({"role": "assistant", "content": msg.content or ""})
336
259
  elif isinstance(msg, ToolMessage):
260
+ # Convert tool results to user messages for context
337
261
  messages.append({"role": "user", "content": f"Tool result: {msg.content}"})
338
262
 
339
- self.logger.debug(f"📨 Enviando {len(messages)} mensajes al modelo")
340
-
341
263
  try:
342
264
  # Invoke model with native tool binding
343
265
  response = self.model_with_tools.invoke(messages)
344
266
 
345
- self.logger.debug(f"✅ Respuesta recibida del modelo")
346
-
347
- # Check for tool calls
348
- if hasattr(response, 'tool_calls') and response.tool_calls:
349
- self.logger.info(f"🔧 Llamadas a herramientas detectadas: {len(response.tool_calls)}")
350
- for i, tc in enumerate(response.tool_calls, 1):
351
- tool_name = tc.get('name', 'Unknown')
352
- self.logger.debug(f" {i}. {tool_name}")
353
-
354
267
  # Return updated state
355
268
  return {
356
269
  **state,
357
270
  "context": context,
358
- "messages": [response]
271
+ "messages": [response] # add_messages annotation handles proper appending
359
272
  }
360
273
 
361
274
  except Exception as e:
362
- self.logger.error(f"Error en agent_node: {e}")
363
- self.logger.exception("Traceback:")
275
+ self.logger.error(f"Error en agent_node: {e}")
276
+ self.logger.exception("Traceback completo:")
277
+ # Graceful fallback for error scenarios
364
278
  fallback_response = AIMessage(content="I apologize, but I encountered an error processing your request.")
365
279
  return {
366
280
  **state,
@@ -371,16 +285,24 @@ class LangChainBot:
371
285
  def should_continue(state: ChatState) -> str:
372
286
  """
373
287
  Conditional edge function to determine workflow continuation.
288
+
289
+ Analyzes the last message to decide whether to execute tools or end the workflow.
290
+ This leverages LangGraph's native tool calling detection.
291
+
292
+ Args:
293
+ state (ChatState): Current conversation state
294
+
295
+ Returns:
296
+ str: Next node to execute ("tools" or "end")
374
297
  """
375
298
  last_message = state["messages"][-1]
376
299
 
300
+ # Check for pending tool calls using native tool calling detection
377
301
  if (isinstance(last_message, AIMessage) and
378
302
  hasattr(last_message, 'tool_calls') and
379
303
  last_message.tool_calls):
380
- self.logger.debug("➡️ Continuando a ejecución de herramientas")
381
304
  return "tools"
382
305
 
383
- self.logger.debug("🏁 Finalizando workflow")
384
306
  return "end"
385
307
 
386
308
  # Construct the workflow graph
@@ -388,18 +310,18 @@ class LangChainBot:
388
310
 
389
311
  # Add primary agent node
390
312
  workflow.add_node("agent", agent_node)
391
- self.logger.debug("✅ Nodo 'agent' agregado")
392
313
 
393
314
  # Add tool execution node if tools are available
394
315
  if self.tools:
316
+ # ToolNode automatically handles tool execution and result formatting
395
317
  tool_node = ToolNode(self.tools)
396
318
  workflow.add_node("tools", tool_node)
397
- self.logger.debug("✅ Nodo 'tools' agregado")
398
319
 
399
320
  # Define workflow edges and entry point
400
321
  workflow.set_entry_point("agent")
401
322
 
402
323
  if self.tools:
324
+ # Conditional routing based on tool call presence
403
325
  workflow.add_conditional_edges(
404
326
  "agent",
405
327
  should_continue,
@@ -408,21 +330,17 @@ class LangChainBot:
408
330
  "end": END
409
331
  }
410
332
  )
333
+ # Return to agent after tool execution for final response formatting
411
334
  workflow.add_edge("tools", "agent")
412
- self.logger.debug("✅ Edges condicionales configurados")
413
335
  else:
336
+ # Direct termination if no tools are available
414
337
  workflow.add_edge("agent", END)
415
- self.logger.debug("✅ Edge directo a END configurado")
416
338
 
417
339
  # Compile workflow with optional checkpointing
418
340
  if self.checkpointer:
419
- compiled = workflow.compile(checkpointer=self.checkpointer)
420
- self.logger.info("✅ Workflow compilado con checkpointer")
341
+ return workflow.compile(checkpointer=self.checkpointer)
421
342
  else:
422
- compiled = workflow.compile()
423
- self.logger.info("✅ Workflow compilado sin checkpointer")
424
-
425
- return compiled
343
+ return workflow.compile()
426
344
 
427
345
  # ===== LEGACY API COMPATIBILITY =====
428
346
 
@@ -446,70 +364,59 @@ class LangChainBot:
446
364
  This method automatically handles tool execution and context integration
447
365
  from processed files while maintaining the original API signature.
448
366
  """
449
- self.logger.info("="*80)
450
- self.logger.info("📨 GET_RESPONSE llamado")
451
- self.logger.debug(f"💬 Input: {user_input[:200]}...")
452
-
453
367
  # Prepare initial workflow state
454
368
  initial_state = {
455
369
  "messages": self.chat_history + [HumanMessage(content=user_input)],
456
370
  "context": ""
457
371
  }
458
372
 
459
- self.logger.debug(f"📊 Estado inicial: {len(initial_state['messages'])} mensajes")
460
-
461
- try:
462
- # Execute the LangGraph workflow
463
- self.logger.info("🔄 Ejecutando workflow...")
464
- result = asyncio.run(self.graph.ainvoke(initial_state))
465
- self.logger.info("✅ Workflow completado")
466
-
467
- # Update internal conversation history
468
- self.chat_history = result["messages"]
469
- self.logger.debug(f"💾 Historial actualizado: {len(self.chat_history)} mensajes")
470
-
471
- # Extract final response from the last assistant message
472
- final_response = ""
473
- total_input_tokens = 0
474
- total_output_tokens = 0
475
-
476
- for msg in reversed(result["messages"]):
477
- if isinstance(msg, AIMessage) and msg.content:
478
- final_response = msg.content
479
- break
480
-
481
- # Extract token usage from response metadata
482
- last_message = result["messages"][-1]
483
- if hasattr(last_message, 'response_metadata'):
484
- token_usage = last_message.response_metadata.get('token_usage', {})
485
- total_input_tokens = token_usage.get('prompt_tokens', 0)
486
- total_output_tokens = token_usage.get('completion_tokens', 0)
487
-
488
- self.logger.info(f"📊 Tokens: input={total_input_tokens}, output={total_output_tokens}")
489
- self.logger.info(f"📝 Respuesta: {len(final_response)} caracteres")
490
- self.logger.info("="*80 + "\n")
491
-
492
- return ResponseModel(
493
- user_tokens=total_input_tokens,
494
- bot_tokens=total_output_tokens,
495
- response=final_response
496
- )
497
-
498
- except Exception as e:
499
- self.logger.error("="*80)
500
- self.logger.error("❌ ERROR EN GET_RESPONSE")
501
- self.logger.error(f"Mensaje: {str(e)}")
502
- self.logger.exception("Traceback:")
503
- self.logger.error("="*80 + "\n")
504
- raise
373
+ # Execute the LangGraph workflow
374
+ # Siempre usar ainvoke (funciona para ambos casos)
375
+ result = asyncio.run(self.graph.ainvoke(initial_state))
376
+
377
+ # Update internal conversation history
378
+ self.chat_history = result["messages"]
379
+
380
+ # Extract final response from the last assistant message
381
+ final_response = ""
382
+ total_input_tokens = 0
383
+ total_output_tokens = 0
384
+
385
+ for msg in reversed(result["messages"]):
386
+ if isinstance(msg, AIMessage) and msg.content:
387
+ final_response = msg.content
388
+ break
389
+
390
+ # Extract token usage from response metadata
391
+ last_message = result["messages"][-1]
392
+ if hasattr(last_message, 'response_metadata'):
393
+ token_usage = last_message.response_metadata.get('token_usage', {})
394
+ total_input_tokens = token_usage.get('prompt_tokens', 0)
395
+ total_output_tokens = token_usage.get('completion_tokens', 0)
396
+
397
+ return ResponseModel(
398
+ user_tokens=total_input_tokens,
399
+ bot_tokens=total_output_tokens,
400
+ response=final_response
401
+ )
505
402
 
506
403
  def get_response_stream(self, user_input: str) -> Generator[str, None, None]:
507
404
  """
508
405
  Generate a streaming response for real-time user interaction.
509
- """
510
- self.logger.info("📨 GET_RESPONSE_STREAM llamado")
511
- self.logger.debug(f"💬 Input: {user_input[:200]}...")
512
406
 
407
+ This method provides streaming capabilities while maintaining backward
408
+ compatibility with the original API.
409
+
410
+ Args:
411
+ user_input (str): The user's message or query
412
+
413
+ Yields:
414
+ str: Response chunks as they are generated
415
+
416
+ Note:
417
+ Current implementation streams complete responses. For token-level
418
+ streaming, consider using the model's native streaming capabilities.
419
+ """
513
420
  initial_state = {
514
421
  "messages": self.chat_history + [HumanMessage(content=user_input)],
515
422
  "context": ""
@@ -517,105 +424,156 @@ class LangChainBot:
517
424
 
518
425
  accumulated_response = ""
519
426
 
520
- try:
521
- for chunk in self.graph.stream(initial_state):
522
- if "agent" in chunk:
523
- for message in chunk["agent"]["messages"]:
524
- if isinstance(message, AIMessage) and message.content:
525
- accumulated_response = message.content
526
- yield message.content
527
-
528
- if accumulated_response:
529
- self.chat_history.extend([
530
- HumanMessage(content=user_input),
531
- AIMessage(content=accumulated_response)
532
- ])
533
-
534
- self.logger.info(f"✅ Stream completado: {len(accumulated_response)} caracteres")
535
-
536
- except Exception as e:
537
- self.logger.error(f"❌ Error en stream: {e}")
538
- self.logger.exception("Traceback:")
539
- raise
427
+ # Stream workflow execution
428
+ for chunk in self.graph.stream(initial_state):
429
+ # Extract content from workflow chunks
430
+ if "agent" in chunk:
431
+ for message in chunk["agent"]["messages"]:
432
+ if isinstance(message, AIMessage) and message.content:
433
+ # Stream complete responses (can be enhanced for token-level streaming)
434
+ accumulated_response = message.content
435
+ yield message.content
436
+
437
+ # Update conversation history after streaming completion
438
+ if accumulated_response:
439
+ self.chat_history.extend([
440
+ HumanMessage(content=user_input),
441
+ AIMessage(content=accumulated_response)
442
+ ])
540
443
 
541
444
  def load_conversation_history(self, messages: List[Message]):
542
445
  """
543
446
  Load conversation history from Django model instances.
447
+
448
+ This method maintains compatibility with existing Django-based conversation
449
+ storage while preparing the history for modern LangGraph processing.
450
+
451
+ Args:
452
+ messages (List[Message]): List of Django Message model instances
453
+ Expected to have 'content' and 'is_bot' attributes
544
454
  """
545
- self.logger.info(f"📥 Cargando historial: {len(messages)} mensajes")
546
455
  self.chat_history.clear()
547
456
  for message in messages:
548
457
  if message.is_bot:
549
458
  self.chat_history.append(AIMessage(content=message.content))
550
459
  else:
551
460
  self.chat_history.append(HumanMessage(content=message.content))
552
- self.logger.debug("✅ Historial cargado")
553
461
 
554
462
  def save_messages(self, user_message: str, bot_response: str):
555
463
  """
556
464
  Save messages to internal conversation history.
465
+
466
+ This method provides backward compatibility for manual history management.
467
+
468
+ Args:
469
+ user_message (str): The user's input message
470
+ bot_response (str): The bot's generated response
557
471
  """
558
- self.logger.debug("💾 Guardando mensajes en historial interno")
559
472
  self.chat_history.append(HumanMessage(content=user_message))
560
473
  self.chat_history.append(AIMessage(content=bot_response))
561
474
 
562
475
  def process_file(self, file: FileProcessorInterface):
563
476
  """
564
477
  Process and index a file for contextual retrieval.
565
- """
566
- self.logger.info("📄 Procesando archivo para indexación")
567
- try:
568
- document = file.getText()
569
- text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
570
- texts = text_splitter.split_documents(document)
478
+
479
+ This method maintains compatibility with existing file processing workflows
480
+ while leveraging FAISS for efficient similarity search.
481
+
482
+ Args:
483
+ file (FileProcessorInterface): File processor instance that implements getText()
571
484
 
572
- self.logger.debug(f"✂️ Documento dividido en {len(texts)} chunks")
485
+ Note:
486
+ Processed files are automatically available for context retrieval
487
+ in subsequent conversations without additional configuration.
488
+ """
489
+ document = file.getText()
490
+ text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
491
+ texts = text_splitter.split_documents(document)
573
492
 
574
- if self.vector_store is None:
575
- self.vector_store = FAISS.from_texts(
576
- [doc.page_content for doc in texts],
577
- self.embeddings
578
- )
579
- self.logger.info("✅ Vector store creado")
580
- else:
581
- self.vector_store.add_texts([doc.page_content for doc in texts])
582
- self.logger.info("✅ Textos agregados a vector store existente")
583
-
584
- except Exception as e:
585
- self.logger.error(f"❌ Error procesando archivo: {e}")
586
- self.logger.exception("Traceback:")
587
- raise
493
+ if self.vector_store is None:
494
+ self.vector_store = FAISS.from_texts(
495
+ [doc.page_content for doc in texts],
496
+ self.embeddings
497
+ )
498
+ else:
499
+ self.vector_store.add_texts([doc.page_content for doc in texts])
588
500
 
589
501
  def clear_memory(self):
590
502
  """
591
503
  Clear conversation history and processed file context.
504
+
505
+ This method resets the bot to a clean state, removing all conversation
506
+ history and processed file context.
592
507
  """
593
- self.logger.info("🗑️ Limpiando memoria")
594
508
  self.chat_history.clear()
595
509
  self.vector_store = None
596
- self.logger.debug("✅ Memoria limpiada")
597
510
 
598
511
  def get_chat_history(self) -> List[BaseMessage]:
599
512
  """
600
513
  Retrieve a copy of the current conversation history.
514
+
515
+ Returns:
516
+ List[BaseMessage]: Copy of the conversation history
601
517
  """
602
518
  return self.chat_history.copy()
603
519
 
604
520
  def set_chat_history(self, history: List[BaseMessage]):
605
521
  """
606
522
  Set the conversation history from a list of BaseMessage instances.
523
+
524
+ Args:
525
+ history (List[BaseMessage]): New conversation history to set
607
526
  """
608
- self.logger.info(f"📝 Estableciendo historial: {len(history)} mensajes")
609
527
  self.chat_history = history.copy()
610
528
 
611
529
  def _get_context(self, query: str) -> str:
612
530
  """
613
531
  Retrieve relevant context from processed files using similarity search.
532
+
533
+ This method performs semantic search over processed file content to find
534
+ the most relevant information for the current query.
535
+
536
+ Args:
537
+ query (str): The query to search for relevant context
538
+
539
+ Returns:
540
+ str: Concatenated relevant context from processed files
614
541
  """
615
542
  if self.vector_store:
616
- self.logger.debug(f"🔍 Buscando contexto para query: {query[:100]}...")
617
543
  docs = self.vector_store.similarity_search(query, k=4)
618
- context = "\n".join([doc.page_content for doc in docs])
619
- self.logger.debug(f"✅ Contexto encontrado: {len(context)} caracteres")
620
- return context
544
+ return "\n".join([doc.page_content for doc in docs])
545
+ return ""
546
+
547
+ def process_file(self, file: FileProcessorInterface):
548
+ """API original - Procesa archivo y lo añade al vector store"""
549
+ document = file.getText()
550
+ text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
551
+ texts = text_splitter.split_documents(document)
552
+
553
+ if self.vector_store is None:
554
+ self.vector_store = FAISS.from_texts(
555
+ [doc.page_content for doc in texts],
556
+ self.embeddings
557
+ )
558
+ else:
559
+ self.vector_store.add_texts([doc.page_content for doc in texts])
560
+
561
+ def clear_memory(self):
562
+ """API original - Limpia la memoria de conversación"""
563
+ self.chat_history.clear()
564
+ self.vector_store = None
565
+
566
+ def get_chat_history(self) -> List[BaseMessage]:
567
+ """API original - Obtiene el historial completo"""
568
+ return self.chat_history.copy()
569
+
570
+ def set_chat_history(self, history: List[BaseMessage]):
571
+ """API original - Establece el historial de conversación"""
572
+ self.chat_history = history.copy()
573
+
574
+ def _get_context(self, query: str) -> str:
575
+ """Obtiene contexto relevante de archivos procesados"""
576
+ if self.vector_store:
577
+ docs = self.vector_store.similarity_search(query, k=4)
578
+ return "\n".join([doc.page_content for doc in docs])
621
579
  return ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sonika-langchain-bot
3
- Version: 0.0.16
3
+ Version: 0.0.18
4
4
  Summary: Agente langchain con LLM
5
5
  Author: Erley Blanco Carvajal
6
6
  License: MIT License
@@ -1,15 +1,15 @@
1
1
  sonika_langchain_bot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  sonika_langchain_bot/document_processor.py,sha256=RuHT22Zt-psoe4adFWKwBJ0gi638fq8r2S5WZoDK8fY,10979
3
3
  sonika_langchain_bot/langchain_bdi.py,sha256=ithc55azP5XSPb8AGRUrDGYnVI6I4IqpqElLNat4BAQ,7024
4
- sonika_langchain_bot/langchain_bot_agent.py,sha256=VfYx5HFhZvslKoC-2aH5d8iGi1VCwVkfCnWIOhiPXHA,27752
4
+ sonika_langchain_bot/langchain_bot_agent.py,sha256=wdQrIVUxmkU-VmGfrdIcnDJPdKmRQXgeKYf_50By5cE,24359
5
5
  sonika_langchain_bot/langchain_bot_agent_bdi.py,sha256=Ev0hhRQYe6kyGAHiFDhFsfu6QnTwUFaA9oB8DfNV7u4,8613
6
6
  sonika_langchain_bot/langchain_clasificator.py,sha256=GR85ZAliymBSoDa5PXB31BvJkuiokGjS2v3RLdXnzzk,1381
7
7
  sonika_langchain_bot/langchain_class.py,sha256=5anB6v_wCzEoAJRb8fV9lPPS72E7-k51y_aeiip8RAw,1114
8
8
  sonika_langchain_bot/langchain_files.py,sha256=SEyqnJgBc_nbCIG31eypunBbO33T5AHFOhQZcghTks4,381
9
9
  sonika_langchain_bot/langchain_models.py,sha256=vqSSZ48tNofrTMLv1QugDdyey2MuIeSdlLSD37AnzkI,2235
10
10
  sonika_langchain_bot/langchain_tools.py,sha256=y7wLf1DbUua3QIvz938Ek-JIMOuQhrOIptJadW8OIsU,466
11
- sonika_langchain_bot-0.0.16.dist-info/licenses/LICENSE,sha256=O8VZ4aU_rUMAArvYTm2bshcZ991huv_tpfB5BKHH9Q8,1064
12
- sonika_langchain_bot-0.0.16.dist-info/METADATA,sha256=W4D-pG07cUdhxOruzIq38scWPJAdmKdyJHcT-hvkklI,6508
13
- sonika_langchain_bot-0.0.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- sonika_langchain_bot-0.0.16.dist-info/top_level.txt,sha256=UsTTSZFEw2wrPSVh4ufu01e2m_E7O_QVYT_k4zCQaAE,21
15
- sonika_langchain_bot-0.0.16.dist-info/RECORD,,
11
+ sonika_langchain_bot-0.0.18.dist-info/licenses/LICENSE,sha256=O8VZ4aU_rUMAArvYTm2bshcZ991huv_tpfB5BKHH9Q8,1064
12
+ sonika_langchain_bot-0.0.18.dist-info/METADATA,sha256=mfLJal88ZamfkvOouwQZ01VwVz9784vb5ynFOr5Ust0,6508
13
+ sonika_langchain_bot-0.0.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ sonika_langchain_bot-0.0.18.dist-info/top_level.txt,sha256=UsTTSZFEw2wrPSVh4ufu01e2m_E7O_QVYT_k4zCQaAE,21
15
+ sonika_langchain_bot-0.0.18.dist-info/RECORD,,