sonika-langchain-bot 0.0.9__py3-none-any.whl → 0.0.11__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.
- sonika_langchain_bot/langchain_bdi.py +3 -10
- sonika_langchain_bot/langchain_bot_agent.py +281 -0
- sonika_langchain_bot/langchain_bot_agent_bdi.py +3 -0
- sonika_langchain_bot-0.0.11.dist-info/METADATA +215 -0
- sonika_langchain_bot-0.0.11.dist-info/RECORD +14 -0
- {sonika_langchain_bot-0.0.9.dist-info → sonika_langchain_bot-0.0.11.dist-info}/WHEEL +1 -1
- sonika_langchain_bot-0.0.9.dist-info/METADATA +0 -145
- sonika_langchain_bot-0.0.9.dist-info/RECORD +0 -13
- {sonika_langchain_bot-0.0.9.dist-info → sonika_langchain_bot-0.0.11.dist-info/licenses}/LICENSE +0 -0
- {sonika_langchain_bot-0.0.9.dist-info → sonika_langchain_bot-0.0.11.dist-info}/top_level.txt +0 -0
@@ -64,16 +64,6 @@ class BotBeliefSystem:
|
|
64
64
|
def _initialize_core_beliefs(self):
|
65
65
|
"""Initializes the core beliefs of the bot."""
|
66
66
|
core_beliefs = [
|
67
|
-
Belief(
|
68
|
-
f"My name is {self.bot_name}.",
|
69
|
-
BeliefType.CORE,
|
70
|
-
confidence=1.0
|
71
|
-
),
|
72
|
-
Belief(
|
73
|
-
f"I operate on Telegram, and my responses should be as concise and accurate as possible.",
|
74
|
-
BeliefType.CORE,
|
75
|
-
confidence=1.0
|
76
|
-
),
|
77
67
|
# Add more core beliefs as needed
|
78
68
|
]
|
79
69
|
|
@@ -81,6 +71,9 @@ class BotBeliefSystem:
|
|
81
71
|
self.add_belief(belief)
|
82
72
|
|
83
73
|
def add_tools_beliefs(self, tools: List[Tool]):
|
74
|
+
|
75
|
+
if len(tools) == 0:
|
76
|
+
return
|
84
77
|
"""Carga y procesa las herramientas disponibles para el bot."""
|
85
78
|
# Instrucciones sobre el uso de herramientas en inglés
|
86
79
|
instrucciones_de_uso = '''\nWhen you want to execute a tool, enclose the command with three asterisks and provide all parameters needed.
|
@@ -0,0 +1,281 @@
|
|
1
|
+
from typing import Generator, List
|
2
|
+
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate, HumanMessagePromptTemplate
|
3
|
+
from langchain.schema import AIMessage, HumanMessage, BaseMessage
|
4
|
+
from langchain.text_splitter import CharacterTextSplitter
|
5
|
+
from langchain_community.vectorstores import FAISS
|
6
|
+
from sonika_langchain_bot.langchain_class import FileProcessorInterface, IEmbeddings, ILanguageModel, Message, ResponseModel
|
7
|
+
from langgraph.checkpoint.memory import MemorySaver
|
8
|
+
from langgraph.prebuilt import create_react_agent
|
9
|
+
from langchain_community.tools import BaseTool
|
10
|
+
import re
|
11
|
+
|
12
|
+
class LangChainBot:
|
13
|
+
"""
|
14
|
+
Implementación principal del bot conversacional con capacidades de procesamiento de archivos,
|
15
|
+
memoria de conversación y uso de herramientas personalizadas.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(self, language_model: ILanguageModel, embeddings: IEmbeddings, instructions: str, tools: List[BaseTool]):
|
19
|
+
"""
|
20
|
+
Inicializa el bot con el modelo de lenguaje, embeddings y herramientas necesarias.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
language_model (ILanguageModel): Modelo de lenguaje a utilizar
|
24
|
+
embeddings (IEmbeddings): Modelo de embeddings para procesamiento de texto
|
25
|
+
instructions (str): Instrucciones del sistema
|
26
|
+
tools (List[BaseTool]): Lista de herramientas disponibles
|
27
|
+
"""
|
28
|
+
self.language_model = language_model
|
29
|
+
self.embeddings = embeddings
|
30
|
+
# Reemplazamos ConversationBufferMemory con una lista simple de mensajes
|
31
|
+
self.chat_history: List[BaseMessage] = []
|
32
|
+
self.memory_agent = MemorySaver()
|
33
|
+
self.vector_store = None
|
34
|
+
self.tools = tools
|
35
|
+
self.instructions = instructions
|
36
|
+
self.add_tools_to_instructions(tools)
|
37
|
+
self.conversation = self._create_conversation_chain()
|
38
|
+
self.agent_executor = self._create_agent_executor()
|
39
|
+
|
40
|
+
def add_tools_to_instructions(self, tools: List[BaseTool]):
|
41
|
+
"""Agrega información de las herramientas a las instrucciones base del sistema."""
|
42
|
+
if len(tools) == 0:
|
43
|
+
return
|
44
|
+
|
45
|
+
# Instrucciones sobre el uso de herramientas
|
46
|
+
tools_instructions = '''\n\nWhen you want to execute a tool, enclose the command with three asterisks and provide all parameters needed.
|
47
|
+
Ensure you gather all relevant information from the conversation to use the parameters.
|
48
|
+
If information is missing, search online.
|
49
|
+
|
50
|
+
This is a list of the tools you can execute:
|
51
|
+
'''
|
52
|
+
|
53
|
+
# Procesar cada herramienta y agregarla a las instrucciones
|
54
|
+
for tool in tools:
|
55
|
+
tool_name = tool.name
|
56
|
+
tool_description = tool.description
|
57
|
+
|
58
|
+
tools_instructions += f"\nTool Name: {tool_name}\n"
|
59
|
+
tools_instructions += f"Description: {tool_description}\n"
|
60
|
+
|
61
|
+
# Intentar obtener información de parámetros
|
62
|
+
run_method = getattr(tool, '_run', None)
|
63
|
+
if run_method:
|
64
|
+
try:
|
65
|
+
import inspect
|
66
|
+
params = inspect.signature(run_method)
|
67
|
+
tools_instructions += f"Parameters: {params}\n"
|
68
|
+
except:
|
69
|
+
tools_instructions += "Parameters: Not available\n"
|
70
|
+
else:
|
71
|
+
tools_instructions += "Parameters: Not available\n"
|
72
|
+
|
73
|
+
tools_instructions += "---\n"
|
74
|
+
|
75
|
+
# Agregar las instrucciones de herramientas a las instrucciones base
|
76
|
+
self.instructions += tools_instructions
|
77
|
+
|
78
|
+
|
79
|
+
def _create_conversation_chain(self):
|
80
|
+
"""
|
81
|
+
Crea la cadena de conversación con el prompt template y la memoria.
|
82
|
+
"""
|
83
|
+
full_system_prompt = f"{self.instructions}\n\n"
|
84
|
+
|
85
|
+
prompt = ChatPromptTemplate.from_messages([
|
86
|
+
SystemMessagePromptTemplate.from_template(full_system_prompt),
|
87
|
+
MessagesPlaceholder(variable_name="history"),
|
88
|
+
HumanMessagePromptTemplate.from_template("{input}")
|
89
|
+
])
|
90
|
+
|
91
|
+
# Usando RunnableSequence para reemplazar LLMChain
|
92
|
+
return prompt | self.language_model.model
|
93
|
+
|
94
|
+
def _create_agent_executor(self):
|
95
|
+
"""
|
96
|
+
Crea el ejecutor del agente con las herramientas configuradas.
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
Agent: Agente configurado con las herramientas
|
100
|
+
"""
|
101
|
+
return create_react_agent(self.language_model.model, self.tools, checkpointer=self.memory_agent)
|
102
|
+
|
103
|
+
def _getInstruccionTool(self, bot_response):
|
104
|
+
"""
|
105
|
+
Extrae las instrucciones para herramientas del texto de respuesta del bot.
|
106
|
+
|
107
|
+
Args:
|
108
|
+
bot_response (str): Respuesta del bot a analizar
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
str: Instrucción extraída o cadena vacía si no se encuentra
|
112
|
+
"""
|
113
|
+
patron = r'\*\*\*(.*?)\*\*\*'
|
114
|
+
coincidencia = re.search(patron, bot_response)
|
115
|
+
return coincidencia.group(1).strip() if coincidencia else ''
|
116
|
+
|
117
|
+
def get_response(self, user_input: str) -> ResponseModel:
|
118
|
+
"""
|
119
|
+
Genera una respuesta para la entrada del usuario, procesando el contexto y ejecutando herramientas si es necesario.
|
120
|
+
|
121
|
+
Args:
|
122
|
+
user_input (str): Entrada del usuario
|
123
|
+
|
124
|
+
Returns:
|
125
|
+
ResponseModel: Modelo de respuesta con tokens y texto
|
126
|
+
"""
|
127
|
+
context = self._get_context(user_input)
|
128
|
+
augmented_input = f"User question: {user_input}"
|
129
|
+
if context:
|
130
|
+
augmented_input = f"Context from attached files:\n{context}\n\nUser question: {user_input}"
|
131
|
+
|
132
|
+
# Usamos el historial de chat directamente
|
133
|
+
bot_response = self.conversation.invoke({
|
134
|
+
"input": augmented_input,
|
135
|
+
"history": self.chat_history
|
136
|
+
})
|
137
|
+
|
138
|
+
token_usage = bot_response.response_metadata.get('token_usage', {})
|
139
|
+
bot_response_content = bot_response.content
|
140
|
+
|
141
|
+
instruction_tool = self._getInstruccionTool(bot_response_content)
|
142
|
+
|
143
|
+
if instruction_tool:
|
144
|
+
messages = [HumanMessage(content=instruction_tool)]
|
145
|
+
thread_id = "abc123"
|
146
|
+
config = {"configurable": {"thread_id": thread_id}}
|
147
|
+
|
148
|
+
result_stream = self.agent_executor.stream(
|
149
|
+
{"messages": messages}, config
|
150
|
+
)
|
151
|
+
|
152
|
+
tool_response = ""
|
153
|
+
agent_response = ""
|
154
|
+
|
155
|
+
for response in result_stream:
|
156
|
+
if 'tools' in response:
|
157
|
+
for message in response['tools']['messages']:
|
158
|
+
tool_response = message.content
|
159
|
+
if 'agent' in response:
|
160
|
+
for message in response['agent']['messages']:
|
161
|
+
agent_response = message.content
|
162
|
+
|
163
|
+
bot_response_content = agent_response if agent_response else tool_response
|
164
|
+
|
165
|
+
user_tokens = token_usage.get('prompt_tokens', 0)
|
166
|
+
bot_tokens = token_usage.get('completion_tokens', 0)
|
167
|
+
|
168
|
+
self.save_messages(user_input, bot_response_content)
|
169
|
+
|
170
|
+
return ResponseModel(user_tokens=user_tokens, bot_tokens=bot_tokens, response=bot_response_content)
|
171
|
+
|
172
|
+
def get_response_stream(self, user_input: str) -> Generator[str, None, None]:
|
173
|
+
"""
|
174
|
+
Genera una respuesta en streaming para la entrada del usuario, procesando el contexto.
|
175
|
+
|
176
|
+
Args:
|
177
|
+
user_input (str): Entrada del usuario
|
178
|
+
|
179
|
+
Yields:
|
180
|
+
str: Fragmentos de la respuesta generada por el modelo en tiempo real
|
181
|
+
"""
|
182
|
+
context = self._get_context(user_input)
|
183
|
+
augmented_input = f"User question: {user_input}"
|
184
|
+
if context:
|
185
|
+
augmented_input = f"Context from attached files:\n{context}\n\nUser question: {user_input}"
|
186
|
+
|
187
|
+
# Usamos el historial de chat directamente
|
188
|
+
result_stream = self.conversation.stream({
|
189
|
+
"input": augmented_input,
|
190
|
+
"history": self.chat_history
|
191
|
+
})
|
192
|
+
|
193
|
+
full_response = ""
|
194
|
+
for response in result_stream:
|
195
|
+
content = response.content
|
196
|
+
full_response += content
|
197
|
+
yield content
|
198
|
+
|
199
|
+
# Guardamos los mensajes después del streaming
|
200
|
+
self.save_messages(user_input, full_response)
|
201
|
+
|
202
|
+
def _get_context(self, query: str) -> str:
|
203
|
+
"""
|
204
|
+
Obtiene el contexto relevante para una consulta del almacén de vectores.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
query (str): Consulta para buscar contexto
|
208
|
+
|
209
|
+
Returns:
|
210
|
+
str: Contexto encontrado o cadena vacía
|
211
|
+
"""
|
212
|
+
if self.vector_store:
|
213
|
+
docs = self.vector_store.similarity_search(query)
|
214
|
+
return "\n".join([doc.page_content for doc in docs])
|
215
|
+
return ""
|
216
|
+
|
217
|
+
def clear_memory(self):
|
218
|
+
"""
|
219
|
+
Limpia la memoria de conversación y el almacén de vectores.
|
220
|
+
"""
|
221
|
+
self.chat_history.clear()
|
222
|
+
self.vector_store = None
|
223
|
+
|
224
|
+
def load_conversation_history(self, messages: List[Message]):
|
225
|
+
"""
|
226
|
+
Carga el historial de conversación previo usando la estructura de mensajes simplificada.
|
227
|
+
|
228
|
+
Args:
|
229
|
+
messages: Lista de objetos Message que representan cada mensaje.
|
230
|
+
"""
|
231
|
+
self.chat_history.clear()
|
232
|
+
for message in messages:
|
233
|
+
if message.is_bot:
|
234
|
+
self.chat_history.append(AIMessage(content=message.content))
|
235
|
+
else:
|
236
|
+
self.chat_history.append(HumanMessage(content=message.content))
|
237
|
+
|
238
|
+
def save_messages(self, user_message: str, bot_response: str):
|
239
|
+
"""
|
240
|
+
Guarda los mensajes en el historial de conversación.
|
241
|
+
|
242
|
+
Args:
|
243
|
+
user_message (str): Mensaje del usuario
|
244
|
+
bot_response (str): Respuesta del bot
|
245
|
+
"""
|
246
|
+
self.chat_history.append(HumanMessage(content=user_message))
|
247
|
+
self.chat_history.append(AIMessage(content=bot_response))
|
248
|
+
|
249
|
+
def process_file(self, file: FileProcessorInterface):
|
250
|
+
"""
|
251
|
+
Procesa un archivo y lo añade al almacén de vectores.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
file (FileProcessorInterface): Archivo a procesar
|
255
|
+
"""
|
256
|
+
document = file.getText()
|
257
|
+
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
258
|
+
texts = text_splitter.split_documents(document)
|
259
|
+
|
260
|
+
if self.vector_store is None:
|
261
|
+
self.vector_store = FAISS.from_texts([doc.page_content for doc in texts], self.embeddings)
|
262
|
+
else:
|
263
|
+
self.vector_store.add_texts([doc.page_content for doc in texts])
|
264
|
+
|
265
|
+
def get_chat_history(self) -> List[BaseMessage]:
|
266
|
+
"""
|
267
|
+
Obtiene el historial completo de la conversación.
|
268
|
+
|
269
|
+
Returns:
|
270
|
+
List[BaseMessage]: Lista de mensajes de la conversación
|
271
|
+
"""
|
272
|
+
return self.chat_history.copy()
|
273
|
+
|
274
|
+
def set_chat_history(self, history: List[BaseMessage]):
|
275
|
+
"""
|
276
|
+
Establece el historial de conversación.
|
277
|
+
|
278
|
+
Args:
|
279
|
+
history (List[BaseMessage]): Lista de mensajes a establecer
|
280
|
+
"""
|
281
|
+
self.chat_history = history.copy()
|
@@ -46,6 +46,7 @@ class LangChainBot:
|
|
46
46
|
"""
|
47
47
|
beliefs_context = self.belief_system.generate_prompt_context()
|
48
48
|
full_system_prompt = f"{beliefs_context}\n\n"
|
49
|
+
print(full_system_prompt)
|
49
50
|
|
50
51
|
prompt = ChatPromptTemplate.from_messages([
|
51
52
|
SystemMessagePromptTemplate.from_template(full_system_prompt),
|
@@ -94,6 +95,8 @@ class LangChainBot:
|
|
94
95
|
if context:
|
95
96
|
augmented_input = f"Context from attached files:\n{context}\n\nUser question: {user_input}"
|
96
97
|
|
98
|
+
print(augmented_input)
|
99
|
+
|
97
100
|
bot_response = self.conversation.invoke({"input": augmented_input, "history": self.memory.chat_memory.messages})
|
98
101
|
|
99
102
|
token_usage= bot_response.response_metadata['token_usage']
|
@@ -0,0 +1,215 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: sonika-langchain-bot
|
3
|
+
Version: 0.0.11
|
4
|
+
Summary: Agente langchain con LLM
|
5
|
+
Author: Erley Blanco Carvajal
|
6
|
+
License: MIT License
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
9
|
+
Classifier: Operating System :: OS Independent
|
10
|
+
Requires-Python: >=3.6
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
Requires-Dist: langchain==0.3.26
|
14
|
+
Requires-Dist: langchain-community==0.3.26
|
15
|
+
Requires-Dist: langchain-core==0.3.66
|
16
|
+
Requires-Dist: langchain-openai==0.3.24
|
17
|
+
Requires-Dist: langgraph==0.4.8
|
18
|
+
Requires-Dist: langgraph-checkpoint==2.1.0
|
19
|
+
Requires-Dist: langgraph-sdk==0.1.70
|
20
|
+
Requires-Dist: dataclasses-json==0.6.7
|
21
|
+
Requires-Dist: python-dateutil==2.9.0.post0
|
22
|
+
Requires-Dist: pydantic==2.11.7
|
23
|
+
Requires-Dist: faiss-cpu==1.11.0
|
24
|
+
Requires-Dist: pypdf==5.6.1
|
25
|
+
Requires-Dist: python-dotenv==1.0.1
|
26
|
+
Requires-Dist: typing_extensions==4.14.0
|
27
|
+
Requires-Dist: typing-inspect==0.9.0
|
28
|
+
Provides-Extra: dev
|
29
|
+
Requires-Dist: sphinx<9.0.0,>=8.1.3; extra == "dev"
|
30
|
+
Requires-Dist: sphinx-rtd-theme<4.0.0,>=3.0.1; extra == "dev"
|
31
|
+
Dynamic: author
|
32
|
+
Dynamic: classifier
|
33
|
+
Dynamic: description
|
34
|
+
Dynamic: description-content-type
|
35
|
+
Dynamic: license
|
36
|
+
Dynamic: license-file
|
37
|
+
Dynamic: provides-extra
|
38
|
+
Dynamic: requires-dist
|
39
|
+
Dynamic: requires-python
|
40
|
+
Dynamic: summary
|
41
|
+
|
42
|
+
# Sonika LangChain Bot <a href="https://pepy.tech/projects/sonika-langchain-bot"><img src="https://static.pepy.tech/badge/sonika-langchain-bot" alt="PyPI Downloads"></a>
|
43
|
+
|
44
|
+
A Python library that implements a conversational agent using LangChain with tool execution capabilities and text classification.
|
45
|
+
|
46
|
+
## Installation
|
47
|
+
|
48
|
+
```bash
|
49
|
+
pip install sonika-langchain-bot
|
50
|
+
```
|
51
|
+
|
52
|
+
## Prerequisites
|
53
|
+
|
54
|
+
You'll need the following API keys:
|
55
|
+
|
56
|
+
- OpenAI API Key
|
57
|
+
|
58
|
+
Create a `.env` file in the root of your project with the following variables:
|
59
|
+
|
60
|
+
```env
|
61
|
+
OPENAI_API_KEY=your_api_key_here
|
62
|
+
```
|
63
|
+
|
64
|
+
## Key Features
|
65
|
+
|
66
|
+
- Conversational agent with tool execution capabilities
|
67
|
+
- Text classification with structured output
|
68
|
+
- Custom tool integration
|
69
|
+
- Streaming responses
|
70
|
+
- Conversation history management
|
71
|
+
- Flexible instruction-based behavior
|
72
|
+
|
73
|
+
## Basic Usage
|
74
|
+
|
75
|
+
### Agent with Tools Example
|
76
|
+
|
77
|
+
```python
|
78
|
+
import os
|
79
|
+
from dotenv import load_dotenv
|
80
|
+
from langchain_openai import OpenAIEmbeddings
|
81
|
+
from sonika_langchain_bot.langchain_tools import EmailTool
|
82
|
+
from sonika_langchain_bot.langchain_bot_agent import LangChainBot
|
83
|
+
from sonika_langchain_bot.langchain_class import Message, ResponseModel
|
84
|
+
from sonika_langchain_bot.langchain_models import OpenAILanguageModel
|
85
|
+
|
86
|
+
# Load environment variables
|
87
|
+
load_dotenv()
|
88
|
+
|
89
|
+
# Get API key from .env file
|
90
|
+
api_key = os.getenv("OPENAI_API_KEY")
|
91
|
+
|
92
|
+
# Initialize language model and embeddings
|
93
|
+
language_model = OpenAILanguageModel(api_key, model_name='gpt-4o-mini-2024-07-18', temperature=1)
|
94
|
+
embeddings = OpenAIEmbeddings(api_key=api_key)
|
95
|
+
|
96
|
+
# Configure tools
|
97
|
+
tools = [EmailTool()]
|
98
|
+
|
99
|
+
# Create agent instance
|
100
|
+
bot = LangChainBot(language_model, embeddings, instructions="You are an agent", tools=tools)
|
101
|
+
|
102
|
+
# Load conversation history
|
103
|
+
bot.load_conversation_history([Message(content="My name is Erley", is_bot=False)])
|
104
|
+
|
105
|
+
# Get response
|
106
|
+
user_message = 'Send an email with the tool to erley@gmail.com with subject Hello and message Hello Erley'
|
107
|
+
response_model: ResponseModel = bot.get_response(user_message)
|
108
|
+
|
109
|
+
print(response_model)
|
110
|
+
```
|
111
|
+
|
112
|
+
### Streaming Response Example
|
113
|
+
|
114
|
+
```python
|
115
|
+
import os
|
116
|
+
from dotenv import load_dotenv
|
117
|
+
from langchain_openai import OpenAIEmbeddings
|
118
|
+
from sonika_langchain_bot.langchain_bot_agent import LangChainBot
|
119
|
+
from sonika_langchain_bot.langchain_class import Message
|
120
|
+
from sonika_langchain_bot.langchain_models import OpenAILanguageModel
|
121
|
+
|
122
|
+
# Load environment variables
|
123
|
+
load_dotenv()
|
124
|
+
|
125
|
+
# Get API key from .env file
|
126
|
+
api_key = os.getenv("OPENAI_API_KEY")
|
127
|
+
|
128
|
+
# Initialize language model and embeddings
|
129
|
+
language_model = OpenAILanguageModel(api_key, model_name='gpt-4o-mini-2024-07-18', temperature=1)
|
130
|
+
embeddings = OpenAIEmbeddings(api_key=api_key)
|
131
|
+
|
132
|
+
# Create agent instance
|
133
|
+
bot = LangChainBot(language_model, embeddings, instructions="Only answers in english", tools=[])
|
134
|
+
|
135
|
+
# Load conversation history
|
136
|
+
bot.load_conversation_history([Message(content="My name is Erley", is_bot=False)])
|
137
|
+
|
138
|
+
# Get streaming response
|
139
|
+
user_message = 'Hello, what is my name?'
|
140
|
+
for chunk in bot.get_response_stream(user_message):
|
141
|
+
print(chunk)
|
142
|
+
```
|
143
|
+
|
144
|
+
### Text Classification Example
|
145
|
+
|
146
|
+
```python
|
147
|
+
import os
|
148
|
+
from dotenv import load_dotenv
|
149
|
+
from sonika_langchain_bot.langchain_clasificator import TextClassifier
|
150
|
+
from sonika_langchain_bot.langchain_models import OpenAILanguageModel
|
151
|
+
from pydantic import BaseModel, Field
|
152
|
+
|
153
|
+
# Load environment variables
|
154
|
+
load_dotenv()
|
155
|
+
|
156
|
+
# Define classification structure with Pydantic
|
157
|
+
class Classification(BaseModel):
|
158
|
+
intention: str = Field()
|
159
|
+
sentiment: str = Field(..., enum=["happy", "neutral", "sad", "excited"])
|
160
|
+
aggressiveness: int = Field(
|
161
|
+
...,
|
162
|
+
description="describes how aggressive the statement is, the higher the number the more aggressive",
|
163
|
+
enum=[1, 2, 3, 4, 5],
|
164
|
+
)
|
165
|
+
language: str = Field(
|
166
|
+
..., enum=["spanish", "english", "french", "german", "italian"]
|
167
|
+
)
|
168
|
+
|
169
|
+
# Initialize classifier
|
170
|
+
api_key = os.getenv("OPENAI_API_KEY")
|
171
|
+
model = OpenAILanguageModel(api_key=api_key)
|
172
|
+
classifier = TextClassifier(llm=model, validation_class=Classification)
|
173
|
+
|
174
|
+
# Classify text
|
175
|
+
result = classifier.classify("how are you?")
|
176
|
+
print(result)
|
177
|
+
```
|
178
|
+
|
179
|
+
## Available Classes and Components
|
180
|
+
|
181
|
+
### Core Classes
|
182
|
+
|
183
|
+
- **LangChainBot**: Main conversational agent for task execution with tools
|
184
|
+
- **OpenAILanguageModel**: Wrapper for OpenAI language models
|
185
|
+
- **TextClassifier**: Text classification using structured output
|
186
|
+
- **Message**: Message structure for conversation history
|
187
|
+
- **ResponseModel**: Response structure from agent interactions
|
188
|
+
|
189
|
+
### Tools
|
190
|
+
|
191
|
+
- **EmailTool**: Tool for sending emails through the agent
|
192
|
+
|
193
|
+
## Project Structure
|
194
|
+
|
195
|
+
```
|
196
|
+
your_project/
|
197
|
+
├── .env # Environment variables
|
198
|
+
├── src/
|
199
|
+
│ └── sonika_langchain_bot/
|
200
|
+
│ ├── langchain_bot_agent.py
|
201
|
+
│ ├── langchain_clasificator.py
|
202
|
+
│ ├── langchain_class.py
|
203
|
+
│ ├── langchain_models.py
|
204
|
+
│ └── langchain_tools.py
|
205
|
+
└── tests/
|
206
|
+
└── test_bot.py
|
207
|
+
```
|
208
|
+
|
209
|
+
## Contributing
|
210
|
+
|
211
|
+
Contributions are welcome. Please open an issue to discuss major changes you'd like to make.
|
212
|
+
|
213
|
+
## License
|
214
|
+
|
215
|
+
This project is licensed under the MIT License.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
sonika_langchain_bot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
sonika_langchain_bot/langchain_bdi.py,sha256=ithc55azP5XSPb8AGRUrDGYnVI6I4IqpqElLNat4BAQ,7024
|
3
|
+
sonika_langchain_bot/langchain_bot_agent.py,sha256=z7jamAwZqQJEt7jWFZo2nMGWPS4dgTCghpKQdkR3BT4,10890
|
4
|
+
sonika_langchain_bot/langchain_bot_agent_bdi.py,sha256=Ev0hhRQYe6kyGAHiFDhFsfu6QnTwUFaA9oB8DfNV7u4,8613
|
5
|
+
sonika_langchain_bot/langchain_clasificator.py,sha256=GR85ZAliymBSoDa5PXB31BvJkuiokGjS2v3RLdXnzzk,1381
|
6
|
+
sonika_langchain_bot/langchain_class.py,sha256=5anB6v_wCzEoAJRb8fV9lPPS72E7-k51y_aeiip8RAw,1114
|
7
|
+
sonika_langchain_bot/langchain_files.py,sha256=SEyqnJgBc_nbCIG31eypunBbO33T5AHFOhQZcghTks4,381
|
8
|
+
sonika_langchain_bot/langchain_models.py,sha256=vqSSZ48tNofrTMLv1QugDdyey2MuIeSdlLSD37AnzkI,2235
|
9
|
+
sonika_langchain_bot/langchain_tools.py,sha256=y7wLf1DbUua3QIvz938Ek-JIMOuQhrOIptJadW8OIsU,466
|
10
|
+
sonika_langchain_bot-0.0.11.dist-info/licenses/LICENSE,sha256=O8VZ4aU_rUMAArvYTm2bshcZ991huv_tpfB5BKHH9Q8,1064
|
11
|
+
sonika_langchain_bot-0.0.11.dist-info/METADATA,sha256=QBEkIYZbUqtflj8Lx-VBfTAY-3l6u4Mht0Dr1u1K3C8,6368
|
12
|
+
sonika_langchain_bot-0.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
13
|
+
sonika_langchain_bot-0.0.11.dist-info/top_level.txt,sha256=UsTTSZFEw2wrPSVh4ufu01e2m_E7O_QVYT_k4zCQaAE,21
|
14
|
+
sonika_langchain_bot-0.0.11.dist-info/RECORD,,
|
@@ -1,145 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: sonika-langchain-bot
|
3
|
-
Version: 0.0.9
|
4
|
-
Summary: Agente langchain con LLM
|
5
|
-
Author: Erley Blanco Carvajal
|
6
|
-
License: MIT License
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
9
|
-
Classifier: Operating System :: OS Independent
|
10
|
-
Requires-Python: >=3.6
|
11
|
-
Description-Content-Type: text/markdown
|
12
|
-
License-File: LICENSE
|
13
|
-
Requires-Dist: langchain <1.0.0,>=0.3.0
|
14
|
-
Requires-Dist: langchain-community <1.0.0,>=0.3.0
|
15
|
-
Requires-Dist: langchain-core <1.0.0,>=0.3.5
|
16
|
-
Requires-Dist: langchain-openai <1.0.0,>=0.2.0
|
17
|
-
Requires-Dist: langgraph <1.0.0,>=0.2.39
|
18
|
-
Requires-Dist: langgraph-checkpoint <3.0.0,>=2.0.2
|
19
|
-
Requires-Dist: langgraph-sdk <2.0.0,>=0.1.34
|
20
|
-
Requires-Dist: dataclasses-json <1.0.0,>=0.6.7
|
21
|
-
Requires-Dist: python-dateutil <3.0.0,>=2.9.0
|
22
|
-
Requires-Dist: tiktoken <1.0.0,>=0.7.0
|
23
|
-
Requires-Dist: pydantic <3.0.0,>=2.9.2
|
24
|
-
Requires-Dist: faiss-cpu <2.0.0,>=1.8.0
|
25
|
-
Requires-Dist: pypdf <6.0.0,>=5.0.0
|
26
|
-
Requires-Dist: python-dotenv <2.0.0,>=1.0.1
|
27
|
-
Requires-Dist: typing-extensions <5.0.0,>=4.12.0
|
28
|
-
Requires-Dist: typing-inspect <1.0.0,>=0.9.0
|
29
|
-
Provides-Extra: dev
|
30
|
-
Requires-Dist: sphinx <9.0.0,>=8.1.3 ; extra == 'dev'
|
31
|
-
Requires-Dist: sphinx-rtd-theme <4.0.0,>=3.0.1 ; extra == 'dev'
|
32
|
-
|
33
|
-
# Sonika LangChain Bot <a href="https://pepy.tech/projects/sonika-langchain-bot"><img src="https://static.pepy.tech/badge/sonika-langchain-bot" alt="PyPI Downloads"></a>
|
34
|
-
|
35
|
-
|
36
|
-
Una librería Python que implementa un bot conversacional utilizando LangChain con capacidades BDI (Belief-Desire-Intention) y clasificación de texto.
|
37
|
-
|
38
|
-
## Instalación
|
39
|
-
|
40
|
-
```bash
|
41
|
-
pip install sonika-langchain-bot
|
42
|
-
```
|
43
|
-
|
44
|
-
## Requisitos previos
|
45
|
-
|
46
|
-
Necesitarás las siguientes API keys:
|
47
|
-
|
48
|
-
- OpenAI API Key
|
49
|
-
|
50
|
-
Crea un archivo `.env` en la raíz de tu proyecto con las siguientes variables:
|
51
|
-
|
52
|
-
```env
|
53
|
-
OPENAI_API_KEY=tu_api_key_aqui
|
54
|
-
```
|
55
|
-
|
56
|
-
## Características principales
|
57
|
-
|
58
|
-
- Bot conversacional con arquitectura BDI
|
59
|
-
- Clasificación de texto
|
60
|
-
- Ejecución de código personalizado por medio de tools
|
61
|
-
|
62
|
-
## Uso básico
|
63
|
-
|
64
|
-
### Ejemplo de Bot BDI
|
65
|
-
|
66
|
-
```python
|
67
|
-
from sonika_langchain_bot.langchain_bdi import Belief, BeliefType
|
68
|
-
from sonika_langchain_bot.langchain_bot_agent_bdi import LangChainBot
|
69
|
-
from sonika_langchain_bot.langchain_models import OpenAILanguageModel
|
70
|
-
from langchain_openai import OpenAIEmbeddings
|
71
|
-
|
72
|
-
# Inicializar el modelo de lenguaje
|
73
|
-
language_model = OpenAILanguageModel(api_key, model_name='gpt-4o-mini', temperature=1)
|
74
|
-
embeddings = OpenAIEmbeddings(api_key=api_key)
|
75
|
-
|
76
|
-
# Configurar herramientas propias o de terceros
|
77
|
-
search = TavilySearchResults(max_results=2, api_key=api_key_tavily)
|
78
|
-
tools = [search]
|
79
|
-
|
80
|
-
# Configurar creencias
|
81
|
-
beliefs = [
|
82
|
-
Belief(
|
83
|
-
content="Eres un asistente de chat",
|
84
|
-
type=BeliefType.PERSONALITY,
|
85
|
-
confidence=1,
|
86
|
-
source='personality'
|
87
|
-
)
|
88
|
-
]
|
89
|
-
|
90
|
-
# Crear instancia del bot
|
91
|
-
bot = LangChainBot(language_model, embeddings, beliefs=beliefs, tools=tools)
|
92
|
-
|
93
|
-
# Obtener respuesta
|
94
|
-
response = bot.get_response("Hola como te llamas?")
|
95
|
-
|
96
|
-
bot = LangChainBot(language_model, embeddings, beliefs=beliefs, tools=tools)
|
97
|
-
|
98
|
-
user_message = 'Hola como me llamo?'
|
99
|
-
|
100
|
-
#Cargas la conversacion previa con el bot
|
101
|
-
bot.load_conversation_history([Message(content="Mi nombre es Erley", is_bot=False)])
|
102
|
-
# Obtener la respuesta del bot
|
103
|
-
response_model: ResponseModel = bot.get_response(user_message)
|
104
|
-
bot_response = response_model
|
105
|
-
|
106
|
-
print(bot_response)
|
107
|
-
|
108
|
-
#o por streaming
|
109
|
-
for chunk in bot.get_response_stream(user_message):
|
110
|
-
print(chunk)
|
111
|
-
|
112
|
-
```
|
113
|
-
|
114
|
-
### Ejemplo de Clasificación de Texto
|
115
|
-
|
116
|
-
```python
|
117
|
-
from sonika_langchain_bot.langchain_clasificator import TextClassifier
|
118
|
-
from sonika_langchain_bot.langchain_models import OpenAILanguageModel
|
119
|
-
from pydantic import BaseModel, Field
|
120
|
-
|
121
|
-
# Definir estructura de clasificación
|
122
|
-
class Classification(BaseModel):
|
123
|
-
intention: str = Field()
|
124
|
-
sentiment: str = Field(..., enum=["feliz", "neutral", "triste", "excitado"])
|
125
|
-
aggressiveness: int = Field(
|
126
|
-
...,
|
127
|
-
description="describes how aggressive the statement is",
|
128
|
-
enum=[1, 2, 3, 4, 5],
|
129
|
-
)
|
130
|
-
language: str = Field(
|
131
|
-
..., enum=["español", "ingles", "frances", "aleman", "italiano"]
|
132
|
-
)
|
133
|
-
|
134
|
-
# Inicializar clasificador
|
135
|
-
model = OpenAILanguageModel(api_key=api_key)
|
136
|
-
classifier = TextClassifier(llm=model, validation_class=Classification)
|
137
|
-
|
138
|
-
# Clasificar texto
|
139
|
-
result = classifier.classify("Tu texto aquí")
|
140
|
-
```
|
141
|
-
|
142
|
-
## Contribución
|
143
|
-
|
144
|
-
Las contribuciones son bienvenidas. Por favor, abre un issue para discutir los cambios importantes que te gustaría hacer.
|
145
|
-
|
@@ -1,13 +0,0 @@
|
|
1
|
-
sonika_langchain_bot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
sonika_langchain_bot/langchain_bdi.py,sha256=aCo6d2vhn5d1xGmN3Ldpa8DStJUSLuhDPZ3y4iXILIg,7329
|
3
|
-
sonika_langchain_bot/langchain_bot_agent_bdi.py,sha256=vYwhBznypQGn2rmwjHfkPFXzUSkhxslP9pGyymY3Nmo,8547
|
4
|
-
sonika_langchain_bot/langchain_clasificator.py,sha256=GR85ZAliymBSoDa5PXB31BvJkuiokGjS2v3RLdXnzzk,1381
|
5
|
-
sonika_langchain_bot/langchain_class.py,sha256=5anB6v_wCzEoAJRb8fV9lPPS72E7-k51y_aeiip8RAw,1114
|
6
|
-
sonika_langchain_bot/langchain_files.py,sha256=SEyqnJgBc_nbCIG31eypunBbO33T5AHFOhQZcghTks4,381
|
7
|
-
sonika_langchain_bot/langchain_models.py,sha256=vqSSZ48tNofrTMLv1QugDdyey2MuIeSdlLSD37AnzkI,2235
|
8
|
-
sonika_langchain_bot/langchain_tools.py,sha256=y7wLf1DbUua3QIvz938Ek-JIMOuQhrOIptJadW8OIsU,466
|
9
|
-
sonika_langchain_bot-0.0.9.dist-info/LICENSE,sha256=O8VZ4aU_rUMAArvYTm2bshcZ991huv_tpfB5BKHH9Q8,1064
|
10
|
-
sonika_langchain_bot-0.0.9.dist-info/METADATA,sha256=SFXXWxpje_qXsvdLEr_4EgVg_eSOQjB7yH_5ayEIzqM,4454
|
11
|
-
sonika_langchain_bot-0.0.9.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
12
|
-
sonika_langchain_bot-0.0.9.dist-info/top_level.txt,sha256=UsTTSZFEw2wrPSVh4ufu01e2m_E7O_QVYT_k4zCQaAE,21
|
13
|
-
sonika_langchain_bot-0.0.9.dist-info/RECORD,,
|
{sonika_langchain_bot-0.0.9.dist-info → sonika_langchain_bot-0.0.11.dist-info/licenses}/LICENSE
RENAMED
File without changes
|
{sonika_langchain_bot-0.0.9.dist-info → sonika_langchain_bot-0.0.11.dist-info}/top_level.txt
RENAMED
File without changes
|