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.
- 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-312-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-312-x86_64-linux-gnu.so +0 -0
- parrot/utils/toml.py +11 -0
- parrot/utils/types.cpython-312-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
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from aiohttp import web
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from navigator_auth.decorators import (
|
|
5
|
+
is_authenticated,
|
|
6
|
+
user_session
|
|
7
|
+
)
|
|
8
|
+
from navigator.views import BaseView
|
|
9
|
+
from querysource.queries.qs import QS
|
|
10
|
+
from querysource.queries.multi import MultiQS
|
|
11
|
+
from ..bots.abstract import AbstractBot
|
|
12
|
+
from ..llms.vertex import VertexLLM
|
|
13
|
+
from ..bots.data import PandasAgent
|
|
14
|
+
from ..models import AgentModel
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@is_authenticated()
|
|
18
|
+
@user_session()
|
|
19
|
+
class AgentHandler(BaseView):
|
|
20
|
+
"""
|
|
21
|
+
AgentHandler.
|
|
22
|
+
description: Agent Handler for Parrot Application.
|
|
23
|
+
|
|
24
|
+
TODO: Support for per-user session agents.
|
|
25
|
+
- Tool for doing an EDA (exploratory data-analysis) on a dataframe.
|
|
26
|
+
- Tool for doing a data profiling on a dataframe.
|
|
27
|
+
"""
|
|
28
|
+
async def put(self, *args, **kwargs):
|
|
29
|
+
"""
|
|
30
|
+
put.
|
|
31
|
+
description: Put method for AgentHandler
|
|
32
|
+
|
|
33
|
+
Use this method to create a new Agent.
|
|
34
|
+
"""
|
|
35
|
+
app = self.request.app
|
|
36
|
+
_id = self.request.match_info.get('agent_name', None)
|
|
37
|
+
data = await self.request.json()
|
|
38
|
+
name = data.pop('name', None)
|
|
39
|
+
if not name:
|
|
40
|
+
return self.json_response(
|
|
41
|
+
{
|
|
42
|
+
"message": "Agent name not found."
|
|
43
|
+
},
|
|
44
|
+
status=404
|
|
45
|
+
)
|
|
46
|
+
_id = data.pop('chatbot_id', None)
|
|
47
|
+
# To create a new agent, we need:
|
|
48
|
+
# A list of queries (Query slugs) to be converted into dataframes
|
|
49
|
+
query = data.pop('query', None)
|
|
50
|
+
if not query:
|
|
51
|
+
return self.json_response(
|
|
52
|
+
{
|
|
53
|
+
"message": "No query was found."
|
|
54
|
+
},
|
|
55
|
+
status=400
|
|
56
|
+
)
|
|
57
|
+
# A list of tools to be used by the agent
|
|
58
|
+
tools = kwargs.pop('tools', [])
|
|
59
|
+
# a backstory and an optional capabilities for Bot.
|
|
60
|
+
backstory = data.pop('backstory', None)
|
|
61
|
+
capabilities = data.pop('capabilities', None)
|
|
62
|
+
try:
|
|
63
|
+
manager = app['bot_manager']
|
|
64
|
+
except KeyError:
|
|
65
|
+
return self.json_response(
|
|
66
|
+
{
|
|
67
|
+
"message": "Chatbot Manager is not installed."
|
|
68
|
+
},
|
|
69
|
+
status=404
|
|
70
|
+
)
|
|
71
|
+
if agent := manager.get_agent(_id):
|
|
72
|
+
args = {
|
|
73
|
+
"message": f"Agent {name} already exists.",
|
|
74
|
+
"agent": agent.name,
|
|
75
|
+
"agent_id": agent.chatbot_id,
|
|
76
|
+
"description": agent.description,
|
|
77
|
+
"backstory": agent.backstory,
|
|
78
|
+
"capabilities": agent.get_capabilities(),
|
|
79
|
+
"type": 'PandasAgent',
|
|
80
|
+
"llm": f"{agent.llm!r}",
|
|
81
|
+
"temperature": agent.llm.temperature,
|
|
82
|
+
}
|
|
83
|
+
return self.json_response(
|
|
84
|
+
args,
|
|
85
|
+
status=208
|
|
86
|
+
)
|
|
87
|
+
try:
|
|
88
|
+
# Generate the Data Frames from the queries:
|
|
89
|
+
dfs = await PandasAgent.gen_data(
|
|
90
|
+
query=query.copy(),
|
|
91
|
+
agent_name=_id,
|
|
92
|
+
refresh=True,
|
|
93
|
+
no_cache=True
|
|
94
|
+
)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
return self.json_response(
|
|
97
|
+
{
|
|
98
|
+
"message": f"Error generating dataframes: {e}"
|
|
99
|
+
},
|
|
100
|
+
status=400
|
|
101
|
+
)
|
|
102
|
+
try:
|
|
103
|
+
args = {
|
|
104
|
+
"name": name,
|
|
105
|
+
"df": dfs,
|
|
106
|
+
"query": query,
|
|
107
|
+
"tools": tools,
|
|
108
|
+
"backstory": backstory,
|
|
109
|
+
"capabilities": capabilities,
|
|
110
|
+
**data
|
|
111
|
+
}
|
|
112
|
+
if _id:
|
|
113
|
+
args['chatbot_id'] = _id
|
|
114
|
+
# Create and Add the agent to the manager
|
|
115
|
+
agent = await manager.create_agent(
|
|
116
|
+
class_name=PandasAgent,
|
|
117
|
+
**args
|
|
118
|
+
)
|
|
119
|
+
await agent.configure(app=app)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
return self.json_response(
|
|
122
|
+
{
|
|
123
|
+
"message": f"Error on Agent creation: {e}"
|
|
124
|
+
},
|
|
125
|
+
status=400
|
|
126
|
+
)
|
|
127
|
+
# Check if the agent was created successfully
|
|
128
|
+
if not agent:
|
|
129
|
+
return self.json_response(
|
|
130
|
+
{
|
|
131
|
+
"message": f"Error creating agent: {e}"
|
|
132
|
+
},
|
|
133
|
+
status=400
|
|
134
|
+
)
|
|
135
|
+
# Saving Agent into DB:
|
|
136
|
+
try:
|
|
137
|
+
args.pop('df')
|
|
138
|
+
args['query'] = query
|
|
139
|
+
result = await manager.save_agent(**args)
|
|
140
|
+
if not result:
|
|
141
|
+
manager.remove_agent(agent)
|
|
142
|
+
return self.json_response(
|
|
143
|
+
{
|
|
144
|
+
"message": f"Error saving agent {agent.name}"
|
|
145
|
+
},
|
|
146
|
+
status=400
|
|
147
|
+
)
|
|
148
|
+
except Exception as e:
|
|
149
|
+
manager.remove_agent(agent)
|
|
150
|
+
return self.json_response(
|
|
151
|
+
{
|
|
152
|
+
"message": f"Error saving agent {agent.name}: {e}"
|
|
153
|
+
},
|
|
154
|
+
status=400
|
|
155
|
+
)
|
|
156
|
+
# Return the agent information
|
|
157
|
+
return self.json_response(
|
|
158
|
+
{
|
|
159
|
+
"message": f"Agent {name} created successfully.",
|
|
160
|
+
"agent": agent.name,
|
|
161
|
+
"agent_id": agent.chatbot_id,
|
|
162
|
+
"description": agent.description,
|
|
163
|
+
"backstory": agent.backstory,
|
|
164
|
+
"capabilities": agent.get_capabilities(),
|
|
165
|
+
"type": 'PandasAgent',
|
|
166
|
+
"llm": f"{agent.llm!r}",
|
|
167
|
+
"temperature": agent.llm.temperature,
|
|
168
|
+
},
|
|
169
|
+
status=201
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
async def post(self, *args, **kwargs):
|
|
173
|
+
"""
|
|
174
|
+
post.
|
|
175
|
+
description: Do a query to the Agent.
|
|
176
|
+
Use this method to interact with a Agent.
|
|
177
|
+
"""
|
|
178
|
+
app = self.request.app
|
|
179
|
+
try:
|
|
180
|
+
manager = app['bot_manager']
|
|
181
|
+
except KeyError:
|
|
182
|
+
return self.json_response(
|
|
183
|
+
{
|
|
184
|
+
"message": "Chatbot Manager is not installed."
|
|
185
|
+
},
|
|
186
|
+
status=404
|
|
187
|
+
)
|
|
188
|
+
name = self.request.match_info.get('agent_name', None)
|
|
189
|
+
if not name:
|
|
190
|
+
return self.json_response(
|
|
191
|
+
{
|
|
192
|
+
"message": "Agent name not found."
|
|
193
|
+
},
|
|
194
|
+
status=404
|
|
195
|
+
)
|
|
196
|
+
data = await self.request.json()
|
|
197
|
+
if not 'query' in data:
|
|
198
|
+
return self.json_response(
|
|
199
|
+
{
|
|
200
|
+
"message": "No query was found."
|
|
201
|
+
},
|
|
202
|
+
status=400
|
|
203
|
+
)
|
|
204
|
+
if agent := manager.get_agent(name):
|
|
205
|
+
# doing a question to the agent:
|
|
206
|
+
try:
|
|
207
|
+
response, result = await agent.invoke(
|
|
208
|
+
data['query']
|
|
209
|
+
)
|
|
210
|
+
result.response = response
|
|
211
|
+
# null the chat_history:
|
|
212
|
+
result.chat_history = []
|
|
213
|
+
return self.json_response(response=result)
|
|
214
|
+
except Exception as e:
|
|
215
|
+
return self.json_response(
|
|
216
|
+
{
|
|
217
|
+
"message": f"Error invoking agent: {e}"
|
|
218
|
+
},
|
|
219
|
+
status=400
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
return self.json_response(
|
|
223
|
+
{
|
|
224
|
+
"message": f"Agent {name} not found."
|
|
225
|
+
},
|
|
226
|
+
status=404
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
async def patch(self, *args, **kwargs):
|
|
230
|
+
"""
|
|
231
|
+
patch.
|
|
232
|
+
description: Update the data of the Agent.
|
|
233
|
+
Use this method to update the dataframes assigned to the Agent.
|
|
234
|
+
"""
|
|
235
|
+
app = self.request.app
|
|
236
|
+
try:
|
|
237
|
+
manager = app['bot_manager']
|
|
238
|
+
except KeyError:
|
|
239
|
+
return self.json_response(
|
|
240
|
+
{
|
|
241
|
+
"message": "Chatbot Manager is not installed."
|
|
242
|
+
},
|
|
243
|
+
status=404
|
|
244
|
+
)
|
|
245
|
+
name = self.request.match_info.get('agent_name', None)
|
|
246
|
+
if not name:
|
|
247
|
+
return self.json_response(
|
|
248
|
+
{
|
|
249
|
+
"message": "Agent name not found."
|
|
250
|
+
},
|
|
251
|
+
status=404
|
|
252
|
+
)
|
|
253
|
+
try:
|
|
254
|
+
data = await self.request.json()
|
|
255
|
+
except Exception as e:
|
|
256
|
+
data = {}
|
|
257
|
+
query = data.pop('query', None)
|
|
258
|
+
if agent := manager.get_agent(name):
|
|
259
|
+
# dextract the new query from the request, or from agent
|
|
260
|
+
qry = query if query else agent.get_query()
|
|
261
|
+
try:
|
|
262
|
+
# Generate the Data Frames from the queries:
|
|
263
|
+
dfs = await PandasAgent.gen_data(
|
|
264
|
+
agent_name=str(agent.chatbot_id),
|
|
265
|
+
query=qry,
|
|
266
|
+
refresh=True
|
|
267
|
+
)
|
|
268
|
+
if dfs:
|
|
269
|
+
# Update the agent with the new dataframes
|
|
270
|
+
agent.df = dfs
|
|
271
|
+
# Update the agent with the new query
|
|
272
|
+
await agent.configure(df=dfs)
|
|
273
|
+
return self.json_response(
|
|
274
|
+
{
|
|
275
|
+
"message": f"{agent.name}: Agent Data was Updated."
|
|
276
|
+
},
|
|
277
|
+
status=202
|
|
278
|
+
)
|
|
279
|
+
except Exception as e:
|
|
280
|
+
return self.json_response(
|
|
281
|
+
{
|
|
282
|
+
"message": f"Error refreshing agent {agent.name}: {e}"
|
|
283
|
+
},
|
|
284
|
+
status=400
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
return self.json_response(
|
|
288
|
+
{
|
|
289
|
+
"message": f"Agent {name} not found."
|
|
290
|
+
},
|
|
291
|
+
status=404
|
|
292
|
+
)
|
parrot/handlers/bots.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from asyncdb import AsyncDB
|
|
2
|
+
from navigator.views import (
|
|
3
|
+
ModelView,
|
|
4
|
+
BaseView,
|
|
5
|
+
FormModel
|
|
6
|
+
)
|
|
7
|
+
from parrot.conf import (
|
|
8
|
+
BIGQUERY_CREDENTIALS,
|
|
9
|
+
BIGQUERY_PROJECT_ID,
|
|
10
|
+
)
|
|
11
|
+
from ..models import (
|
|
12
|
+
ChatbotModel,
|
|
13
|
+
ChatbotUsage,
|
|
14
|
+
PromptLibrary,
|
|
15
|
+
ChatbotFeedback,
|
|
16
|
+
FeedbackType
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PromptLibraryManagement(ModelView):
|
|
21
|
+
"""
|
|
22
|
+
PromptLibraryManagement.
|
|
23
|
+
description: PromptLibraryManagement for Parrot Application.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
model = PromptLibrary
|
|
27
|
+
name: str = "Prompt Library Management"
|
|
28
|
+
path: str = '/api/v1/prompt_library'
|
|
29
|
+
pk: str = 'prompt_id'
|
|
30
|
+
|
|
31
|
+
async def _set_created_by(self, value, column, data):
|
|
32
|
+
if not value:
|
|
33
|
+
return await self.get_userid(session=self._session)
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ChatbotUsageHandler(ModelView):
|
|
38
|
+
"""
|
|
39
|
+
ChatbotUsageHandler.
|
|
40
|
+
description: ChatbotUsageHandler for Parrot Application.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
model = ChatbotUsage
|
|
44
|
+
driver: str = 'bigquery'
|
|
45
|
+
credentials: dict = {
|
|
46
|
+
"credentials": BIGQUERY_CREDENTIALS,
|
|
47
|
+
"project_id": BIGQUERY_PROJECT_ID,
|
|
48
|
+
}
|
|
49
|
+
name: str = "Chatbot Usage"
|
|
50
|
+
path: str = '/api/v1/chatbots/usage'
|
|
51
|
+
pk: str = 'sid'
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ChatbotSharingQuestion(BaseView):
|
|
55
|
+
"""
|
|
56
|
+
ChatbotSharingQuestion.
|
|
57
|
+
description: ChatbotSharingQuestion for Parrot Application.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def get_connection(self):
|
|
61
|
+
params = {
|
|
62
|
+
"credentials": BIGQUERY_CREDENTIALS,
|
|
63
|
+
"project_id": BIGQUERY_PROJECT_ID,
|
|
64
|
+
}
|
|
65
|
+
return AsyncDB(
|
|
66
|
+
'bigquery',
|
|
67
|
+
params=params
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
async def get(self):
|
|
71
|
+
qs = self.get_arguments(self.request)
|
|
72
|
+
sid = qs.get('sid', None)
|
|
73
|
+
if not sid:
|
|
74
|
+
return self.error(
|
|
75
|
+
response={
|
|
76
|
+
"message": "You need to Provided a ID of Question"
|
|
77
|
+
},
|
|
78
|
+
status=400
|
|
79
|
+
)
|
|
80
|
+
db = self.get_connection()
|
|
81
|
+
try:
|
|
82
|
+
async with await db.connection() as conn: #pylint: disable=E1101
|
|
83
|
+
ChatbotUsage.Meta.connection = conn
|
|
84
|
+
# Getting a SID from sid
|
|
85
|
+
question = await ChatbotUsage.get(sid=sid)
|
|
86
|
+
if not question:
|
|
87
|
+
return self.error(
|
|
88
|
+
response={
|
|
89
|
+
"message": "Question not found"
|
|
90
|
+
},
|
|
91
|
+
status=404
|
|
92
|
+
)
|
|
93
|
+
return self.json_response(
|
|
94
|
+
{
|
|
95
|
+
"chatbot": question.chatbot_id,
|
|
96
|
+
"question": question.question,
|
|
97
|
+
"answer": question.response,
|
|
98
|
+
"at": question.used_at
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
except Exception as e:
|
|
102
|
+
return self.error(
|
|
103
|
+
response={
|
|
104
|
+
"message": f"Error on Chatbot Sharing Question: {e}"
|
|
105
|
+
},
|
|
106
|
+
status=400
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class FeedbackTypeHandler(BaseView):
|
|
112
|
+
"""
|
|
113
|
+
FeedbackTypeHandler.
|
|
114
|
+
description: FeedbackTypeHandler for Parrot Application.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
async def get(self):
|
|
118
|
+
qs = self.get_arguments(self.request)
|
|
119
|
+
category = qs.get('feedback_type', 'good').capitalize()
|
|
120
|
+
feedback_list = FeedbackType.list_feedback(category)
|
|
121
|
+
return self.json_response({
|
|
122
|
+
"feedback": feedback_list
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
# Manage Feedback:
|
|
126
|
+
class ChatbotFeedbackHandler(FormModel):
|
|
127
|
+
"""
|
|
128
|
+
ChatbotFeedbackHandler.
|
|
129
|
+
description: ChatbotFeedbackHandler for Parrot Application.
|
|
130
|
+
"""
|
|
131
|
+
model = ChatbotFeedback
|
|
132
|
+
path: str = '/api/v1/bot_feedback'
|
|
133
|
+
|
|
134
|
+
def get_connection(self):
|
|
135
|
+
params = {
|
|
136
|
+
"credentials": BIGQUERY_CREDENTIALS,
|
|
137
|
+
"project_id": BIGQUERY_PROJECT_ID,
|
|
138
|
+
}
|
|
139
|
+
return AsyncDB(
|
|
140
|
+
'bigquery',
|
|
141
|
+
params=params,
|
|
142
|
+
force_closing=False
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
async def post(self):
|
|
146
|
+
feedback = await self.validate_payload()
|
|
147
|
+
if not feedback:
|
|
148
|
+
return self.error(
|
|
149
|
+
response={
|
|
150
|
+
"message": "Error on Bot Feedback"
|
|
151
|
+
},
|
|
152
|
+
status=400
|
|
153
|
+
)
|
|
154
|
+
db = self.get_connection()
|
|
155
|
+
try:
|
|
156
|
+
async with await db.connection() as conn: #pylint: disable=E1101
|
|
157
|
+
data = feedback.to_dict()
|
|
158
|
+
# convert to string (bigquery uses json.dumps to convert to string)
|
|
159
|
+
data['sid'] = str(data['sid'])
|
|
160
|
+
data['chatbot_id'] = str(data['chatbot_id'])
|
|
161
|
+
data['expiration_timestamp'] = str(data['expiration_timestamp'])
|
|
162
|
+
data['feedback_type'] = feedback.feedback_type.value
|
|
163
|
+
# writing directly to bigquery
|
|
164
|
+
await conn.write(
|
|
165
|
+
[data],
|
|
166
|
+
table_id=ChatbotFeedback.Meta.name,
|
|
167
|
+
dataset_id=ChatbotFeedback.Meta.schema,
|
|
168
|
+
use_streams=False,
|
|
169
|
+
use_pandas=False
|
|
170
|
+
)
|
|
171
|
+
return self.json_response({
|
|
172
|
+
"message": "Bot Feedback Submitted, Thank you for your feedback!.",
|
|
173
|
+
"question": f"Question of ID: {feedback.sid} for bot {feedback.chatbot_id}"
|
|
174
|
+
}, status=201)
|
|
175
|
+
except Exception as e:
|
|
176
|
+
return self.error(
|
|
177
|
+
response={
|
|
178
|
+
"message": f"Error on Bot Feedback: {e}"
|
|
179
|
+
},
|
|
180
|
+
status=400
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class ChatbotHandler(ModelView):
|
|
185
|
+
"""
|
|
186
|
+
ChatbotHandler.
|
|
187
|
+
description: ChatbotHandler for Parrot Application.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
model = ChatbotModel
|
|
191
|
+
name: str = "Chatbot Management"
|
|
192
|
+
pk: str = 'chatbot_id'
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
async def _set_created_by(self, value, column, data):
|
|
196
|
+
return await self.get_userid(session=self._session)
|
parrot/handlers/chat.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
from aiohttp import web
|
|
2
|
+
from navigator_auth.decorators import (
|
|
3
|
+
is_authenticated,
|
|
4
|
+
user_session,
|
|
5
|
+
allowed_organizations
|
|
6
|
+
)
|
|
7
|
+
from navigator.views import BaseView
|
|
8
|
+
from ..bots.abstract import AbstractBot
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@is_authenticated()
|
|
12
|
+
@user_session()
|
|
13
|
+
class ChatHandler(BaseView):
|
|
14
|
+
"""
|
|
15
|
+
ChatHandler.
|
|
16
|
+
description: Chat Handler for Parrot Application.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
async def get(self, **kwargs):
|
|
20
|
+
"""
|
|
21
|
+
get.
|
|
22
|
+
description: Get method for ChatHandler.
|
|
23
|
+
"""
|
|
24
|
+
name = self.request.match_info.get('chatbot_name', None)
|
|
25
|
+
if not name:
|
|
26
|
+
return self.json_response({
|
|
27
|
+
"message": "Welcome to Parrot Chatbot Service."
|
|
28
|
+
})
|
|
29
|
+
else:
|
|
30
|
+
# retrieve chatbof information:
|
|
31
|
+
manager = self.request.app['bot_manager']
|
|
32
|
+
chatbot = manager.get_bot(name)
|
|
33
|
+
if not chatbot:
|
|
34
|
+
return self.error(
|
|
35
|
+
f"Chatbot {name} not found.",
|
|
36
|
+
status=404
|
|
37
|
+
)
|
|
38
|
+
config_file = getattr(chatbot, 'config_file', None)
|
|
39
|
+
return self.json_response({
|
|
40
|
+
"chatbot": chatbot.name,
|
|
41
|
+
"description": chatbot.description,
|
|
42
|
+
"role": chatbot.role,
|
|
43
|
+
"embedding_model": chatbot.embedding_model,
|
|
44
|
+
"llm": f"{chatbot.llm!r}",
|
|
45
|
+
"temperature": chatbot.llm.temperature,
|
|
46
|
+
"config_file": config_file
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
async def post(self, *args, **kwargs):
|
|
50
|
+
"""
|
|
51
|
+
post.
|
|
52
|
+
description: Post method for ChatHandler.
|
|
53
|
+
|
|
54
|
+
Use this method to interact with a Chatbot.
|
|
55
|
+
"""
|
|
56
|
+
app = self.request.app
|
|
57
|
+
name = self.request.match_info.get('chatbot_name', None)
|
|
58
|
+
qs = self.query_parameters(self.request)
|
|
59
|
+
data = await self.request.json()
|
|
60
|
+
if not 'query' in data:
|
|
61
|
+
return self.json_response(
|
|
62
|
+
{
|
|
63
|
+
"message": "No query was found."
|
|
64
|
+
},
|
|
65
|
+
status=400
|
|
66
|
+
)
|
|
67
|
+
if 'use_llm' in qs:
|
|
68
|
+
# passing another LLM to the Chatbot:
|
|
69
|
+
llm = qs.get('use_llm')
|
|
70
|
+
else:
|
|
71
|
+
llm = None
|
|
72
|
+
try:
|
|
73
|
+
manager = app['bot_manager']
|
|
74
|
+
except KeyError:
|
|
75
|
+
return self.json_response(
|
|
76
|
+
{
|
|
77
|
+
"message": "Chatbot Manager is not installed."
|
|
78
|
+
},
|
|
79
|
+
status=404
|
|
80
|
+
)
|
|
81
|
+
try:
|
|
82
|
+
chatbot: AbstractBot = manager.get_bot(name)
|
|
83
|
+
if not chatbot:
|
|
84
|
+
raise KeyError(
|
|
85
|
+
f"Chatbot {name} not found."
|
|
86
|
+
)
|
|
87
|
+
except (TypeError, KeyError):
|
|
88
|
+
return self.json_response(
|
|
89
|
+
{
|
|
90
|
+
"message": f"Chatbot {name} not found."
|
|
91
|
+
},
|
|
92
|
+
status=404
|
|
93
|
+
)
|
|
94
|
+
# getting the question:
|
|
95
|
+
question = data.pop('query')
|
|
96
|
+
try:
|
|
97
|
+
session = self.request.session
|
|
98
|
+
except AttributeError:
|
|
99
|
+
session = None
|
|
100
|
+
if not session:
|
|
101
|
+
return self.json_response(
|
|
102
|
+
{
|
|
103
|
+
"message": "User Session is required to interact with a Chatbot."
|
|
104
|
+
},
|
|
105
|
+
status=400
|
|
106
|
+
)
|
|
107
|
+
try:
|
|
108
|
+
async with chatbot.retrieval(request=self.request) as retrieval:
|
|
109
|
+
session_id = session.get('session_id', None)
|
|
110
|
+
memory_key = f'{session.session_id}_{name}_message_store'
|
|
111
|
+
memory = retrieval.get_memory(session_id=memory_key)
|
|
112
|
+
result = await retrieval.invoke(
|
|
113
|
+
question=question,
|
|
114
|
+
llm=llm,
|
|
115
|
+
memory=memory,
|
|
116
|
+
**data
|
|
117
|
+
)
|
|
118
|
+
# Drop "memory" information:
|
|
119
|
+
result.chat_history = None
|
|
120
|
+
if result.source_documents:
|
|
121
|
+
documents = []
|
|
122
|
+
for doc in result.source_documents:
|
|
123
|
+
dc = {
|
|
124
|
+
**doc.metadata
|
|
125
|
+
}
|
|
126
|
+
documents.append(dc)
|
|
127
|
+
result.source_documents = documents
|
|
128
|
+
return self.json_response(response=result)
|
|
129
|
+
except ValueError as exc:
|
|
130
|
+
return self.error(
|
|
131
|
+
f"{exc}",
|
|
132
|
+
exception=exc,
|
|
133
|
+
status=400
|
|
134
|
+
)
|
|
135
|
+
except web.HTTPException as exc:
|
|
136
|
+
return self.error(
|
|
137
|
+
f"{exc}",
|
|
138
|
+
exception=exc,
|
|
139
|
+
status=400
|
|
140
|
+
)
|
|
141
|
+
except Exception as exc:
|
|
142
|
+
return self.error(
|
|
143
|
+
f"Error invoking chatbot {name}: {exc}",
|
|
144
|
+
exception=exc,
|
|
145
|
+
status=400
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@is_authenticated()
|
|
150
|
+
@user_session()
|
|
151
|
+
class BotHandler(BaseView):
|
|
152
|
+
"""BotHandler.
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
Use this handler to interact with a brand new chatbot, consuming a configuration.
|
|
156
|
+
"""
|
|
157
|
+
async def put(self):
|
|
158
|
+
"""Create a New Bot (passing a configuration).
|
|
159
|
+
"""
|
|
160
|
+
try:
|
|
161
|
+
manager = self.request.app['bot_manager']
|
|
162
|
+
except KeyError:
|
|
163
|
+
return self.json_response(
|
|
164
|
+
{
|
|
165
|
+
"message": "Chatbot Manager is not installed."
|
|
166
|
+
},
|
|
167
|
+
status=404
|
|
168
|
+
)
|
|
169
|
+
# TODO: Making a Validation of data
|
|
170
|
+
data = await self.request.json()
|
|
171
|
+
name = data.pop('name', None)
|
|
172
|
+
if not name:
|
|
173
|
+
return self.json_response(
|
|
174
|
+
{
|
|
175
|
+
"message": "Name for Bot Creation is required."
|
|
176
|
+
},
|
|
177
|
+
status=400
|
|
178
|
+
)
|
|
179
|
+
try:
|
|
180
|
+
chatbot = manager.create_chatbot(name=name, **data)
|
|
181
|
+
await chatbot.configure(name=name, app=self.request.app)
|
|
182
|
+
return self.json_response(
|
|
183
|
+
{
|
|
184
|
+
"message": f"Chatbot {name} created successfully."
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
except Exception as exc:
|
|
188
|
+
return self.error(
|
|
189
|
+
f"Error creating chatbot {name}: {exc}",
|
|
190
|
+
exception=exc,
|
|
191
|
+
status=400
|
|
192
|
+
)
|