sonika-langchain-bot 0.0.15__py3-none-any.whl → 0.0.20__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.
- sonika_langchain_bot/langchain_bot_agent.py +166 -48
- sonika_langchain_bot/langchain_clasificator.py +48 -12
- {sonika_langchain_bot-0.0.15.dist-info → sonika_langchain_bot-0.0.20.dist-info}/METADATA +1 -1
- {sonika_langchain_bot-0.0.15.dist-info → sonika_langchain_bot-0.0.20.dist-info}/RECORD +7 -7
- {sonika_langchain_bot-0.0.15.dist-info → sonika_langchain_bot-0.0.20.dist-info}/WHEEL +0 -0
- {sonika_langchain_bot-0.0.15.dist-info → sonika_langchain_bot-0.0.20.dist-info}/licenses/LICENSE +0 -0
- {sonika_langchain_bot-0.0.15.dist-info → sonika_langchain_bot-0.0.20.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
from typing import Generator, List, Optional, Dict, Any, TypedDict, Annotated
|
|
1
|
+
from typing import Generator, List, Optional, Dict, Any, TypedDict, Annotated, Callable
|
|
2
2
|
import asyncio
|
|
3
|
+
import logging
|
|
3
4
|
from langchain.schema import AIMessage, HumanMessage, BaseMessage
|
|
4
5
|
from langchain_core.messages import ToolMessage
|
|
5
6
|
from langchain.text_splitter import CharacterTextSplitter
|
|
6
7
|
from langchain_community.vectorstores import FAISS
|
|
7
8
|
from langchain_community.tools import BaseTool
|
|
9
|
+
from langchain.callbacks.base import BaseCallbackHandler
|
|
8
10
|
from langgraph.graph import StateGraph, END, add_messages
|
|
9
11
|
from langgraph.prebuilt import ToolNode
|
|
10
12
|
from langgraph.checkpoint.memory import MemorySaver
|
|
@@ -24,6 +26,101 @@ class ChatState(TypedDict):
|
|
|
24
26
|
messages: Annotated[List[BaseMessage], add_messages]
|
|
25
27
|
context: str
|
|
26
28
|
|
|
29
|
+
class _InternalToolLogger(BaseCallbackHandler):
|
|
30
|
+
"""
|
|
31
|
+
Internal callback handler that bridges LangChain callbacks to user-provided functions.
|
|
32
|
+
|
|
33
|
+
This class is used internally to forward tool execution events to the optional
|
|
34
|
+
callback functions provided by the user during bot initialization.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self,
|
|
38
|
+
on_start: Optional[Callable[[str, str], None]] = None,
|
|
39
|
+
on_end: Optional[Callable[[str, str], None]] = None,
|
|
40
|
+
on_error: Optional[Callable[[str, str], None]] = None):
|
|
41
|
+
"""
|
|
42
|
+
Initialize the internal tool logger.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
on_start: Optional callback function called when a tool starts execution
|
|
46
|
+
on_end: Optional callback function called when a tool completes successfully
|
|
47
|
+
on_error: Optional callback function called when a tool encounters an error
|
|
48
|
+
"""
|
|
49
|
+
super().__init__()
|
|
50
|
+
self.on_start_callback = on_start
|
|
51
|
+
self.on_end_callback = on_end
|
|
52
|
+
self.on_error_callback = on_error
|
|
53
|
+
self.current_tool_name = None
|
|
54
|
+
self.tool_executions = [] # Para tracking interno si se necesita
|
|
55
|
+
|
|
56
|
+
def on_tool_start(self, serialized: Dict[str, Any], input_str: str, **kwargs) -> None:
|
|
57
|
+
|
|
58
|
+
print(f"DEBUG: on_tool_start se ejecutó!") # ← AGREGAR ESTO
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
"""Called when a tool starts executing."""
|
|
62
|
+
tool_name = serialized.get("name", "unknown")
|
|
63
|
+
self.current_tool_name = tool_name
|
|
64
|
+
|
|
65
|
+
# Track execution internally
|
|
66
|
+
self.tool_executions.append({
|
|
67
|
+
"tool": tool_name,
|
|
68
|
+
"input": input_str,
|
|
69
|
+
"status": "started"
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
# Call user's callback if provided
|
|
73
|
+
if self.on_start_callback:
|
|
74
|
+
try:
|
|
75
|
+
self.on_start_callback(tool_name, input_str)
|
|
76
|
+
except Exception as e:
|
|
77
|
+
# Don't let user callback errors break the workflow
|
|
78
|
+
logging.error(f"Error in on_tool_start callback: {e}")
|
|
79
|
+
|
|
80
|
+
def on_tool_end(self, output: str, **kwargs) -> None:
|
|
81
|
+
print(f"DEBUG: on_tool_end se ejecutó!")
|
|
82
|
+
tool_name = self.current_tool_name or "unknown"
|
|
83
|
+
|
|
84
|
+
# Convert output to string if it's a ToolMessage or other object
|
|
85
|
+
if hasattr(output, 'content'):
|
|
86
|
+
output_str = output.content
|
|
87
|
+
elif isinstance(output, str):
|
|
88
|
+
output_str = output
|
|
89
|
+
else:
|
|
90
|
+
output_str = str(output)
|
|
91
|
+
|
|
92
|
+
# Update internal tracking
|
|
93
|
+
if self.tool_executions:
|
|
94
|
+
self.tool_executions[-1]["status"] = "success"
|
|
95
|
+
self.tool_executions[-1]["output"] = output_str
|
|
96
|
+
|
|
97
|
+
# Call user's callback if provided
|
|
98
|
+
if self.on_end_callback:
|
|
99
|
+
try:
|
|
100
|
+
self.on_end_callback(tool_name, output_str)
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logging.error(f"Error in on_tool_end callback: {e}")
|
|
103
|
+
|
|
104
|
+
self.current_tool_name = None
|
|
105
|
+
|
|
106
|
+
def on_tool_error(self, error: Exception, **kwargs) -> None: # ← CORRECTO
|
|
107
|
+
print(f"DEBUG: on_tool_error se ejecutó!")
|
|
108
|
+
tool_name = self.current_tool_name or "unknown"
|
|
109
|
+
error_message = str(error)
|
|
110
|
+
|
|
111
|
+
# Update internal tracking
|
|
112
|
+
if self.tool_executions:
|
|
113
|
+
self.tool_executions[-1]["status"] = "error"
|
|
114
|
+
self.tool_executions[-1]["error"] = error_message
|
|
115
|
+
|
|
116
|
+
# Call user's callback if provided
|
|
117
|
+
if self.on_error_callback:
|
|
118
|
+
try:
|
|
119
|
+
self.on_error_callback(tool_name, error_message)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logging.error(f"Error in on_tool_error callback: {e}")
|
|
122
|
+
|
|
123
|
+
self.current_tool_name = None
|
|
27
124
|
|
|
28
125
|
class LangChainBot:
|
|
29
126
|
"""
|
|
@@ -38,6 +135,7 @@ class LangChainBot:
|
|
|
38
135
|
- File processing with vector search
|
|
39
136
|
- Thread-based conversation persistence
|
|
40
137
|
- Streaming responses
|
|
138
|
+
- Tool execution callbacks for real-time monitoring
|
|
41
139
|
- Backward compatibility with legacy APIs
|
|
42
140
|
"""
|
|
43
141
|
|
|
@@ -47,9 +145,13 @@ class LangChainBot:
|
|
|
47
145
|
instructions: str,
|
|
48
146
|
tools: Optional[List[BaseTool]] = None,
|
|
49
147
|
mcp_servers: Optional[Dict[str, Any]] = None,
|
|
50
|
-
use_checkpointer: bool = False
|
|
148
|
+
use_checkpointer: bool = False,
|
|
149
|
+
logger: Optional[logging.Logger] = None,
|
|
150
|
+
on_tool_start: Optional[Callable[[str, str], None]] = None,
|
|
151
|
+
on_tool_end: Optional[Callable[[str, str], None]] = None,
|
|
152
|
+
on_tool_error: Optional[Callable[[str, str], None]] = None):
|
|
51
153
|
"""
|
|
52
|
-
Initialize the modern LangGraph bot with optional MCP support.
|
|
154
|
+
Initialize the modern LangGraph bot with optional MCP support and tool execution callbacks.
|
|
53
155
|
|
|
54
156
|
Args:
|
|
55
157
|
language_model (ILanguageModel): The language model to use for generation
|
|
@@ -58,11 +160,36 @@ class LangChainBot:
|
|
|
58
160
|
tools (List[BaseTool], optional): Traditional LangChain tools to bind to the model
|
|
59
161
|
mcp_servers (Dict[str, Any], optional): MCP server configurations for dynamic tool loading
|
|
60
162
|
use_checkpointer (bool): Enable automatic conversation persistence using LangGraph checkpoints
|
|
163
|
+
logger (Optional[logging.Logger]): Logger instance for error tracking (silent by default if not provided)
|
|
164
|
+
on_tool_start (Callable[[str, str], None], optional): Callback function executed when a tool starts.
|
|
165
|
+
Receives (tool_name: str, input_data: str)
|
|
166
|
+
on_tool_end (Callable[[str, str], None], optional): Callback function executed when a tool completes successfully.
|
|
167
|
+
Receives (tool_name: str, output: str)
|
|
168
|
+
on_tool_error (Callable[[str, str], None], optional): Callback function executed when a tool fails.
|
|
169
|
+
Receives (tool_name: str, error_message: str)
|
|
61
170
|
|
|
62
171
|
Note:
|
|
63
172
|
The instructions will be automatically enhanced with tool descriptions
|
|
64
173
|
when tools are provided, eliminating the need for manual tool instruction formatting.
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
```python
|
|
177
|
+
def on_tool_execution(tool_name: str, input_data: str):
|
|
178
|
+
print(f"Tool {tool_name} started with input: {input_data}")
|
|
179
|
+
|
|
180
|
+
bot = LangChainBot(
|
|
181
|
+
language_model=model,
|
|
182
|
+
embeddings=embeddings,
|
|
183
|
+
instructions="You are a helpful assistant",
|
|
184
|
+
on_tool_start=on_tool_execution
|
|
185
|
+
)
|
|
186
|
+
```
|
|
65
187
|
"""
|
|
188
|
+
# Configure logger (silent by default if not provided)
|
|
189
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
190
|
+
if logger is None:
|
|
191
|
+
self.logger.addHandler(logging.NullHandler())
|
|
192
|
+
|
|
66
193
|
# Core components
|
|
67
194
|
self.language_model = language_model
|
|
68
195
|
self.embeddings = embeddings
|
|
@@ -76,6 +203,11 @@ class LangChainBot:
|
|
|
76
203
|
self.tools = tools or []
|
|
77
204
|
self.mcp_client = None
|
|
78
205
|
|
|
206
|
+
# Tool execution callbacks
|
|
207
|
+
self.on_tool_start = on_tool_start
|
|
208
|
+
self.on_tool_end = on_tool_end
|
|
209
|
+
self.on_tool_error = on_tool_error
|
|
210
|
+
|
|
79
211
|
# Initialize MCP servers if provided
|
|
80
212
|
if mcp_servers:
|
|
81
213
|
self._initialize_mcp(mcp_servers)
|
|
@@ -121,9 +253,9 @@ class LangChainBot:
|
|
|
121
253
|
self.mcp_client = MultiServerMCPClient(mcp_servers)
|
|
122
254
|
mcp_tools = asyncio.run(self.mcp_client.get_tools())
|
|
123
255
|
self.tools.extend(mcp_tools)
|
|
124
|
-
print(f"✅ MCP initialized: {len(mcp_tools)} tools from {len(mcp_servers)} servers")
|
|
125
256
|
except Exception as e:
|
|
126
|
-
|
|
257
|
+
self.logger.error(f"Error inicializando MCP: {e}")
|
|
258
|
+
self.logger.exception("Traceback completo:")
|
|
127
259
|
self.mcp_client = None
|
|
128
260
|
|
|
129
261
|
def _prepare_model_with_tools(self):
|
|
@@ -264,7 +396,8 @@ class LangChainBot:
|
|
|
264
396
|
}
|
|
265
397
|
|
|
266
398
|
except Exception as e:
|
|
267
|
-
|
|
399
|
+
self.logger.error(f"Error en agent_node: {e}")
|
|
400
|
+
self.logger.exception("Traceback completo:")
|
|
268
401
|
# Graceful fallback for error scenarios
|
|
269
402
|
fallback_response = AIMessage(content="I apologize, but I encountered an error processing your request.")
|
|
270
403
|
return {
|
|
@@ -341,6 +474,7 @@ class LangChainBot:
|
|
|
341
474
|
|
|
342
475
|
This method provides the primary interface for single-turn conversations,
|
|
343
476
|
maintaining backward compatibility with existing ChatService implementations.
|
|
477
|
+
Tool execution callbacks (if provided) will be triggered during execution.
|
|
344
478
|
|
|
345
479
|
Args:
|
|
346
480
|
user_input (str): The user's message or query
|
|
@@ -361,11 +495,18 @@ class LangChainBot:
|
|
|
361
495
|
"context": ""
|
|
362
496
|
}
|
|
363
497
|
|
|
364
|
-
#
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
498
|
+
# Create callback handler if any callbacks are provided
|
|
499
|
+
config = {}
|
|
500
|
+
if self.on_tool_start or self.on_tool_end or self.on_tool_error:
|
|
501
|
+
tool_logger = _InternalToolLogger(
|
|
502
|
+
on_start=self.on_tool_start,
|
|
503
|
+
on_end=self.on_tool_end,
|
|
504
|
+
on_error=self.on_tool_error
|
|
505
|
+
)
|
|
506
|
+
config["callbacks"] = [tool_logger]
|
|
507
|
+
|
|
508
|
+
# Execute the LangGraph workflow with callbacks
|
|
509
|
+
result = asyncio.run(self.graph.ainvoke(initial_state, config=config))
|
|
369
510
|
|
|
370
511
|
# Update internal conversation history
|
|
371
512
|
self.chat_history = result["messages"]
|
|
@@ -398,7 +539,8 @@ class LangChainBot:
|
|
|
398
539
|
Generate a streaming response for real-time user interaction.
|
|
399
540
|
|
|
400
541
|
This method provides streaming capabilities while maintaining backward
|
|
401
|
-
compatibility with the original API.
|
|
542
|
+
compatibility with the original API. Tool execution callbacks (if provided)
|
|
543
|
+
will be triggered during execution.
|
|
402
544
|
|
|
403
545
|
Args:
|
|
404
546
|
user_input (str): The user's message or query
|
|
@@ -415,10 +557,20 @@ class LangChainBot:
|
|
|
415
557
|
"context": ""
|
|
416
558
|
}
|
|
417
559
|
|
|
560
|
+
# Create callback handler if any callbacks are provided
|
|
561
|
+
config = {}
|
|
562
|
+
if self.on_tool_start or self.on_tool_end or self.on_tool_error:
|
|
563
|
+
tool_logger = _InternalToolLogger(
|
|
564
|
+
on_start=self.on_tool_start,
|
|
565
|
+
on_end=self.on_tool_end,
|
|
566
|
+
on_error=self.on_tool_error
|
|
567
|
+
)
|
|
568
|
+
config["callbacks"] = [tool_logger]
|
|
569
|
+
|
|
418
570
|
accumulated_response = ""
|
|
419
571
|
|
|
420
|
-
# Stream workflow execution
|
|
421
|
-
for chunk in self.graph.stream(initial_state):
|
|
572
|
+
# Stream workflow execution with callbacks
|
|
573
|
+
for chunk in self.graph.stream(initial_state, config=config):
|
|
422
574
|
# Extract content from workflow chunks
|
|
423
575
|
if "agent" in chunk:
|
|
424
576
|
for message in chunk["agent"]["messages"]:
|
|
@@ -532,40 +684,6 @@ class LangChainBot:
|
|
|
532
684
|
Returns:
|
|
533
685
|
str: Concatenated relevant context from processed files
|
|
534
686
|
"""
|
|
535
|
-
if self.vector_store:
|
|
536
|
-
docs = self.vector_store.similarity_search(query, k=4)
|
|
537
|
-
return "\n".join([doc.page_content for doc in docs])
|
|
538
|
-
return ""
|
|
539
|
-
|
|
540
|
-
def process_file(self, file: FileProcessorInterface):
|
|
541
|
-
"""API original - Procesa archivo y lo añade al vector store"""
|
|
542
|
-
document = file.getText()
|
|
543
|
-
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
|
544
|
-
texts = text_splitter.split_documents(document)
|
|
545
|
-
|
|
546
|
-
if self.vector_store is None:
|
|
547
|
-
self.vector_store = FAISS.from_texts(
|
|
548
|
-
[doc.page_content for doc in texts],
|
|
549
|
-
self.embeddings
|
|
550
|
-
)
|
|
551
|
-
else:
|
|
552
|
-
self.vector_store.add_texts([doc.page_content for doc in texts])
|
|
553
|
-
|
|
554
|
-
def clear_memory(self):
|
|
555
|
-
"""API original - Limpia la memoria de conversación"""
|
|
556
|
-
self.chat_history.clear()
|
|
557
|
-
self.vector_store = None
|
|
558
|
-
|
|
559
|
-
def get_chat_history(self) -> List[BaseMessage]:
|
|
560
|
-
"""API original - Obtiene el historial completo"""
|
|
561
|
-
return self.chat_history.copy()
|
|
562
|
-
|
|
563
|
-
def set_chat_history(self, history: List[BaseMessage]):
|
|
564
|
-
"""API original - Establece el historial de conversación"""
|
|
565
|
-
self.chat_history = history.copy()
|
|
566
|
-
|
|
567
|
-
def _get_context(self, query: str) -> str:
|
|
568
|
-
"""Obtiene contexto relevante de archivos procesados"""
|
|
569
687
|
if self.vector_store:
|
|
570
688
|
docs = self.vector_store.similarity_search(query, k=4)
|
|
571
689
|
return "\n".join([doc.page_content for doc in docs])
|
|
@@ -2,16 +2,30 @@ from pydantic import BaseModel
|
|
|
2
2
|
from typing import Dict, Any, Type
|
|
3
3
|
from sonika_langchain_bot.langchain_class import ILanguageModel
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
class ClassificationResponse(BaseModel):
|
|
6
|
+
"""Respuesta de clasificación con tokens utilizados"""
|
|
7
|
+
input_tokens: int
|
|
8
|
+
output_tokens: int
|
|
9
|
+
result: Dict[str, Any]
|
|
10
|
+
|
|
6
11
|
class TextClassifier:
|
|
7
12
|
def __init__(self, validation_class: Type[BaseModel], llm: ILanguageModel):
|
|
8
|
-
self.llm =llm
|
|
13
|
+
self.llm = llm
|
|
9
14
|
self.validation_class = validation_class
|
|
10
|
-
#
|
|
11
|
-
self.
|
|
15
|
+
# Guardamos ambas versiones del modelo
|
|
16
|
+
self.original_model = self.llm.model # Sin structured output
|
|
17
|
+
self.structured_model = self.llm.model.with_structured_output(validation_class)
|
|
12
18
|
|
|
13
|
-
def classify(self, text: str) ->
|
|
14
|
-
|
|
19
|
+
def classify(self, text: str) -> ClassificationResponse:
|
|
20
|
+
"""
|
|
21
|
+
Clasifica el texto según la clase de validación.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
text: Texto a clasificar
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
ClassificationResponse: Objeto con result, input_tokens y output_tokens
|
|
28
|
+
"""
|
|
15
29
|
prompt = f"""
|
|
16
30
|
Classify the following text based on the properties defined in the validation class.
|
|
17
31
|
|
|
@@ -19,12 +33,34 @@ class TextClassifier:
|
|
|
19
33
|
|
|
20
34
|
Only extract the properties mentioned in the validation class.
|
|
21
35
|
"""
|
|
22
|
-
response = self.llm.invoke(prompt=prompt)
|
|
23
36
|
|
|
24
|
-
#
|
|
37
|
+
# Primero invocamos el modelo ORIGINAL para obtener metadata de tokens
|
|
38
|
+
raw_response = self.original_model.invoke(prompt)
|
|
39
|
+
|
|
40
|
+
# Extraer información de tokens del AIMessage original
|
|
41
|
+
input_tokens = 0
|
|
42
|
+
output_tokens = 0
|
|
43
|
+
|
|
44
|
+
if hasattr(raw_response, 'response_metadata'):
|
|
45
|
+
token_usage = raw_response.response_metadata.get('token_usage', {})
|
|
46
|
+
input_tokens = token_usage.get('prompt_tokens', 0)
|
|
47
|
+
output_tokens = token_usage.get('completion_tokens', 0)
|
|
48
|
+
|
|
49
|
+
# Ahora invocamos con structured output para obtener el objeto parseado
|
|
50
|
+
response = self.structured_model.invoke(prompt)
|
|
51
|
+
|
|
52
|
+
# Validar que el response es de la clase correcta
|
|
25
53
|
if isinstance(response, self.validation_class):
|
|
26
|
-
# Crear el resultado dinámicamente basado en los atributos
|
|
27
|
-
|
|
28
|
-
|
|
54
|
+
# Crear el resultado dinámicamente basado en los atributos
|
|
55
|
+
result_data = {
|
|
56
|
+
field: getattr(response, field)
|
|
57
|
+
for field in self.validation_class.__fields__.keys()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return ClassificationResponse(
|
|
61
|
+
input_tokens=input_tokens,
|
|
62
|
+
output_tokens=output_tokens,
|
|
63
|
+
result=result_data
|
|
64
|
+
)
|
|
29
65
|
else:
|
|
30
|
-
raise ValueError(f"The response is not of type '{self.validation_class.__name__}'")
|
|
66
|
+
raise ValueError(f"The response is not of type '{self.validation_class.__name__}'")
|
|
@@ -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=
|
|
4
|
+
sonika_langchain_bot/langchain_bot_agent.py,sha256=l1Kj4iDnGSH-1NZkFxdlVCKOQxoDMsPjWNdxS3GapcA,29214
|
|
5
5
|
sonika_langchain_bot/langchain_bot_agent_bdi.py,sha256=Ev0hhRQYe6kyGAHiFDhFsfu6QnTwUFaA9oB8DfNV7u4,8613
|
|
6
|
-
sonika_langchain_bot/langchain_clasificator.py,sha256=
|
|
6
|
+
sonika_langchain_bot/langchain_clasificator.py,sha256=h0-H_1bqgA04rF2ZHh5zOg2PinqTuLQMcSK7AGK4uw8,2583
|
|
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.
|
|
12
|
-
sonika_langchain_bot-0.0.
|
|
13
|
-
sonika_langchain_bot-0.0.
|
|
14
|
-
sonika_langchain_bot-0.0.
|
|
15
|
-
sonika_langchain_bot-0.0.
|
|
11
|
+
sonika_langchain_bot-0.0.20.dist-info/licenses/LICENSE,sha256=O8VZ4aU_rUMAArvYTm2bshcZ991huv_tpfB5BKHH9Q8,1064
|
|
12
|
+
sonika_langchain_bot-0.0.20.dist-info/METADATA,sha256=bIPx5NtqGhSIRI1nPP-PZInj8MUhHRheq7VRwQNqOXY,6508
|
|
13
|
+
sonika_langchain_bot-0.0.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
+
sonika_langchain_bot-0.0.20.dist-info/top_level.txt,sha256=UsTTSZFEw2wrPSVh4ufu01e2m_E7O_QVYT_k4zCQaAE,21
|
|
15
|
+
sonika_langchain_bot-0.0.20.dist-info/RECORD,,
|
|
File without changes
|
{sonika_langchain_bot-0.0.15.dist-info → sonika_langchain_bot-0.0.20.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{sonika_langchain_bot-0.0.15.dist-info → sonika_langchain_bot-0.0.20.dist-info}/top_level.txt
RENAMED
|
File without changes
|