ai-parrot 0.8.3__cp312-cp312-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.

Files changed (128) hide show
  1. ai_parrot-0.8.3.dist-info/LICENSE +21 -0
  2. ai_parrot-0.8.3.dist-info/METADATA +306 -0
  3. ai_parrot-0.8.3.dist-info/RECORD +128 -0
  4. ai_parrot-0.8.3.dist-info/WHEEL +6 -0
  5. ai_parrot-0.8.3.dist-info/top_level.txt +2 -0
  6. parrot/__init__.py +30 -0
  7. parrot/bots/__init__.py +5 -0
  8. parrot/bots/abstract.py +1115 -0
  9. parrot/bots/agent.py +492 -0
  10. parrot/bots/basic.py +9 -0
  11. parrot/bots/bose.py +17 -0
  12. parrot/bots/chatbot.py +271 -0
  13. parrot/bots/cody.py +17 -0
  14. parrot/bots/copilot.py +117 -0
  15. parrot/bots/data.py +730 -0
  16. parrot/bots/dataframe.py +103 -0
  17. parrot/bots/hrbot.py +15 -0
  18. parrot/bots/interfaces/__init__.py +1 -0
  19. parrot/bots/interfaces/retrievers.py +12 -0
  20. parrot/bots/notebook.py +619 -0
  21. parrot/bots/odoo.py +17 -0
  22. parrot/bots/prompts/__init__.py +41 -0
  23. parrot/bots/prompts/agents.py +91 -0
  24. parrot/bots/prompts/data.py +214 -0
  25. parrot/bots/retrievals/__init__.py +1 -0
  26. parrot/bots/retrievals/constitutional.py +19 -0
  27. parrot/bots/retrievals/multi.py +122 -0
  28. parrot/bots/retrievals/retrieval.py +610 -0
  29. parrot/bots/tools/__init__.py +7 -0
  30. parrot/bots/tools/eda.py +325 -0
  31. parrot/bots/tools/pdf.py +50 -0
  32. parrot/bots/tools/plot.py +48 -0
  33. parrot/bots/troc.py +16 -0
  34. parrot/conf.py +170 -0
  35. parrot/crew/__init__.py +3 -0
  36. parrot/crew/tools/__init__.py +22 -0
  37. parrot/crew/tools/bing.py +13 -0
  38. parrot/crew/tools/config.py +43 -0
  39. parrot/crew/tools/duckgo.py +62 -0
  40. parrot/crew/tools/file.py +24 -0
  41. parrot/crew/tools/google.py +168 -0
  42. parrot/crew/tools/gtrends.py +16 -0
  43. parrot/crew/tools/md2pdf.py +25 -0
  44. parrot/crew/tools/rag.py +42 -0
  45. parrot/crew/tools/search.py +32 -0
  46. parrot/crew/tools/url.py +21 -0
  47. parrot/exceptions.cpython-312-x86_64-linux-gnu.so +0 -0
  48. parrot/handlers/__init__.py +4 -0
  49. parrot/handlers/agents.py +292 -0
  50. parrot/handlers/bots.py +196 -0
  51. parrot/handlers/chat.py +192 -0
  52. parrot/interfaces/__init__.py +6 -0
  53. parrot/interfaces/database.py +27 -0
  54. parrot/interfaces/http.py +805 -0
  55. parrot/interfaces/images/__init__.py +0 -0
  56. parrot/interfaces/images/plugins/__init__.py +18 -0
  57. parrot/interfaces/images/plugins/abstract.py +58 -0
  58. parrot/interfaces/images/plugins/exif.py +709 -0
  59. parrot/interfaces/images/plugins/hash.py +52 -0
  60. parrot/interfaces/images/plugins/vision.py +104 -0
  61. parrot/interfaces/images/plugins/yolo.py +66 -0
  62. parrot/interfaces/images/plugins/zerodetect.py +197 -0
  63. parrot/llms/__init__.py +1 -0
  64. parrot/llms/abstract.py +69 -0
  65. parrot/llms/anthropic.py +58 -0
  66. parrot/llms/gemma.py +15 -0
  67. parrot/llms/google.py +44 -0
  68. parrot/llms/groq.py +67 -0
  69. parrot/llms/hf.py +45 -0
  70. parrot/llms/openai.py +61 -0
  71. parrot/llms/pipes.py +114 -0
  72. parrot/llms/vertex.py +89 -0
  73. parrot/loaders/__init__.py +9 -0
  74. parrot/loaders/abstract.py +628 -0
  75. parrot/loaders/files/__init__.py +0 -0
  76. parrot/loaders/files/abstract.py +39 -0
  77. parrot/loaders/files/text.py +63 -0
  78. parrot/loaders/txt.py +26 -0
  79. parrot/manager.py +333 -0
  80. parrot/models.py +504 -0
  81. parrot/py.typed +0 -0
  82. parrot/stores/__init__.py +11 -0
  83. parrot/stores/abstract.py +248 -0
  84. parrot/stores/chroma.py +188 -0
  85. parrot/stores/duck.py +162 -0
  86. parrot/stores/embeddings/__init__.py +10 -0
  87. parrot/stores/embeddings/abstract.py +46 -0
  88. parrot/stores/embeddings/base.py +52 -0
  89. parrot/stores/embeddings/bge.py +20 -0
  90. parrot/stores/embeddings/fastembed.py +17 -0
  91. parrot/stores/embeddings/google.py +18 -0
  92. parrot/stores/embeddings/huggingface.py +20 -0
  93. parrot/stores/embeddings/ollama.py +14 -0
  94. parrot/stores/embeddings/openai.py +26 -0
  95. parrot/stores/embeddings/transformers.py +21 -0
  96. parrot/stores/embeddings/vertexai.py +17 -0
  97. parrot/stores/empty.py +10 -0
  98. parrot/stores/faiss.py +160 -0
  99. parrot/stores/milvus.py +397 -0
  100. parrot/stores/postgres.py +653 -0
  101. parrot/stores/qdrant.py +170 -0
  102. parrot/tools/__init__.py +23 -0
  103. parrot/tools/abstract.py +68 -0
  104. parrot/tools/asknews.py +33 -0
  105. parrot/tools/basic.py +51 -0
  106. parrot/tools/bby.py +359 -0
  107. parrot/tools/bing.py +13 -0
  108. parrot/tools/docx.py +343 -0
  109. parrot/tools/duck.py +62 -0
  110. parrot/tools/execute.py +56 -0
  111. parrot/tools/gamma.py +28 -0
  112. parrot/tools/google.py +170 -0
  113. parrot/tools/gvoice.py +301 -0
  114. parrot/tools/results.py +278 -0
  115. parrot/tools/stack.py +27 -0
  116. parrot/tools/weather.py +70 -0
  117. parrot/tools/wikipedia.py +58 -0
  118. parrot/tools/zipcode.py +198 -0
  119. parrot/utils/__init__.py +2 -0
  120. parrot/utils/parsers/__init__.py +5 -0
  121. parrot/utils/parsers/toml.cpython-312-x86_64-linux-gnu.so +0 -0
  122. parrot/utils/toml.py +11 -0
  123. parrot/utils/types.cpython-312-x86_64-linux-gnu.so +0 -0
  124. parrot/utils/uv.py +11 -0
  125. parrot/version.py +10 -0
  126. resources/users/__init__.py +5 -0
  127. resources/users/handlers.py +13 -0
  128. 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
@@ -0,0 +1,9 @@
1
+ from .abstract import AbstractBot
2
+
3
+ class BasicBot(AbstractBot):
4
+ """Represents an BasicBot in Navigator.
5
+
6
+ Each BasicBot has a name, a role, a goal, a backstory,
7
+ and an optional language model (llm).
8
+ """
9
+ pass
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'