ai-parrot 0.8.3__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.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 ai-parrot might be problematic. Click here for more details.
- ai_parrot-0.8.3.dist-info/LICENSE +21 -0
- ai_parrot-0.8.3.dist-info/METADATA +306 -0
- ai_parrot-0.8.3.dist-info/RECORD +128 -0
- ai_parrot-0.8.3.dist-info/WHEEL +6 -0
- ai_parrot-0.8.3.dist-info/top_level.txt +2 -0
- parrot/__init__.py +30 -0
- parrot/bots/__init__.py +5 -0
- parrot/bots/abstract.py +1115 -0
- parrot/bots/agent.py +492 -0
- parrot/bots/basic.py +9 -0
- parrot/bots/bose.py +17 -0
- parrot/bots/chatbot.py +271 -0
- parrot/bots/cody.py +17 -0
- parrot/bots/copilot.py +117 -0
- parrot/bots/data.py +730 -0
- parrot/bots/dataframe.py +103 -0
- parrot/bots/hrbot.py +15 -0
- parrot/bots/interfaces/__init__.py +1 -0
- parrot/bots/interfaces/retrievers.py +12 -0
- parrot/bots/notebook.py +619 -0
- parrot/bots/odoo.py +17 -0
- parrot/bots/prompts/__init__.py +41 -0
- parrot/bots/prompts/agents.py +91 -0
- parrot/bots/prompts/data.py +214 -0
- parrot/bots/retrievals/__init__.py +1 -0
- parrot/bots/retrievals/constitutional.py +19 -0
- parrot/bots/retrievals/multi.py +122 -0
- parrot/bots/retrievals/retrieval.py +610 -0
- parrot/bots/tools/__init__.py +7 -0
- parrot/bots/tools/eda.py +325 -0
- parrot/bots/tools/pdf.py +50 -0
- parrot/bots/tools/plot.py +48 -0
- parrot/bots/troc.py +16 -0
- parrot/conf.py +170 -0
- parrot/crew/__init__.py +3 -0
- parrot/crew/tools/__init__.py +22 -0
- parrot/crew/tools/bing.py +13 -0
- parrot/crew/tools/config.py +43 -0
- parrot/crew/tools/duckgo.py +62 -0
- parrot/crew/tools/file.py +24 -0
- parrot/crew/tools/google.py +168 -0
- parrot/crew/tools/gtrends.py +16 -0
- parrot/crew/tools/md2pdf.py +25 -0
- parrot/crew/tools/rag.py +42 -0
- parrot/crew/tools/search.py +32 -0
- parrot/crew/tools/url.py +21 -0
- parrot/exceptions.cpython-311-x86_64-linux-gnu.so +0 -0
- parrot/handlers/__init__.py +4 -0
- parrot/handlers/agents.py +292 -0
- parrot/handlers/bots.py +196 -0
- parrot/handlers/chat.py +192 -0
- parrot/interfaces/__init__.py +6 -0
- parrot/interfaces/database.py +27 -0
- parrot/interfaces/http.py +805 -0
- parrot/interfaces/images/__init__.py +0 -0
- parrot/interfaces/images/plugins/__init__.py +18 -0
- parrot/interfaces/images/plugins/abstract.py +58 -0
- parrot/interfaces/images/plugins/exif.py +709 -0
- parrot/interfaces/images/plugins/hash.py +52 -0
- parrot/interfaces/images/plugins/vision.py +104 -0
- parrot/interfaces/images/plugins/yolo.py +66 -0
- parrot/interfaces/images/plugins/zerodetect.py +197 -0
- parrot/llms/__init__.py +1 -0
- parrot/llms/abstract.py +69 -0
- parrot/llms/anthropic.py +58 -0
- parrot/llms/gemma.py +15 -0
- parrot/llms/google.py +44 -0
- parrot/llms/groq.py +67 -0
- parrot/llms/hf.py +45 -0
- parrot/llms/openai.py +61 -0
- parrot/llms/pipes.py +114 -0
- parrot/llms/vertex.py +89 -0
- parrot/loaders/__init__.py +9 -0
- parrot/loaders/abstract.py +628 -0
- parrot/loaders/files/__init__.py +0 -0
- parrot/loaders/files/abstract.py +39 -0
- parrot/loaders/files/text.py +63 -0
- parrot/loaders/txt.py +26 -0
- parrot/manager.py +333 -0
- parrot/models.py +504 -0
- parrot/py.typed +0 -0
- parrot/stores/__init__.py +11 -0
- parrot/stores/abstract.py +248 -0
- parrot/stores/chroma.py +188 -0
- parrot/stores/duck.py +162 -0
- parrot/stores/embeddings/__init__.py +10 -0
- parrot/stores/embeddings/abstract.py +46 -0
- parrot/stores/embeddings/base.py +52 -0
- parrot/stores/embeddings/bge.py +20 -0
- parrot/stores/embeddings/fastembed.py +17 -0
- parrot/stores/embeddings/google.py +18 -0
- parrot/stores/embeddings/huggingface.py +20 -0
- parrot/stores/embeddings/ollama.py +14 -0
- parrot/stores/embeddings/openai.py +26 -0
- parrot/stores/embeddings/transformers.py +21 -0
- parrot/stores/embeddings/vertexai.py +17 -0
- parrot/stores/empty.py +10 -0
- parrot/stores/faiss.py +160 -0
- parrot/stores/milvus.py +397 -0
- parrot/stores/postgres.py +653 -0
- parrot/stores/qdrant.py +170 -0
- parrot/tools/__init__.py +23 -0
- parrot/tools/abstract.py +68 -0
- parrot/tools/asknews.py +33 -0
- parrot/tools/basic.py +51 -0
- parrot/tools/bby.py +359 -0
- parrot/tools/bing.py +13 -0
- parrot/tools/docx.py +343 -0
- parrot/tools/duck.py +62 -0
- parrot/tools/execute.py +56 -0
- parrot/tools/gamma.py +28 -0
- parrot/tools/google.py +170 -0
- parrot/tools/gvoice.py +301 -0
- parrot/tools/results.py +278 -0
- parrot/tools/stack.py +27 -0
- parrot/tools/weather.py +70 -0
- parrot/tools/wikipedia.py +58 -0
- parrot/tools/zipcode.py +198 -0
- parrot/utils/__init__.py +2 -0
- parrot/utils/parsers/__init__.py +5 -0
- parrot/utils/parsers/toml.cpython-311-x86_64-linux-gnu.so +0 -0
- parrot/utils/toml.py +11 -0
- parrot/utils/types.cpython-311-x86_64-linux-gnu.so +0 -0
- parrot/utils/uv.py +11 -0
- parrot/version.py +10 -0
- resources/users/__init__.py +5 -0
- resources/users/handlers.py +13 -0
- resources/users/models.py +205 -0
parrot/bots/agent.py
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Dict, List, Union, Any
|
|
3
|
+
import os
|
|
4
|
+
from string import Template
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from aiohttp import web
|
|
7
|
+
from langchain_core.prompts import (
|
|
8
|
+
ChatPromptTemplate
|
|
9
|
+
)
|
|
10
|
+
from langchain_core.retrievers import BaseRetriever
|
|
11
|
+
from langchain import hub
|
|
12
|
+
from langchain.callbacks.base import BaseCallbackHandler
|
|
13
|
+
from langchain.agents import (
|
|
14
|
+
create_react_agent,
|
|
15
|
+
create_openai_functions_agent,
|
|
16
|
+
create_openai_tools_agent,
|
|
17
|
+
create_tool_calling_agent
|
|
18
|
+
)
|
|
19
|
+
from langchain.agents.agent import (
|
|
20
|
+
AgentExecutor,
|
|
21
|
+
RunnableAgent,
|
|
22
|
+
RunnableMultiActionAgent,
|
|
23
|
+
)
|
|
24
|
+
from langchain.agents.agent_toolkits import create_retriever_tool
|
|
25
|
+
from langchain.prompts import (
|
|
26
|
+
ChatPromptTemplate,
|
|
27
|
+
MessagesPlaceholder,
|
|
28
|
+
SystemMessagePromptTemplate,
|
|
29
|
+
HumanMessagePromptTemplate,
|
|
30
|
+
)
|
|
31
|
+
from langchain_community.utilities import SQLDatabase
|
|
32
|
+
from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit
|
|
33
|
+
from langchain_community.agent_toolkits.sql.base import create_sql_agent
|
|
34
|
+
from langchain_community.agent_toolkits.json.base import create_json_agent
|
|
35
|
+
from langchain_community.tools.json.tool import JsonSpec
|
|
36
|
+
from langchain_community.agent_toolkits.json.toolkit import JsonToolkit
|
|
37
|
+
from langchain_community.utilities import TextRequestsWrapper
|
|
38
|
+
# for exponential backoff
|
|
39
|
+
from tenacity import (
|
|
40
|
+
retry,
|
|
41
|
+
stop_after_attempt,
|
|
42
|
+
wait_random_exponential,
|
|
43
|
+
) # for exponential backoff
|
|
44
|
+
from datamodel.typedefs import SafeDict
|
|
45
|
+
from datamodel.parsers.json import json_decoder # noqa pylint: disable=E0611
|
|
46
|
+
from navconfig.logging import logging
|
|
47
|
+
from .abstract import AbstractBot
|
|
48
|
+
from ..models import AgentResponse
|
|
49
|
+
from ..tools import AbstractTool, SearchTool, MathTool, DuckDuckGoSearchTool
|
|
50
|
+
from ..tools.results import ResultStoreTool, GetResultTool, ListResultsTool
|
|
51
|
+
from ..tools.gvoice import GoogleVoiceTool
|
|
52
|
+
from .prompts import AGENT_PROMPT, AGENT_PROMPT_SUFFIX, FORMAT_INSTRUCTIONS
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
os.environ["GRPC_ENABLE_FORK_SUPPORT"] = "0"
|
|
56
|
+
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # Hide TensorFlow logs if present
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class BasicAgent(AbstractBot):
|
|
60
|
+
"""Represents an Agent in Navigator.
|
|
61
|
+
|
|
62
|
+
Agents are chatbots that can access to Tools and execute commands.
|
|
63
|
+
Each Agent has a name, a role, a goal, a backstory,
|
|
64
|
+
and an optional language model (llm).
|
|
65
|
+
|
|
66
|
+
These agents are designed to interact with structured and unstructured data sources.
|
|
67
|
+
"""
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
name: str = 'Agent',
|
|
71
|
+
agent_type: str = None,
|
|
72
|
+
use_llm: str = 'vertexai',
|
|
73
|
+
llm: str = None,
|
|
74
|
+
tools: List[AbstractTool] = None,
|
|
75
|
+
system_prompt: str = None,
|
|
76
|
+
human_prompt: str = None,
|
|
77
|
+
prompt_template: str = None,
|
|
78
|
+
**kwargs
|
|
79
|
+
):
|
|
80
|
+
super().__init__(
|
|
81
|
+
name=name,
|
|
82
|
+
llm=llm,
|
|
83
|
+
use_llm=use_llm,
|
|
84
|
+
system_prompt=system_prompt,
|
|
85
|
+
human_prompt=human_prompt,
|
|
86
|
+
**kwargs
|
|
87
|
+
)
|
|
88
|
+
self.agent = None
|
|
89
|
+
self.agent_type = agent_type or 'tool-calling'
|
|
90
|
+
self._use_chat: bool = True # For Agents, we use chat models
|
|
91
|
+
self._agent = None # Agent Executor
|
|
92
|
+
self.prompt_template = prompt_template or AGENT_PROMPT
|
|
93
|
+
self.tools = tools or self.default_tools(tools)
|
|
94
|
+
if system_prompt:
|
|
95
|
+
self.prompt_template = self.prompt_template.format_map(
|
|
96
|
+
SafeDict(
|
|
97
|
+
system_prompt_base=system_prompt
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
self.prompt_template = self.prompt_template.format_map(
|
|
102
|
+
SafeDict(
|
|
103
|
+
system_prompt_base="""
|
|
104
|
+
Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.
|
|
105
|
+
"""
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
self.prompt = self.define_prompt(self.prompt_template)
|
|
109
|
+
## Logging:
|
|
110
|
+
self.logger = logging.getLogger(
|
|
111
|
+
f'{self.name}.Agent'
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def default_tools(self, tools: list = None) -> List[AbstractTool]:
|
|
115
|
+
ctools = [
|
|
116
|
+
DuckDuckGoSearchTool(),
|
|
117
|
+
# SearchTool(),
|
|
118
|
+
MathTool(),
|
|
119
|
+
GoogleVoiceTool(name="generate_podcast_style_audio_file"),
|
|
120
|
+
]
|
|
121
|
+
# result_store_tool = ResultStoreTool()
|
|
122
|
+
# get_result_tool = GetResultTool()
|
|
123
|
+
# list_results_tool = ListResultsTool()
|
|
124
|
+
# adding result management:
|
|
125
|
+
# ctools.extend([result_store_tool, get_result_tool, list_results_tool])
|
|
126
|
+
if tools:
|
|
127
|
+
ctools.extend(tools)
|
|
128
|
+
return ctools
|
|
129
|
+
|
|
130
|
+
# Add helper methods to directly access the stored results
|
|
131
|
+
def get_stored_result(self, key: str) -> Any:
|
|
132
|
+
"""Retrieve a stored result directly."""
|
|
133
|
+
return ResultStoreTool.get_result(key)
|
|
134
|
+
|
|
135
|
+
def list_stored_results(self) -> Dict[str, Dict[str, Any]]:
|
|
136
|
+
"""List all stored results directly."""
|
|
137
|
+
return ResultStoreTool.list_results()
|
|
138
|
+
|
|
139
|
+
def clear_stored_results(self) -> None:
|
|
140
|
+
"""Clear all stored results."""
|
|
141
|
+
ResultStoreTool.clear_results()
|
|
142
|
+
|
|
143
|
+
def define_prompt(self, prompt, **kwargs):
|
|
144
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
145
|
+
list_of_tools = ""
|
|
146
|
+
for tool in self.tools:
|
|
147
|
+
name = tool.name
|
|
148
|
+
description = tool.description # noqa pylint: disable=E1101
|
|
149
|
+
list_of_tools += f'- {name}: {description}\n'
|
|
150
|
+
list_of_tools += "\n"
|
|
151
|
+
tools_names = [tool.name for tool in self.tools]
|
|
152
|
+
tmpl = Template(prompt)
|
|
153
|
+
final_prompt = tmpl.safe_substitute(
|
|
154
|
+
today_date=now,
|
|
155
|
+
list_of_tools=list_of_tools,
|
|
156
|
+
backstory=self.backstory,
|
|
157
|
+
rationale=self.rationale,
|
|
158
|
+
format_instructions=FORMAT_INSTRUCTIONS.format(
|
|
159
|
+
tool_names=", ".join(tools_names)),
|
|
160
|
+
)
|
|
161
|
+
# Define a structured system message
|
|
162
|
+
system_message = f"""
|
|
163
|
+
Today is {now}. If an event is expected to have occurred before this date,
|
|
164
|
+
assume that results exist and verify using a web search tool.
|
|
165
|
+
|
|
166
|
+
If you call a tool and receive a valid answer, finalize your response immediately.
|
|
167
|
+
Do NOT repeat the same tool call multiple times for the same question.
|
|
168
|
+
"""
|
|
169
|
+
final_prompt += AGENT_PROMPT_SUFFIX
|
|
170
|
+
chat_prompt = ChatPromptTemplate.from_messages([
|
|
171
|
+
SystemMessagePromptTemplate.from_template(system_message),
|
|
172
|
+
ChatPromptTemplate.from_template(final_prompt)
|
|
173
|
+
])
|
|
174
|
+
return chat_prompt.partial(
|
|
175
|
+
tools=self.tools,
|
|
176
|
+
tool_names=", ".join([tool.name for tool in self.tools]),
|
|
177
|
+
name=self.name,
|
|
178
|
+
**kwargs
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def get_retriever_tool(
|
|
182
|
+
self,
|
|
183
|
+
retriever: BaseRetriever,
|
|
184
|
+
name: str = 'vector_retriever',
|
|
185
|
+
description: str = 'Search for information about a topic in a Vector Retriever.',
|
|
186
|
+
):
|
|
187
|
+
return create_retriever_tool(
|
|
188
|
+
name=name,
|
|
189
|
+
description=description,
|
|
190
|
+
retriever=retriever,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def runnable_json_agent(self, json_file: Union[str, Path], **kwargs):
|
|
194
|
+
"""
|
|
195
|
+
Creates a JSON Agent using `create_json_agent`.
|
|
196
|
+
|
|
197
|
+
This agent is designed to work with structured JSON input and output.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
RunnableMultiActionAgent: A JSON-based agent.
|
|
201
|
+
|
|
202
|
+
✅ Use Case: Best when dealing with structured JSON data and needing a predictable schema.
|
|
203
|
+
"""
|
|
204
|
+
data = None
|
|
205
|
+
if isinstance(json_file, str):
|
|
206
|
+
data = json_file
|
|
207
|
+
elif isinstance(json_file, Path):
|
|
208
|
+
data = json_file.read_text()
|
|
209
|
+
data = json_decoder(data)
|
|
210
|
+
json_spec = JsonSpec(dict_= data, max_value_length=4000)
|
|
211
|
+
json_toolkit = JsonToolkit(spec=json_spec)
|
|
212
|
+
agent = create_json_agent(
|
|
213
|
+
llm=self._llm,
|
|
214
|
+
toolkit=json_toolkit,
|
|
215
|
+
verbose=True,
|
|
216
|
+
prompt=self.prompt,
|
|
217
|
+
)
|
|
218
|
+
return self.prompt | self._llm | agent
|
|
219
|
+
|
|
220
|
+
def runnable_agent(self, **kwargs):
|
|
221
|
+
"""
|
|
222
|
+
Creates a ZeroShot ReAct Agent.
|
|
223
|
+
|
|
224
|
+
This agent uses reasoning and tool execution iteratively to generate responses.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
RunnableMultiActionAgent: A ReAct-based agent.
|
|
228
|
+
|
|
229
|
+
✅ Use Case: Best for decision-making and reasoning tasks where the agent must break problems down into multiple steps.
|
|
230
|
+
|
|
231
|
+
"""
|
|
232
|
+
return RunnableMultiActionAgent(
|
|
233
|
+
runnable = create_react_agent(
|
|
234
|
+
self._llm,
|
|
235
|
+
self.tools,
|
|
236
|
+
prompt=self.prompt,
|
|
237
|
+
), # type: ignore
|
|
238
|
+
input_keys_arg=["input"],
|
|
239
|
+
return_keys_arg=["output"],
|
|
240
|
+
**kwargs
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def function_calling_agent(self, **kwargs):
|
|
244
|
+
"""
|
|
245
|
+
Creates a Function Calling Agent.
|
|
246
|
+
|
|
247
|
+
This agent uses reasoning and tool execution iteratively to generate responses.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
RunnableMultiActionAgent: A ReAct-based agent.
|
|
251
|
+
|
|
252
|
+
✅ Use Case: Best for decision-making and reasoning tasks where the agent must break problems down into multiple steps.
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
return RunnableMultiActionAgent(
|
|
256
|
+
runnable = create_tool_calling_agent(
|
|
257
|
+
self._llm,
|
|
258
|
+
self.tools,
|
|
259
|
+
prompt=self.prompt,
|
|
260
|
+
), # type: ignore
|
|
261
|
+
input_keys_arg=["input"],
|
|
262
|
+
return_keys_arg=["output"],
|
|
263
|
+
**kwargs
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
def openai_agent(self, **kwargs):
|
|
267
|
+
"""
|
|
268
|
+
Creates OpenAI-like task executor Agent.
|
|
269
|
+
|
|
270
|
+
This agent uses reasoning and tool execution iteratively to generate responses.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
RunnableMultiActionAgent: A ReAct-based agent.
|
|
274
|
+
|
|
275
|
+
✅ Use Case: Best for decision-making and reasoning tasks where the agent must break problems down into multiple steps.
|
|
276
|
+
|
|
277
|
+
"""
|
|
278
|
+
return RunnableMultiActionAgent(
|
|
279
|
+
runnable = create_openai_functions_agent(
|
|
280
|
+
self._llm,
|
|
281
|
+
self.tools,
|
|
282
|
+
prompt=self.prompt
|
|
283
|
+
), # type: ignore
|
|
284
|
+
input_keys_arg=["input"],
|
|
285
|
+
return_keys_arg=["output"],
|
|
286
|
+
**kwargs
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
def sql_agent(self, dsn: str, **kwargs):
|
|
290
|
+
"""
|
|
291
|
+
Creates a SQL Agent.
|
|
292
|
+
|
|
293
|
+
This agent is designed to work with SQL queries and databases.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
AgentExecutor: A SQL-based AgentExecutor.
|
|
297
|
+
|
|
298
|
+
✅ Use Case: Best for querying databases and working with SQL data.
|
|
299
|
+
"""
|
|
300
|
+
db = SQLDatabase.from_uri(dsn)
|
|
301
|
+
toolkit = SQLDatabaseToolkit(db=db, llm=self._llm)
|
|
302
|
+
# prompt_template = hub.pull("langchain-ai/sql-agent-system-prompt")
|
|
303
|
+
return create_sql_agent(
|
|
304
|
+
llm=self._llm,
|
|
305
|
+
toolkit=toolkit,
|
|
306
|
+
db=db,
|
|
307
|
+
agent_type= "openai-tools",
|
|
308
|
+
extra_tools=self.tools,
|
|
309
|
+
max_iterations=5,
|
|
310
|
+
handle_parsing_errors=True,
|
|
311
|
+
verbose=True,
|
|
312
|
+
prompt=self.prompt,
|
|
313
|
+
agent_executor_kwargs = {"return_intermediate_steps": False}
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
def get_executor(
|
|
317
|
+
self,
|
|
318
|
+
agent: RunnableAgent,
|
|
319
|
+
tools: list,
|
|
320
|
+
verbose: bool = True,
|
|
321
|
+
**kwargs
|
|
322
|
+
):
|
|
323
|
+
"""Create a new AgentExecutor.
|
|
324
|
+
"""
|
|
325
|
+
return AgentExecutor(
|
|
326
|
+
agent=agent,
|
|
327
|
+
tools=tools,
|
|
328
|
+
verbose=verbose,
|
|
329
|
+
return_intermediate_steps=False,
|
|
330
|
+
max_iterations=5,
|
|
331
|
+
max_execution_time=360,
|
|
332
|
+
handle_parsing_errors=True,
|
|
333
|
+
# memory=self.memory,
|
|
334
|
+
**kwargs,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
def get_agent(self):
|
|
338
|
+
return self.get_executor(self.agent, self.tools)
|
|
339
|
+
|
|
340
|
+
async def configure(self, app=None) -> None:
|
|
341
|
+
"""Basic Configuration of Agent.
|
|
342
|
+
"""
|
|
343
|
+
if app:
|
|
344
|
+
if isinstance(app, web.Application):
|
|
345
|
+
self.app = app # register the app into the Extension
|
|
346
|
+
else:
|
|
347
|
+
self.app = app.get_app() # Nav Application
|
|
348
|
+
# adding this configured chatbot to app:
|
|
349
|
+
if self.app:
|
|
350
|
+
self.app[f"{self.name.lower()}_bot"] = self
|
|
351
|
+
# Configure LLM:
|
|
352
|
+
self.configure_llm(use_chat=True)
|
|
353
|
+
# And define Prompt:
|
|
354
|
+
self._define_prompt()
|
|
355
|
+
# Configure VectorStore if enabled:
|
|
356
|
+
if self._use_vector:
|
|
357
|
+
self.configure_store()
|
|
358
|
+
# Conversation History:
|
|
359
|
+
self.memory = self.get_memory()
|
|
360
|
+
# 1. Initialize the Agent (as the base for RunnableMultiActionAgent)
|
|
361
|
+
if self.agent_type == 'zero_shot':
|
|
362
|
+
self.agent = self.runnable_agent()
|
|
363
|
+
elif self.agent_type in ('function_calling', 'tool-calling'):
|
|
364
|
+
self.agent = self.function_calling_agent()
|
|
365
|
+
elif self.agent_type == 'openai':
|
|
366
|
+
self.agent = self.openai_agent()
|
|
367
|
+
# elif self.agent_type == 'json':
|
|
368
|
+
# self.agent = self.runnable_json_agent()
|
|
369
|
+
# elif self.agent_type == 'sql':
|
|
370
|
+
# self.agent = self.sql_agent()
|
|
371
|
+
else:
|
|
372
|
+
self.agent = self.runnable_agent()
|
|
373
|
+
# self.agent = self.openai_agent()
|
|
374
|
+
# 2. Create Agent Executor - This is where we typically run the agent.
|
|
375
|
+
# While RunnableMultiActionAgent itself might be "runnable",
|
|
376
|
+
# we often use AgentExecutor to manage the agent's execution loop.
|
|
377
|
+
self._agent = self.get_executor(self.agent, self.tools)
|
|
378
|
+
|
|
379
|
+
async def question(
|
|
380
|
+
self,
|
|
381
|
+
question: str = None,
|
|
382
|
+
**kwargs
|
|
383
|
+
):
|
|
384
|
+
"""question.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
question (str): The question to ask the chatbot.
|
|
388
|
+
memory (Any): The memory to use.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
Any: The response from the Agent.
|
|
392
|
+
|
|
393
|
+
"""
|
|
394
|
+
# TODO: adding the vector-search to the agent
|
|
395
|
+
input_question = {
|
|
396
|
+
"input": question
|
|
397
|
+
}
|
|
398
|
+
result = self._agent.invoke(input_question)
|
|
399
|
+
try:
|
|
400
|
+
response = AgentResponse(question=question, **result)
|
|
401
|
+
# response.response = self.as_markdown(
|
|
402
|
+
# response
|
|
403
|
+
# )
|
|
404
|
+
return response
|
|
405
|
+
except Exception as e:
|
|
406
|
+
self.logger.exception(
|
|
407
|
+
f"Error on response: {e}"
|
|
408
|
+
)
|
|
409
|
+
raise
|
|
410
|
+
|
|
411
|
+
@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(3))
|
|
412
|
+
async def invoke(self, query: str):
|
|
413
|
+
"""invoke.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
query (str): The query to ask the chatbot.
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
str: The response from the chatbot.
|
|
420
|
+
|
|
421
|
+
"""
|
|
422
|
+
input_question = {
|
|
423
|
+
"input": query
|
|
424
|
+
}
|
|
425
|
+
result = await self._agent.ainvoke(input_question)
|
|
426
|
+
try:
|
|
427
|
+
response = AgentResponse(question=query, **result)
|
|
428
|
+
try:
|
|
429
|
+
return self.as_markdown(
|
|
430
|
+
response
|
|
431
|
+
), response
|
|
432
|
+
except Exception as exc:
|
|
433
|
+
self.logger.exception(
|
|
434
|
+
f"Error on response: {exc}"
|
|
435
|
+
)
|
|
436
|
+
return result.get('output', None), None
|
|
437
|
+
except Exception as e:
|
|
438
|
+
return result, e
|
|
439
|
+
|
|
440
|
+
async def __aenter__(self):
|
|
441
|
+
if not self._agent:
|
|
442
|
+
await self.configure()
|
|
443
|
+
return self
|
|
444
|
+
|
|
445
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
446
|
+
self._agent = None
|
|
447
|
+
|
|
448
|
+
def sanitize_prompt_text(self, text: str) -> str:
|
|
449
|
+
"""
|
|
450
|
+
Sanitize text for use in prompts to avoid parsing issues.
|
|
451
|
+
|
|
452
|
+
This function:
|
|
453
|
+
1. Escapes any triple backticks that might interfere with code blocks
|
|
454
|
+
2. Normalizes newlines
|
|
455
|
+
3. Removes any potentially problematic characters
|
|
456
|
+
4. Ensures proper escaping of markdown formatting
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
text (str): The text to sanitize
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
str: The sanitized text
|
|
463
|
+
"""
|
|
464
|
+
if not text:
|
|
465
|
+
return ""
|
|
466
|
+
|
|
467
|
+
# Convert None to empty string
|
|
468
|
+
if text is None:
|
|
469
|
+
return ""
|
|
470
|
+
|
|
471
|
+
# Normalize newlines
|
|
472
|
+
text = text.replace('\r\n', '\n').replace('\r', '\n')
|
|
473
|
+
|
|
474
|
+
# Escape triple backticks - this is crucial as they can interfere with code blocks
|
|
475
|
+
# Replace triple backticks with escaped version
|
|
476
|
+
text = text.replace("```", "\\`\\`\\`")
|
|
477
|
+
|
|
478
|
+
# Handle markdown code blocks more safely
|
|
479
|
+
# If we detect a python code block, ensure it's properly formatted
|
|
480
|
+
pattern = r'\\`\\`\\`python\n(.*?)\\`\\`\\`'
|
|
481
|
+
import re
|
|
482
|
+
text = re.sub(
|
|
483
|
+
pattern,
|
|
484
|
+
r'The following is Python code:\n\1\nEnd of Python code.',
|
|
485
|
+
text,
|
|
486
|
+
flags=re.DOTALL
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
# Remove any control characters that might cause issues
|
|
490
|
+
text = ''.join(ch for ch in text if ord(ch) >= 32 or ch in '\n\t')
|
|
491
|
+
|
|
492
|
+
return text
|
parrot/bots/basic.py
ADDED
parrot/bots/bose.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .chatbot import Chatbot
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BoseBot(Chatbot):
|
|
5
|
+
"""Represents an agent in Navigator.
|
|
6
|
+
https://eminent-kiwi-trusty.ngrok-free.app/api/bose/messages
|
|
7
|
+
Each agent has a name, a role, a goal, a backstory,
|
|
8
|
+
and an optional language model (llm).
|
|
9
|
+
"""
|
|
10
|
+
name: str = 'BoseBot'
|
|
11
|
+
company: str = 'T-ROC Global'
|
|
12
|
+
company_website: str = 'https://www.trocglobal.com'
|
|
13
|
+
contact_information = 'communications@trocglobal.com'
|
|
14
|
+
contact_form = 'https://bose.trocdigital.io/bose/bose_ticketing_system'
|
|
15
|
+
role: str = 'Bose Sound Systems Expert Technician and Consultant.'
|
|
16
|
+
goal = 'Bring useful information to Bose Technicians and Consultants.'
|
|
17
|
+
specialty_area = 'Bose endcap displays that are used to showcase Bose products'
|