sonika-langchain-bot 0.0.4__tar.gz → 0.0.5__tar.gz
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-0.0.4 → sonika_langchain_bot-0.0.5}/PKG-INFO +17 -17
- sonika_langchain_bot-0.0.5/setup.py +43 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot/__init__.py +0 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot/langchain_bdi.py +183 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot/langchain_bot_agent_bdi.py +230 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot/langchain_clasificator.py +41 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot/langchain_class.py +41 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot/langchain_files.py +12 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot/langchain_models.py +46 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot/langchain_tools.py +14 -0
- {sonika_langchain_bot-0.0.4 → sonika_langchain_bot-0.0.5/src}/sonika_langchain_bot.egg-info/PKG-INFO +17 -17
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot.egg-info/SOURCES.txt +17 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot.egg-info/requires.txt +20 -0
- sonika_langchain_bot-0.0.5/src/sonika_langchain_bot.egg-info/top_level.txt +1 -0
- {sonika_langchain_bot-0.0.4 → sonika_langchain_bot-0.0.5}/test/test.py +1 -0
- sonika_langchain_bot-0.0.4/setup.py +0 -42
- sonika_langchain_bot-0.0.4/sonika_langchain_bot.egg-info/SOURCES.txt +0 -9
- sonika_langchain_bot-0.0.4/sonika_langchain_bot.egg-info/requires.txt +0 -20
- sonika_langchain_bot-0.0.4/sonika_langchain_bot.egg-info/top_level.txt +0 -1
- {sonika_langchain_bot-0.0.4 → sonika_langchain_bot-0.0.5}/LICENSE +0 -0
- {sonika_langchain_bot-0.0.4 → sonika_langchain_bot-0.0.5}/README.md +0 -0
- {sonika_langchain_bot-0.0.4 → sonika_langchain_bot-0.0.5}/setup.cfg +0 -0
- {sonika_langchain_bot-0.0.4 → sonika_langchain_bot-0.0.5/src}/sonika_langchain_bot.egg-info/dependency_links.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sonika-langchain-bot
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.5
|
4
4
|
Summary: Agente langchain con LLM
|
5
5
|
Author: Erley Blanco Carvajal
|
6
6
|
License: MIT License
|
@@ -10,22 +10,22 @@ Classifier: Operating System :: OS Independent
|
|
10
10
|
Requires-Python: >=3.6
|
11
11
|
Description-Content-Type: text/markdown
|
12
12
|
License-File: LICENSE
|
13
|
-
Requires-Dist: langchain
|
14
|
-
Requires-Dist: langchain-community
|
15
|
-
Requires-Dist: langchain-core
|
16
|
-
Requires-Dist: langchain-openai
|
17
|
-
Requires-Dist: langgraph
|
18
|
-
Requires-Dist: langgraph-checkpoint
|
19
|
-
Requires-Dist: langgraph-sdk
|
20
|
-
Requires-Dist: dataclasses-json
|
21
|
-
Requires-Dist: python-dateutil
|
22
|
-
Requires-Dist: tiktoken
|
23
|
-
Requires-Dist: pydantic
|
24
|
-
Requires-Dist: faiss-cpu
|
25
|
-
Requires-Dist: pypdf
|
26
|
-
Requires-Dist: python-dotenv
|
27
|
-
Requires-Dist: typing_extensions
|
28
|
-
Requires-Dist: typing-inspect
|
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
29
|
Provides-Extra: dev
|
30
30
|
Requires-Dist: sphinx<9.0.0,>=8.1.3; extra == "dev"
|
31
31
|
Requires-Dist: sphinx-rtd-theme<4.0.0,>=3.0.1; extra == "dev"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
from setuptools import setup, find_packages
|
2
|
+
|
3
|
+
setup(
|
4
|
+
name="sonika-langchain-bot",
|
5
|
+
version="0.0.5",
|
6
|
+
description="Agente langchain con LLM",
|
7
|
+
author="Erley Blanco Carvajal",
|
8
|
+
license="MIT License",
|
9
|
+
long_description=open("README.md", encoding="utf-8").read(),
|
10
|
+
long_description_content_type="text/markdown",
|
11
|
+
packages=find_packages(where="src"), # Encuentra los paquetes dentro de "src"
|
12
|
+
package_dir={"": "src"}, # Indica que los paquetes están en el directorio "src"
|
13
|
+
install_requires=[
|
14
|
+
"langchain>=0.3.0,<1.0.0", # Permite mayor flexibilidad
|
15
|
+
"langchain-community>=0.3.0,<1.0.0",
|
16
|
+
"langchain-core>=0.3.5,<1.0.0",
|
17
|
+
"langchain-openai>=0.2.0,<1.0.0",
|
18
|
+
"langgraph>=0.2.39,<1.0.0",
|
19
|
+
"langgraph-checkpoint>=2.0.2,<3.0.0",
|
20
|
+
"langgraph-sdk>=0.1.34,<2.0.0",
|
21
|
+
"dataclasses-json>=0.6.7,<1.0.0",
|
22
|
+
"python-dateutil>=2.9.0,<3.0.0",
|
23
|
+
"tiktoken>=0.7.0,<1.0.0",
|
24
|
+
"pydantic>=2.9.2,<3.0.0",
|
25
|
+
"faiss-cpu>=1.8.0,<2.0.0",
|
26
|
+
"pypdf>=5.0.0,<6.0.0",
|
27
|
+
"python-dotenv>=1.0.1,<2.0.0",
|
28
|
+
"typing_extensions>=4.12.0,<5.0.0",
|
29
|
+
"typing-inspect>=0.9.0,<1.0.0",
|
30
|
+
],
|
31
|
+
extras_require={
|
32
|
+
"dev": [
|
33
|
+
"sphinx>=8.1.3,<9.0.0",
|
34
|
+
"sphinx-rtd-theme>=3.0.1,<4.0.0",
|
35
|
+
],
|
36
|
+
},
|
37
|
+
classifiers=[
|
38
|
+
"Programming Language :: Python :: 3",
|
39
|
+
"License :: OSI Approved :: MIT License",
|
40
|
+
"Operating System :: OS Independent",
|
41
|
+
],
|
42
|
+
python_requires=">=3.6", # Verifica la versión mínima de Python compatible
|
43
|
+
)
|
File without changes
|
@@ -0,0 +1,183 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from typing import Dict, List, Optional, Set
|
3
|
+
from datetime import datetime
|
4
|
+
import json
|
5
|
+
from enum import Enum
|
6
|
+
from langchain.tools import Tool
|
7
|
+
import inspect
|
8
|
+
|
9
|
+
class BeliefType(Enum):
|
10
|
+
CORE = "core" # Creencias fundamentales sobre sí mismo
|
11
|
+
PERSONALITY = "personality" # Rasgos de personalidad
|
12
|
+
USER = "user" # Creencias sobre el usuario
|
13
|
+
CONTEXT = "context" # Creencias sobre el contexto actual
|
14
|
+
KNOWLEDGE = "knowledge" # Conocimiento general
|
15
|
+
TEMPORAL = "temporal" # Creencias temporales
|
16
|
+
TOOLS = "tools" # Creencias temporales
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class Belief:
|
21
|
+
"""Representa una creencia individual del bot."""
|
22
|
+
content: str
|
23
|
+
type: BeliefType
|
24
|
+
confidence: float = 1.0
|
25
|
+
created_at: datetime = field(default_factory=datetime.now)
|
26
|
+
last_updated: datetime = field(default_factory=datetime.now)
|
27
|
+
source: str = "initial"
|
28
|
+
|
29
|
+
@dataclass
|
30
|
+
class Desire:
|
31
|
+
"""Representa un deseo o objetivo del bot."""
|
32
|
+
description: str
|
33
|
+
priority: int
|
34
|
+
conditions: List[str]
|
35
|
+
achieved: bool = False
|
36
|
+
|
37
|
+
@dataclass
|
38
|
+
class Intention:
|
39
|
+
"""Representa una intención o plan de acción del bot."""
|
40
|
+
description: str
|
41
|
+
steps: List[str]
|
42
|
+
related_desire: str
|
43
|
+
completed: bool = False
|
44
|
+
|
45
|
+
class BotBeliefSystem:
|
46
|
+
"""Sistema principal de creencias del bot."""
|
47
|
+
|
48
|
+
def __init__(self, bot_name: str, bot_version: str, tools: List[Tool], beliefs_init: List[Belief]):
|
49
|
+
self.bot_name = bot_name
|
50
|
+
self.bot_version = bot_version
|
51
|
+
self.beliefs_init = beliefs_init
|
52
|
+
self.beliefs: Dict[BeliefType, Dict[str, Belief]] = {belief_type: {} for belief_type in BeliefType}
|
53
|
+
self.desires: List[Desire] = []
|
54
|
+
self.intentions: List[Intention] = []
|
55
|
+
self.conversation_history: List[Dict] = []
|
56
|
+
self._initialize_core_beliefs()
|
57
|
+
self.add_tools_beliefs(tools)
|
58
|
+
self.set_beliefs_init()
|
59
|
+
|
60
|
+
def set_beliefs_init(self):
|
61
|
+
for belief in self.beliefs_init:
|
62
|
+
self.add_belief(belief)
|
63
|
+
|
64
|
+
def _initialize_core_beliefs(self):
|
65
|
+
"""Initializes the core beliefs of the bot."""
|
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
|
+
# Add more core beliefs as needed
|
78
|
+
]
|
79
|
+
|
80
|
+
for belief in core_beliefs:
|
81
|
+
self.add_belief(belief)
|
82
|
+
|
83
|
+
def add_tools_beliefs(self, tools: List[Tool]):
|
84
|
+
"""Carga y procesa las herramientas disponibles para el bot."""
|
85
|
+
# Instrucciones sobre el uso de herramientas en inglés
|
86
|
+
instrucciones_de_uso = '''\nWhen you want to execute a tool, enclose the command with three asterisks and provide all parameters needed.
|
87
|
+
\nEnsure you gather all relevant information from the conversation to use the parameters.
|
88
|
+
\nIf information is missing, search online.'''
|
89
|
+
|
90
|
+
# Agregar un encabezado para la lista de herramientas
|
91
|
+
instrucciones_de_uso = instrucciones_de_uso + "\nThis is a list of the tools you can execute:"
|
92
|
+
# Almacenar las instrucciones de uso como una creencia
|
93
|
+
self.add_belief(Belief(content=instrucciones_de_uso, type=BeliefType.TOOLS))
|
94
|
+
|
95
|
+
# Procesar cada herramienta y almacenarla como creencia
|
96
|
+
for tool in tools:
|
97
|
+
tool_name = tool.name
|
98
|
+
tool_description = tool.description
|
99
|
+
|
100
|
+
# Crear un texto para la herramienta
|
101
|
+
tool_text = f"Tool Name: {tool_name}\nDescription: {tool_description}\n"
|
102
|
+
run_method = getattr(tool, '_run', None)
|
103
|
+
|
104
|
+
if run_method:
|
105
|
+
params = inspect.signature(run_method)
|
106
|
+
tool_text += f"Parameters: {params}\n"
|
107
|
+
else:
|
108
|
+
tool_text += "No _run method found.\n"
|
109
|
+
|
110
|
+
tool_text += "\n---\n"
|
111
|
+
|
112
|
+
# Almacenar la herramienta como una creencia
|
113
|
+
self.add_belief(Belief(content=tool_text.strip(), type=BeliefType.TOOLS))
|
114
|
+
|
115
|
+
|
116
|
+
def add_belief(self, belief: Belief):
|
117
|
+
"""Agrega o actualiza una creencia."""
|
118
|
+
self.beliefs[belief.type][str(datetime.now())] = belief
|
119
|
+
|
120
|
+
def get_beliefs_by_type(self, belief_type: BeliefType) -> List[Belief]:
|
121
|
+
"""Obtiene todas las creencias de un tipo específico."""
|
122
|
+
return list(self.beliefs[belief_type].values())
|
123
|
+
|
124
|
+
def add_desire(self, desire: Desire):
|
125
|
+
"""Agrega un nuevo deseo."""
|
126
|
+
self.desires.append(desire)
|
127
|
+
self.desires.sort(key=lambda x: x.priority, reverse=True)
|
128
|
+
|
129
|
+
def add_intention(self, intention: Intention):
|
130
|
+
"""Agrega una nueva intención."""
|
131
|
+
self.intentions.append(intention)
|
132
|
+
|
133
|
+
def update_user_belief(self, key: str, value: str):
|
134
|
+
"""Actualiza una creencia sobre el usuario."""
|
135
|
+
belief = Belief(
|
136
|
+
content=f"{key}: {value}",
|
137
|
+
type=BeliefType.USER,
|
138
|
+
confidence=0.8,
|
139
|
+
source="user_interaction"
|
140
|
+
)
|
141
|
+
self.add_belief(belief)
|
142
|
+
|
143
|
+
def get_current_context(self) -> str:
|
144
|
+
"""Obtiene el contexto actual basado en las creencias."""
|
145
|
+
context_beliefs = self.get_beliefs_by_type(BeliefType.CONTEXT)
|
146
|
+
return "\n".join([belief.content for belief in context_beliefs])
|
147
|
+
|
148
|
+
def generate_prompt_context(self) -> str:
|
149
|
+
"""Genera el contexto para el prompt, incluyendo creencias y guías sobre cómo el agente debe utilizar esta información."""
|
150
|
+
context_parts = []
|
151
|
+
|
152
|
+
# Instrucciones base para el agente BDI
|
153
|
+
instructions = [
|
154
|
+
'''''',
|
155
|
+
]
|
156
|
+
context_parts.extend(instructions)
|
157
|
+
|
158
|
+
# Agregar información de creencias núcleo (Core Beliefs)
|
159
|
+
core_beliefs = self.get_beliefs_by_type(BeliefType.CORE)
|
160
|
+
context_parts.append("\nCore Beliefs:")
|
161
|
+
context_parts.extend([f"- {belief.content}" for belief in core_beliefs])
|
162
|
+
|
163
|
+
# Agregar información de personalidad (Personality Traits)
|
164
|
+
personality_beliefs = self.get_beliefs_by_type(BeliefType.PERSONALITY)
|
165
|
+
if personality_beliefs:
|
166
|
+
# Agregar una sección de escala de intensidad y los rasgos de personalidad al contexto
|
167
|
+
context_parts.append("\nPersonality Traits:\n")
|
168
|
+
# Agregar los rasgos de personalidad de la lista
|
169
|
+
context_parts.extend([f"- {belief.content}" for belief in personality_beliefs])
|
170
|
+
|
171
|
+
# Agregar información del usuario (User Information)
|
172
|
+
user_beliefs = self.get_beliefs_by_type(BeliefType.USER)
|
173
|
+
if user_beliefs:
|
174
|
+
context_parts.append("\nUser Information:")
|
175
|
+
context_parts.extend([f"- {belief.content}" for belief in user_beliefs])
|
176
|
+
|
177
|
+
user_beliefs = self.get_beliefs_by_type(BeliefType.TOOLS)
|
178
|
+
if user_beliefs:
|
179
|
+
context_parts.append("\nTools Information:")
|
180
|
+
context_parts.extend([f"- {belief.content}" for belief in user_beliefs])
|
181
|
+
|
182
|
+
# Generación final del prompt
|
183
|
+
return "\n".join(context_parts)
|
@@ -0,0 +1,230 @@
|
|
1
|
+
from typing import List
|
2
|
+
import tiktoken
|
3
|
+
from langchain.memory import ConversationBufferMemory
|
4
|
+
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate, HumanMessagePromptTemplate
|
5
|
+
from langchain.schema import AIMessage, HumanMessage
|
6
|
+
from langchain.text_splitter import CharacterTextSplitter
|
7
|
+
from langchain_community.vectorstores import FAISS
|
8
|
+
from langchain.chains import LLMChain
|
9
|
+
from sonika_langchain_bot.langchain_bdi import Belief, BotBeliefSystem
|
10
|
+
from sonika_langchain_bot.langchain_class import FileProcessorInterface, IEmbeddings, ILanguageModel, Message, ResponseModel
|
11
|
+
from langchain.tools import Tool
|
12
|
+
from langgraph.checkpoint.memory import MemorySaver
|
13
|
+
from langgraph.prebuilt import create_react_agent
|
14
|
+
from langchain_community.tools import BaseTool
|
15
|
+
import inspect
|
16
|
+
import re
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
class LangChainBot:
|
22
|
+
"""
|
23
|
+
Implementación principal del bot conversacional con capacidades de procesamiento de archivos,
|
24
|
+
memoria de conversación y uso de herramientas personalizadas.
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(self, language_model: ILanguageModel, embeddings: IEmbeddings, beliefs: List[Belief], tools: List[BaseTool]):
|
28
|
+
"""
|
29
|
+
Inicializa el bot con el modelo de lenguaje, embeddings y herramientas necesarias.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
language_model (ILanguageModel): Modelo de lenguaje a utilizar
|
33
|
+
embeddings (IEmbeddings): Modelo de embeddings para procesamiento de texto
|
34
|
+
instructions (List[str]): Lista de instrucciones para el bot
|
35
|
+
tools (List[BaseTool]): Lista de herramientas disponibles
|
36
|
+
"""
|
37
|
+
self.language_model = language_model
|
38
|
+
self.embeddings = embeddings
|
39
|
+
self.memory = ConversationBufferMemory(return_messages=True)
|
40
|
+
self.memory_agent = MemorySaver()
|
41
|
+
self.vector_store = None
|
42
|
+
self.tools = tools
|
43
|
+
self.beliefs = beliefs
|
44
|
+
self.belief_system = BotBeliefSystem('Hal9000', '1.0.0',beliefs_init=beliefs,tools=tools )
|
45
|
+
self.conversation = self.create_conversation_chain()
|
46
|
+
self.agent_executor = self.create_agent_executor()
|
47
|
+
|
48
|
+
def generate_prompt_tools(self, tools: List[BaseTool]):
|
49
|
+
"""
|
50
|
+
Carga y procesa las herramientas disponibles para el bot.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
tools (List[BaseTool]): Lista de herramientas a cargar
|
54
|
+
"""
|
55
|
+
text_tools = ""
|
56
|
+
for tool in tools:
|
57
|
+
tool_name = tool.name
|
58
|
+
tool_description = tool.description
|
59
|
+
text_tools += f"Tool Name: {tool_name}\nDescription: {tool_description}\n"
|
60
|
+
run_method = getattr(tool, '_run', None)
|
61
|
+
|
62
|
+
if run_method:
|
63
|
+
params = inspect.signature(run_method)
|
64
|
+
text_tools += f"Parameters: {params}\n"
|
65
|
+
else:
|
66
|
+
text_tools += "No _run method found.\n"
|
67
|
+
text_tools += "\n---\n"
|
68
|
+
|
69
|
+
return text_tools
|
70
|
+
|
71
|
+
def create_conversation_chain(self):
|
72
|
+
"""
|
73
|
+
Crea la cadena de conversación con el prompt template y la memoria.
|
74
|
+
Ahora incluye el contexto del sistema de creencias.
|
75
|
+
"""
|
76
|
+
beliefs_context = self.belief_system.generate_prompt_context()
|
77
|
+
full_system_prompt = f"{beliefs_context}\n\n"
|
78
|
+
|
79
|
+
print(full_system_prompt)
|
80
|
+
|
81
|
+
prompt = ChatPromptTemplate.from_messages([
|
82
|
+
SystemMessagePromptTemplate.from_template(full_system_prompt),
|
83
|
+
MessagesPlaceholder(variable_name="history"),
|
84
|
+
HumanMessagePromptTemplate.from_template("{input}")
|
85
|
+
])
|
86
|
+
return LLMChain(llm=self.language_model.model, prompt=prompt, memory=self.memory)
|
87
|
+
|
88
|
+
def create_agent_executor(self):
|
89
|
+
"""
|
90
|
+
Crea el ejecutor del agente con las herramientas configuradas.
|
91
|
+
|
92
|
+
Returns:
|
93
|
+
Agent: Agente configurado con las herramientas
|
94
|
+
"""
|
95
|
+
return create_react_agent(self.language_model.model, self.tools, checkpointer=self.memory_agent)
|
96
|
+
|
97
|
+
def getInstruccionTool(self, bot_response):
|
98
|
+
"""
|
99
|
+
Extrae las instrucciones para herramientas del texto de respuesta del bot.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
bot_response (str): Respuesta del bot a analizar
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
str: Instrucción extraída o cadena vacía si no se encuentra
|
106
|
+
"""
|
107
|
+
patron = r'\*\*\*(.*?)\*\*\*'
|
108
|
+
coincidencia = re.search(patron, bot_response)
|
109
|
+
if coincidencia:
|
110
|
+
return coincidencia.group(1).strip()
|
111
|
+
else:
|
112
|
+
return ''
|
113
|
+
|
114
|
+
def get_response(self, user_input: str) -> ResponseModel:
|
115
|
+
"""
|
116
|
+
Genera una respuesta para la entrada del usuario, procesando el contexto y ejecutando herramientas si es necesario.
|
117
|
+
|
118
|
+
Args:
|
119
|
+
user_input (str): Entrada del usuario
|
120
|
+
|
121
|
+
Returns:
|
122
|
+
ResponseModel: Modelo de respuesta con tokens y texto
|
123
|
+
"""
|
124
|
+
context = self._get_context(user_input)
|
125
|
+
augmented_input = f"User question: {user_input}"
|
126
|
+
if(context):
|
127
|
+
augmented_input = f"Context from attached files:\n{context}\n\nUser question: {user_input}"
|
128
|
+
|
129
|
+
bot_response = self.conversation.predict(input=augmented_input)
|
130
|
+
instruction_tool = self.getInstruccionTool(bot_response)
|
131
|
+
|
132
|
+
if instruction_tool:
|
133
|
+
messages = [HumanMessage(content=instruction_tool)]
|
134
|
+
thread_id = "abc123"
|
135
|
+
config = {"configurable": {"thread_id": thread_id}}
|
136
|
+
|
137
|
+
result_stream = self.agent_executor.stream(
|
138
|
+
{"messages": messages}, config
|
139
|
+
)
|
140
|
+
|
141
|
+
tool_response = ""
|
142
|
+
agent_response = ""
|
143
|
+
|
144
|
+
for response in result_stream:
|
145
|
+
if 'tools' in response:
|
146
|
+
for message in response['tools']['messages']:
|
147
|
+
tool_response = message.content
|
148
|
+
if 'agent' in response:
|
149
|
+
for message in response['agent']['messages']:
|
150
|
+
agent_response = message.content
|
151
|
+
|
152
|
+
bot_response = agent_response if agent_response else tool_response
|
153
|
+
|
154
|
+
user_tokens = self.language_model.count_tokens(augmented_input)
|
155
|
+
bot_tokens = self.language_model.count_tokens(bot_response)
|
156
|
+
|
157
|
+
self.save_messages(user_input,bot_response)
|
158
|
+
|
159
|
+
return ResponseModel(user_tokens=user_tokens, bot_tokens=bot_tokens, response=bot_response)
|
160
|
+
|
161
|
+
def _get_context(self, query: str) -> str:
|
162
|
+
"""
|
163
|
+
Obtiene el contexto relevante para una consulta del almacén de vectores.
|
164
|
+
|
165
|
+
Args:
|
166
|
+
query (str): Consulta para buscar contexto
|
167
|
+
|
168
|
+
Returns:
|
169
|
+
str: Contexto encontrado o cadena vacía
|
170
|
+
"""
|
171
|
+
if self.vector_store:
|
172
|
+
docs = self.vector_store.similarity_search(query)
|
173
|
+
return "\n".join([doc.page_content for doc in docs])
|
174
|
+
return ""
|
175
|
+
|
176
|
+
def clear_memory(self):
|
177
|
+
"""
|
178
|
+
Limpia la memoria de conversación y el almacén de vectores.
|
179
|
+
"""
|
180
|
+
self.memory.clear()
|
181
|
+
self.vector_store = None
|
182
|
+
|
183
|
+
def load_conversation_history(self, messages: list[Message]):
|
184
|
+
"""
|
185
|
+
Carga el historial de conversación previo usando la estructura de mensajes simplificada.
|
186
|
+
|
187
|
+
Args:
|
188
|
+
messages: Lista de objetos Message que representan cada mensaje.
|
189
|
+
"""
|
190
|
+
for message in messages:
|
191
|
+
if message.is_bot:
|
192
|
+
self.memory.chat_memory.add_message(AIMessage(content=message.content))
|
193
|
+
else:
|
194
|
+
self.memory.chat_memory.add_message(HumanMessage(content=message.content))
|
195
|
+
|
196
|
+
def save_messages(self, user_message: str, bot_response: str):
|
197
|
+
"""
|
198
|
+
Guarda los mensajes en la memoria de conversación.
|
199
|
+
|
200
|
+
Args:
|
201
|
+
user_message (str): Mensaje del usuario
|
202
|
+
bot_response (str): Respuesta del bot
|
203
|
+
"""
|
204
|
+
self.memory.chat_memory.add_message(HumanMessage(content=user_message))
|
205
|
+
self.memory.chat_memory.add_message(AIMessage(content=bot_response))
|
206
|
+
|
207
|
+
def process_file(self, file: FileProcessorInterface):
|
208
|
+
"""
|
209
|
+
Procesa un archivo y lo añade al almacén de vectores.
|
210
|
+
|
211
|
+
Args:
|
212
|
+
file (FileProcessorInterface): Archivo a procesar
|
213
|
+
"""
|
214
|
+
document = file.getText()
|
215
|
+
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
|
216
|
+
texts = text_splitter.split_documents(document)
|
217
|
+
|
218
|
+
if self.vector_store is None:
|
219
|
+
self.vector_store = FAISS.from_texts([doc.page_content for doc in texts], self.embeddings)
|
220
|
+
else:
|
221
|
+
self.vector_store.add_texts([doc.page_content for doc in texts])
|
222
|
+
|
223
|
+
def get_total_tokens(self):
|
224
|
+
"""
|
225
|
+
Obtiene el total de tokens utilizados en la conversación.
|
226
|
+
|
227
|
+
Returns:
|
228
|
+
int: Total de tokens
|
229
|
+
"""
|
230
|
+
return self.language_model.count_tokens(self.memory.chat_memory.get_messages())
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from pydantic import BaseModel
|
2
|
+
from langchain_openai import ChatOpenAI
|
3
|
+
from langchain_core.messages import HumanMessage
|
4
|
+
from typing import Dict, Any, Type
|
5
|
+
from sonika_langchain_bot.langchain_class import ILanguageModel
|
6
|
+
|
7
|
+
|
8
|
+
class OpenAIModel(ILanguageModel):
|
9
|
+
def __init__(self, api_key: str, model_name: str = "gpt-4o-mini", temperature: float = 0.7, validation_class: Type[BaseModel] = None,):
|
10
|
+
self.model = ChatOpenAI(api_key=api_key, temperature=temperature, model=model_name).with_structured_output(validation_class)
|
11
|
+
|
12
|
+
def get_response(self, prompt: str) -> str:
|
13
|
+
message = HumanMessage(content=prompt)
|
14
|
+
# Invocar el modelo con el mensaje creado
|
15
|
+
response = self.model.invoke([message])
|
16
|
+
return response
|
17
|
+
|
18
|
+
# Clase para realizar la clasificación de texto
|
19
|
+
class TextClassifier:
|
20
|
+
def __init__(self, api_key: str, validation_class: Type[BaseModel], llm: ILanguageModel, temperature: float = 0):
|
21
|
+
self.llm =llm
|
22
|
+
self.validation_class = validation_class
|
23
|
+
|
24
|
+
def classify(self, text: str) -> Dict[str, Any]:
|
25
|
+
# Crear el template del prompt
|
26
|
+
prompt = f"""
|
27
|
+
Classify the following text based on the properties defined in the validation class.
|
28
|
+
|
29
|
+
Text: {text}
|
30
|
+
|
31
|
+
Only extract the properties mentioned in the validation class.
|
32
|
+
"""
|
33
|
+
response = self.llm.get_response(prompt=prompt)
|
34
|
+
|
35
|
+
# Asegurarse de que el `response` es de la clase de validación proporcionada
|
36
|
+
if isinstance(response, self.validation_class):
|
37
|
+
# Crear el resultado dinámicamente basado en los atributos de la clase de validación
|
38
|
+
result = {field: getattr(response, field) for field in self.validation_class.__fields__.keys()}
|
39
|
+
return result
|
40
|
+
else:
|
41
|
+
raise ValueError(f"The response is not of type '{self.validation_class.__name__}'")
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
|
5
|
+
class ResponseModel():
|
6
|
+
def __init__(self, user_tokens=None, bot_tokens=None, response = None):
|
7
|
+
self.user_tokens = user_tokens
|
8
|
+
self.bot_tokens = bot_tokens
|
9
|
+
self.response = response
|
10
|
+
|
11
|
+
# Definir la interfaz para procesar archivos
|
12
|
+
class FileProcessorInterface(ABC):
|
13
|
+
@abstractmethod
|
14
|
+
def getText(self):
|
15
|
+
pass
|
16
|
+
|
17
|
+
class ILanguageModel(ABC):
|
18
|
+
@abstractmethod
|
19
|
+
def get_response(self, prompt: str) -> str:
|
20
|
+
pass
|
21
|
+
|
22
|
+
class IEmbeddings(ABC):
|
23
|
+
@abstractmethod
|
24
|
+
def embed_documents(self, documents: List[str]):
|
25
|
+
pass
|
26
|
+
|
27
|
+
@abstractmethod
|
28
|
+
def embed_query(self, query: str):
|
29
|
+
pass
|
30
|
+
|
31
|
+
class Message:
|
32
|
+
"""
|
33
|
+
Clase para representar un mensaje con un indicador de si es del bot y su contenido.
|
34
|
+
"""
|
35
|
+
def __init__(self, is_bot: bool, content: str):
|
36
|
+
self.is_bot = is_bot
|
37
|
+
self.content = content
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from langchain_community.document_loaders import PyPDFLoader
|
2
|
+
from sonika_langchain_bot.langchain_class import FileProcessorInterface
|
3
|
+
|
4
|
+
|
5
|
+
class PDFProcessor(FileProcessorInterface):
|
6
|
+
def __init__(self, file_path: str):
|
7
|
+
self.file_path = file_path
|
8
|
+
|
9
|
+
def getText(self):
|
10
|
+
loader = PyPDFLoader(self.file_path)
|
11
|
+
documents = loader.load()
|
12
|
+
return documents
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from langchain_openai import ChatOpenAI
|
2
|
+
import tiktoken
|
3
|
+
from sonika_langchain_bot.langchain_class import ILanguageModel
|
4
|
+
|
5
|
+
|
6
|
+
class OpenAILanguageModel(ILanguageModel):
|
7
|
+
"""
|
8
|
+
Clase que implementa la interfaz ILanguageModel para interactuar con los modelos de lenguaje de OpenAI.
|
9
|
+
Proporciona funcionalidades para generar respuestas y contar tokens.
|
10
|
+
"""
|
11
|
+
|
12
|
+
def __init__(self, api_key: str, model_name: str = "gpt-4o-mini", temperature: float = 0.7):
|
13
|
+
"""
|
14
|
+
Inicializa el modelo de lenguaje de OpenAI.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
api_key (str): Clave API de OpenAI
|
18
|
+
model_name (str): Nombre del modelo a utilizar
|
19
|
+
temperature (float): Temperatura para la generación de respuestas
|
20
|
+
"""
|
21
|
+
self.model = ChatOpenAI(temperature=temperature, model_name=model_name, api_key=api_key)
|
22
|
+
self.tokenizer = tiktoken.encoding_for_model(model_name)
|
23
|
+
|
24
|
+
def get_response(self, prompt: str) -> str:
|
25
|
+
"""
|
26
|
+
Genera una respuesta basada en el prompt proporcionado.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
prompt (str): Texto de entrada para generar la respuesta
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
str: Respuesta generada por el modelo
|
33
|
+
"""
|
34
|
+
return self.model.predict(prompt)
|
35
|
+
|
36
|
+
def count_tokens(self, text: str) -> int:
|
37
|
+
"""
|
38
|
+
Cuenta el número de tokens en un texto dado.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
text (str): Texto para contar tokens
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
int: Número de tokens en el texto
|
45
|
+
"""
|
46
|
+
return len(self.tokenizer.encode(text))
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from langchain_community.tools import BaseTool
|
2
|
+
# Crear una clase que herede de BaseTool
|
3
|
+
from pydantic import BaseModel
|
4
|
+
|
5
|
+
class EmailTool(BaseTool, BaseModel):
|
6
|
+
name: str = "EmailTool"
|
7
|
+
description: str = "Esta herramienta envía correos electrónicos."
|
8
|
+
|
9
|
+
def _run(self, to_email: str, subject: str, message: str) -> str:
|
10
|
+
|
11
|
+
if True:
|
12
|
+
return "Correo enviado con éxito."
|
13
|
+
else:
|
14
|
+
return "No se pudo enviar el correo."
|
{sonika_langchain_bot-0.0.4 → sonika_langchain_bot-0.0.5/src}/sonika_langchain_bot.egg-info/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sonika-langchain-bot
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.5
|
4
4
|
Summary: Agente langchain con LLM
|
5
5
|
Author: Erley Blanco Carvajal
|
6
6
|
License: MIT License
|
@@ -10,22 +10,22 @@ Classifier: Operating System :: OS Independent
|
|
10
10
|
Requires-Python: >=3.6
|
11
11
|
Description-Content-Type: text/markdown
|
12
12
|
License-File: LICENSE
|
13
|
-
Requires-Dist: langchain
|
14
|
-
Requires-Dist: langchain-community
|
15
|
-
Requires-Dist: langchain-core
|
16
|
-
Requires-Dist: langchain-openai
|
17
|
-
Requires-Dist: langgraph
|
18
|
-
Requires-Dist: langgraph-checkpoint
|
19
|
-
Requires-Dist: langgraph-sdk
|
20
|
-
Requires-Dist: dataclasses-json
|
21
|
-
Requires-Dist: python-dateutil
|
22
|
-
Requires-Dist: tiktoken
|
23
|
-
Requires-Dist: pydantic
|
24
|
-
Requires-Dist: faiss-cpu
|
25
|
-
Requires-Dist: pypdf
|
26
|
-
Requires-Dist: python-dotenv
|
27
|
-
Requires-Dist: typing_extensions
|
28
|
-
Requires-Dist: typing-inspect
|
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
29
|
Provides-Extra: dev
|
30
30
|
Requires-Dist: sphinx<9.0.0,>=8.1.3; extra == "dev"
|
31
31
|
Requires-Dist: sphinx-rtd-theme<4.0.0,>=3.0.1; extra == "dev"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
LICENSE
|
2
|
+
README.md
|
3
|
+
setup.py
|
4
|
+
src/sonika_langchain_bot/__init__.py
|
5
|
+
src/sonika_langchain_bot/langchain_bdi.py
|
6
|
+
src/sonika_langchain_bot/langchain_bot_agent_bdi.py
|
7
|
+
src/sonika_langchain_bot/langchain_clasificator.py
|
8
|
+
src/sonika_langchain_bot/langchain_class.py
|
9
|
+
src/sonika_langchain_bot/langchain_files.py
|
10
|
+
src/sonika_langchain_bot/langchain_models.py
|
11
|
+
src/sonika_langchain_bot/langchain_tools.py
|
12
|
+
src/sonika_langchain_bot.egg-info/PKG-INFO
|
13
|
+
src/sonika_langchain_bot.egg-info/SOURCES.txt
|
14
|
+
src/sonika_langchain_bot.egg-info/dependency_links.txt
|
15
|
+
src/sonika_langchain_bot.egg-info/requires.txt
|
16
|
+
src/sonika_langchain_bot.egg-info/top_level.txt
|
17
|
+
test/test.py
|
@@ -0,0 +1,20 @@
|
|
1
|
+
langchain<1.0.0,>=0.3.0
|
2
|
+
langchain-community<1.0.0,>=0.3.0
|
3
|
+
langchain-core<1.0.0,>=0.3.5
|
4
|
+
langchain-openai<1.0.0,>=0.2.0
|
5
|
+
langgraph<1.0.0,>=0.2.39
|
6
|
+
langgraph-checkpoint<3.0.0,>=2.0.2
|
7
|
+
langgraph-sdk<2.0.0,>=0.1.34
|
8
|
+
dataclasses-json<1.0.0,>=0.6.7
|
9
|
+
python-dateutil<3.0.0,>=2.9.0
|
10
|
+
tiktoken<1.0.0,>=0.7.0
|
11
|
+
pydantic<3.0.0,>=2.9.2
|
12
|
+
faiss-cpu<2.0.0,>=1.8.0
|
13
|
+
pypdf<6.0.0,>=5.0.0
|
14
|
+
python-dotenv<2.0.0,>=1.0.1
|
15
|
+
typing_extensions<5.0.0,>=4.12.0
|
16
|
+
typing-inspect<1.0.0,>=0.9.0
|
17
|
+
|
18
|
+
[dev]
|
19
|
+
sphinx<9.0.0,>=8.1.3
|
20
|
+
sphinx-rtd-theme<4.0.0,>=3.0.1
|
@@ -0,0 +1 @@
|
|
1
|
+
sonika_langchain_bot
|
@@ -10,6 +10,7 @@ from sonika_langchain_bot.langchain_class import ResponseModel
|
|
10
10
|
from sonika_langchain_bot.langchain_models import OpenAILanguageModel
|
11
11
|
from langchain_community.tools.tavily_search import TavilySearchResults
|
12
12
|
from pydantic import BaseModel, Field
|
13
|
+
from sonika_langchain_bot.langchain_bot import OpenAIModel
|
13
14
|
|
14
15
|
|
15
16
|
env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
|
@@ -1,42 +0,0 @@
|
|
1
|
-
from setuptools import setup, find_packages
|
2
|
-
|
3
|
-
setup(
|
4
|
-
name="sonika-langchain-bot",
|
5
|
-
version="0.0.4",
|
6
|
-
description="Agente langchain con LLM",
|
7
|
-
author="Erley Blanco Carvajal",
|
8
|
-
license="MIT License",
|
9
|
-
long_description=open("README.md").read(),
|
10
|
-
long_description_content_type="text/markdown",
|
11
|
-
packages=find_packages(), # Encuentra automáticamente todos los paquetes
|
12
|
-
install_requires=[
|
13
|
-
"langchain==0.3.0",
|
14
|
-
"langchain-community==0.3.0",
|
15
|
-
"langchain-core==0.3.5",
|
16
|
-
"langchain-openai==0.2.0",
|
17
|
-
"langgraph==0.2.39",
|
18
|
-
"langgraph-checkpoint==2.0.2",
|
19
|
-
"langgraph-sdk==0.1.34",
|
20
|
-
"dataclasses-json==0.6.7",
|
21
|
-
"python-dateutil==2.9.0.post0",
|
22
|
-
"tiktoken==0.7.0",
|
23
|
-
"pydantic==2.9.2",
|
24
|
-
"faiss-cpu==1.8.0.post1",
|
25
|
-
"pypdf==5.0.0",
|
26
|
-
"python-dotenv==1.0.1",
|
27
|
-
"typing_extensions==4.12.0",
|
28
|
-
"typing-inspect==0.9.0",
|
29
|
-
],
|
30
|
-
extras_require={
|
31
|
-
"dev": [
|
32
|
-
"sphinx>=8.1.3,<9.0.0",
|
33
|
-
"sphinx-rtd-theme>=3.0.1,<4.0.0",
|
34
|
-
],
|
35
|
-
},
|
36
|
-
classifiers=[
|
37
|
-
"Programming Language :: Python :: 3",
|
38
|
-
"License :: OSI Approved :: MIT License",
|
39
|
-
"Operating System :: OS Independent",
|
40
|
-
],
|
41
|
-
python_requires=">=3.6", # Cambia según la versión mínima de Python que soportes
|
42
|
-
)
|
@@ -1,9 +0,0 @@
|
|
1
|
-
LICENSE
|
2
|
-
README.md
|
3
|
-
setup.py
|
4
|
-
sonika_langchain_bot.egg-info/PKG-INFO
|
5
|
-
sonika_langchain_bot.egg-info/SOURCES.txt
|
6
|
-
sonika_langchain_bot.egg-info/dependency_links.txt
|
7
|
-
sonika_langchain_bot.egg-info/requires.txt
|
8
|
-
sonika_langchain_bot.egg-info/top_level.txt
|
9
|
-
test/test.py
|
@@ -1,20 +0,0 @@
|
|
1
|
-
langchain==0.3.0
|
2
|
-
langchain-community==0.3.0
|
3
|
-
langchain-core==0.3.5
|
4
|
-
langchain-openai==0.2.0
|
5
|
-
langgraph==0.2.39
|
6
|
-
langgraph-checkpoint==2.0.2
|
7
|
-
langgraph-sdk==0.1.34
|
8
|
-
dataclasses-json==0.6.7
|
9
|
-
python-dateutil==2.9.0.post0
|
10
|
-
tiktoken==0.7.0
|
11
|
-
pydantic==2.9.2
|
12
|
-
faiss-cpu==1.8.0.post1
|
13
|
-
pypdf==5.0.0
|
14
|
-
python-dotenv==1.0.1
|
15
|
-
typing_extensions==4.12.0
|
16
|
-
typing-inspect==0.9.0
|
17
|
-
|
18
|
-
[dev]
|
19
|
-
sphinx<9.0.0,>=8.1.3
|
20
|
-
sphinx-rtd-theme<4.0.0,>=3.0.1
|
@@ -1 +0,0 @@
|
|
1
|
-
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|