MindsDB 25.6.4.0__py3-none-any.whl → 25.7.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of MindsDB might be problematic. Click here for more details.
- mindsdb/__about__.py +1 -1
- mindsdb/__main__.py +53 -94
- mindsdb/api/a2a/agent.py +30 -206
- mindsdb/api/a2a/common/server/server.py +26 -27
- mindsdb/api/a2a/task_manager.py +93 -227
- mindsdb/api/a2a/utils.py +21 -0
- mindsdb/api/executor/command_executor.py +8 -6
- mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +1 -1
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +9 -11
- mindsdb/api/executor/datahub/datanodes/system_tables.py +1 -1
- mindsdb/api/executor/planner/query_prepare.py +68 -87
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +6 -1
- mindsdb/api/executor/sql_query/steps/union_step.py +11 -9
- mindsdb/api/executor/utilities/sql.py +97 -21
- mindsdb/api/http/namespaces/agents.py +126 -201
- mindsdb/api/http/namespaces/config.py +12 -1
- mindsdb/api/http/namespaces/file.py +49 -24
- mindsdb/api/mcp/start.py +45 -31
- mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +45 -52
- mindsdb/integrations/handlers/huggingface_handler/__init__.py +17 -12
- mindsdb/integrations/handlers/huggingface_handler/finetune.py +223 -223
- mindsdb/integrations/handlers/huggingface_handler/huggingface_handler.py +383 -383
- mindsdb/integrations/handlers/huggingface_handler/requirements.txt +7 -6
- mindsdb/integrations/handlers/huggingface_handler/requirements_cpu.txt +7 -6
- mindsdb/integrations/handlers/huggingface_handler/settings.py +25 -25
- mindsdb/integrations/handlers/litellm_handler/litellm_handler.py +22 -15
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +244 -141
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +1 -1
- mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +3 -2
- mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +1 -1
- mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
- mindsdb/integrations/libs/keyword_search_base.py +41 -0
- mindsdb/integrations/libs/vectordatabase_handler.py +114 -84
- mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +36 -42
- mindsdb/integrations/utilities/sql_utils.py +11 -0
- mindsdb/interfaces/agents/agents_controller.py +29 -9
- mindsdb/interfaces/agents/langchain_agent.py +7 -5
- mindsdb/interfaces/agents/mcp_client_agent.py +4 -4
- mindsdb/interfaces/agents/mindsdb_database_agent.py +10 -43
- mindsdb/interfaces/data_catalog/data_catalog_reader.py +3 -1
- mindsdb/interfaces/database/projects.py +1 -3
- mindsdb/interfaces/functions/controller.py +54 -64
- mindsdb/interfaces/functions/to_markdown.py +47 -14
- mindsdb/interfaces/knowledge_base/controller.py +228 -110
- mindsdb/interfaces/knowledge_base/evaluate.py +18 -6
- mindsdb/interfaces/knowledge_base/executor.py +346 -0
- mindsdb/interfaces/knowledge_base/llm_client.py +5 -6
- mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +20 -45
- mindsdb/interfaces/knowledge_base/preprocessing/models.py +36 -69
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_kb_tools.py +2 -0
- mindsdb/interfaces/skills/sql_agent.py +181 -130
- mindsdb/interfaces/storage/db.py +9 -7
- mindsdb/utilities/config.py +58 -40
- mindsdb/utilities/exception.py +58 -7
- mindsdb/utilities/security.py +54 -11
- {mindsdb-25.6.4.0.dist-info → mindsdb-25.7.2.0.dist-info}/METADATA +245 -259
- {mindsdb-25.6.4.0.dist-info → mindsdb-25.7.2.0.dist-info}/RECORD +61 -58
- {mindsdb-25.6.4.0.dist-info → mindsdb-25.7.2.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.6.4.0.dist-info → mindsdb-25.7.2.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.6.4.0.dist-info → mindsdb-25.7.2.0.dist-info}/top_level.txt +0 -0
|
@@ -25,23 +25,15 @@ AGENT_QUICK_RESPONSE = "I understand your request. I'm working on a detailed res
|
|
|
25
25
|
|
|
26
26
|
def create_agent(project_name, name, agent):
|
|
27
27
|
if name is None:
|
|
28
|
-
return http_error(
|
|
29
|
-
HTTPStatus.BAD_REQUEST,
|
|
30
|
-
'Missing field',
|
|
31
|
-
'Missing "name" field for agent'
|
|
32
|
-
)
|
|
28
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Missing field", 'Missing "name" field for agent')
|
|
33
29
|
|
|
34
|
-
if
|
|
35
|
-
return http_error(
|
|
36
|
-
HTTPStatus.BAD_REQUEST,
|
|
37
|
-
'Missing field',
|
|
38
|
-
'Missing "model_name" field for agent'
|
|
39
|
-
)
|
|
30
|
+
if "model_name" not in agent:
|
|
31
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Missing field", 'Missing "model_name" field for agent')
|
|
40
32
|
|
|
41
|
-
model_name = agent[
|
|
42
|
-
provider = agent.get(
|
|
43
|
-
params = agent.get(
|
|
44
|
-
skills = agent.get(
|
|
33
|
+
model_name = agent["model_name"]
|
|
34
|
+
provider = agent.get("provider")
|
|
35
|
+
params = agent.get("params", {})
|
|
36
|
+
skills = agent.get("skills", [])
|
|
45
37
|
|
|
46
38
|
agents_controller = AgentsController()
|
|
47
39
|
|
|
@@ -49,117 +41,99 @@ def create_agent(project_name, name, agent):
|
|
|
49
41
|
existing_agent = agents_controller.get_agent(name, project_name=project_name)
|
|
50
42
|
except (ValueError, EntityNotExistsError):
|
|
51
43
|
# Project must exist.
|
|
52
|
-
return http_error(
|
|
53
|
-
HTTPStatus.NOT_FOUND,
|
|
54
|
-
'Project not found',
|
|
55
|
-
f'Project with name {project_name} does not exist'
|
|
56
|
-
)
|
|
44
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist")
|
|
57
45
|
if existing_agent is not None:
|
|
58
46
|
return http_error(
|
|
59
47
|
HTTPStatus.CONFLICT,
|
|
60
|
-
|
|
61
|
-
f
|
|
48
|
+
"Agent already exists",
|
|
49
|
+
f"Agent with name {name} already exists. Please choose a different one.",
|
|
62
50
|
)
|
|
63
51
|
|
|
64
52
|
try:
|
|
65
53
|
created_agent = agents_controller.add_agent(
|
|
66
|
-
name=name,
|
|
67
|
-
project_name=project_name,
|
|
68
|
-
model_name=model_name,
|
|
69
|
-
skills=skills,
|
|
70
|
-
provider=provider,
|
|
71
|
-
params=params
|
|
54
|
+
name=name, project_name=project_name, model_name=model_name, skills=skills, provider=provider, params=params
|
|
72
55
|
)
|
|
73
56
|
return created_agent.as_dict(), HTTPStatus.CREATED
|
|
74
57
|
except ValueError:
|
|
75
58
|
# Model or skill doesn't exist.
|
|
76
59
|
return http_error(
|
|
77
60
|
HTTPStatus.NOT_FOUND,
|
|
78
|
-
|
|
79
|
-
f'The model "{model_name}" or skills "{skills}" do not exist. Please ensure that the names are correct and try again.'
|
|
61
|
+
"Resource not found",
|
|
62
|
+
f'The model "{model_name}" or skills "{skills}" do not exist. Please ensure that the names are correct and try again.',
|
|
80
63
|
)
|
|
81
64
|
except NotImplementedError:
|
|
82
65
|
# Free users trying to create agent.
|
|
83
66
|
return http_error(
|
|
84
67
|
HTTPStatus.UNAUTHORIZED,
|
|
85
|
-
|
|
86
|
-
f'The model "{model_name}" or skills "{skills}" do not exist. Please ensure that the names are correct and try again.'
|
|
68
|
+
"Unavailable to free users",
|
|
69
|
+
f'The model "{model_name}" or skills "{skills}" do not exist. Please ensure that the names are correct and try again.',
|
|
87
70
|
)
|
|
88
71
|
|
|
89
72
|
|
|
90
|
-
@ns_conf.route(
|
|
73
|
+
@ns_conf.route("/<project_name>/agents")
|
|
91
74
|
class AgentsResource(Resource):
|
|
92
|
-
@ns_conf.doc(
|
|
93
|
-
@api_endpoint_metrics(
|
|
75
|
+
@ns_conf.doc("list_agents")
|
|
76
|
+
@api_endpoint_metrics("GET", "/agents")
|
|
94
77
|
def get(self, project_name):
|
|
95
|
-
|
|
78
|
+
"""List all agents"""
|
|
96
79
|
session = SessionController()
|
|
97
80
|
try:
|
|
98
81
|
all_agents = session.agents_controller.get_agents(project_name)
|
|
99
82
|
except EntityNotExistsError:
|
|
100
83
|
# Project needs to exist.
|
|
101
84
|
return http_error(
|
|
102
|
-
HTTPStatus.NOT_FOUND,
|
|
103
|
-
|
|
104
|
-
f'Project with name {project_name} does not exist')
|
|
85
|
+
HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist"
|
|
86
|
+
)
|
|
105
87
|
return [a.as_dict() for a in all_agents]
|
|
106
88
|
|
|
107
|
-
@ns_conf.doc(
|
|
108
|
-
@api_endpoint_metrics(
|
|
89
|
+
@ns_conf.doc("create_agent")
|
|
90
|
+
@api_endpoint_metrics("POST", "/agents")
|
|
109
91
|
def post(self, project_name):
|
|
110
|
-
|
|
92
|
+
"""Create a agent"""
|
|
111
93
|
|
|
112
94
|
# Check for required parameters.
|
|
113
|
-
if
|
|
95
|
+
if "agent" not in request.json:
|
|
114
96
|
return http_error(
|
|
115
|
-
HTTPStatus.BAD_REQUEST,
|
|
116
|
-
'Missing parameter',
|
|
117
|
-
'Must provide "agent" parameter in POST body'
|
|
97
|
+
HTTPStatus.BAD_REQUEST, "Missing parameter", 'Must provide "agent" parameter in POST body'
|
|
118
98
|
)
|
|
119
99
|
|
|
120
|
-
agent = request.json[
|
|
100
|
+
agent = request.json["agent"]
|
|
121
101
|
|
|
122
|
-
name = agent.get(
|
|
102
|
+
name = agent.get("name")
|
|
123
103
|
return create_agent(project_name, name, agent)
|
|
124
104
|
|
|
125
105
|
|
|
126
|
-
@ns_conf.route(
|
|
127
|
-
@ns_conf.param(
|
|
128
|
-
@ns_conf.param(
|
|
106
|
+
@ns_conf.route("/<project_name>/agents/<agent_name>")
|
|
107
|
+
@ns_conf.param("project_name", "Name of the project")
|
|
108
|
+
@ns_conf.param("agent_name", "Name of the agent")
|
|
129
109
|
class AgentResource(Resource):
|
|
130
|
-
@ns_conf.doc(
|
|
131
|
-
@api_endpoint_metrics(
|
|
110
|
+
@ns_conf.doc("get_agent")
|
|
111
|
+
@api_endpoint_metrics("GET", "/agents/agent")
|
|
132
112
|
def get(self, project_name, agent_name):
|
|
133
|
-
|
|
113
|
+
"""Gets an agent by name"""
|
|
134
114
|
session = SessionController()
|
|
135
115
|
try:
|
|
136
116
|
existing_agent = session.agents_controller.get_agent(agent_name, project_name=project_name)
|
|
137
117
|
if existing_agent is None:
|
|
138
118
|
return http_error(
|
|
139
|
-
HTTPStatus.NOT_FOUND,
|
|
140
|
-
'Agent not found',
|
|
141
|
-
f'Agent with name {agent_name} does not exist'
|
|
119
|
+
HTTPStatus.NOT_FOUND, "Agent not found", f"Agent with name {agent_name} does not exist"
|
|
142
120
|
)
|
|
143
121
|
return existing_agent.as_dict()
|
|
144
122
|
except (ValueError, EntityNotExistsError):
|
|
145
123
|
# Project needs to exist.
|
|
146
124
|
return http_error(
|
|
147
|
-
HTTPStatus.NOT_FOUND,
|
|
148
|
-
'Project not found',
|
|
149
|
-
f'Project with name {project_name} does not exist'
|
|
125
|
+
HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist"
|
|
150
126
|
)
|
|
151
127
|
|
|
152
|
-
@ns_conf.doc(
|
|
153
|
-
@api_endpoint_metrics(
|
|
128
|
+
@ns_conf.doc("update_agent")
|
|
129
|
+
@api_endpoint_metrics("PUT", "/agents/agent")
|
|
154
130
|
def put(self, project_name, agent_name):
|
|
155
|
-
|
|
131
|
+
"""Updates an agent by name, creating one if it doesn't exist"""
|
|
156
132
|
|
|
157
133
|
# Check for required parameters.
|
|
158
|
-
if
|
|
134
|
+
if "agent" not in request.json:
|
|
159
135
|
return http_error(
|
|
160
|
-
HTTPStatus.BAD_REQUEST,
|
|
161
|
-
'Missing parameter',
|
|
162
|
-
'Must provide "agent" parameter in POST body'
|
|
136
|
+
HTTPStatus.BAD_REQUEST, "Missing parameter", 'Must provide "agent" parameter in POST body'
|
|
163
137
|
)
|
|
164
138
|
agents_controller = AgentsController()
|
|
165
139
|
|
|
@@ -168,25 +142,23 @@ class AgentResource(Resource):
|
|
|
168
142
|
except (ValueError, EntityNotExistsError):
|
|
169
143
|
# Project must exist.
|
|
170
144
|
return http_error(
|
|
171
|
-
HTTPStatus.NOT_FOUND,
|
|
172
|
-
'Project not found',
|
|
173
|
-
f'Project with name {project_name} does not exist'
|
|
145
|
+
HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist"
|
|
174
146
|
)
|
|
175
147
|
if existing_agent_record is None:
|
|
176
148
|
return http_error(
|
|
177
149
|
HTTPStatus.BAD_REQUEST,
|
|
178
|
-
|
|
179
|
-
|
|
150
|
+
"Creation is not allowed",
|
|
151
|
+
"Creation of an agent using the PUT method is not allowed.",
|
|
180
152
|
)
|
|
181
153
|
|
|
182
|
-
agent = request.json[
|
|
183
|
-
name = agent.get(
|
|
184
|
-
model_name = agent.get(
|
|
185
|
-
skills_to_add = agent.get(
|
|
186
|
-
skills_to_remove = agent.get(
|
|
187
|
-
skills_to_rewrite = agent.get(
|
|
188
|
-
provider = agent.get(
|
|
189
|
-
params = agent.get(
|
|
154
|
+
agent = request.json["agent"]
|
|
155
|
+
name = agent.get("name", None)
|
|
156
|
+
model_name = agent.get("model_name", None)
|
|
157
|
+
skills_to_add = agent.get("skills_to_add", [])
|
|
158
|
+
skills_to_remove = agent.get("skills_to_remove", [])
|
|
159
|
+
skills_to_rewrite = agent.get("skills", [])
|
|
160
|
+
provider = agent.get("provider")
|
|
161
|
+
params = agent.get("params", None)
|
|
190
162
|
|
|
191
163
|
# Agent must not exist with new name.
|
|
192
164
|
if name is not None and name != agent_name:
|
|
@@ -194,8 +166,8 @@ class AgentResource(Resource):
|
|
|
194
166
|
if agent_with_new_name is not None:
|
|
195
167
|
return http_error(
|
|
196
168
|
HTTPStatus.CONFLICT,
|
|
197
|
-
|
|
198
|
-
f
|
|
169
|
+
"Agent already exists",
|
|
170
|
+
f"Agent with name {name} already exists. Please choose a different one.",
|
|
199
171
|
)
|
|
200
172
|
|
|
201
173
|
if existing_agent_record is None:
|
|
@@ -213,21 +185,21 @@ class AgentResource(Resource):
|
|
|
213
185
|
skills_controller = session.skills_controller
|
|
214
186
|
retrieval_skill_added = False
|
|
215
187
|
if len(skills_to_add) > 0:
|
|
216
|
-
skills_names = [x[
|
|
188
|
+
skills_names = [x["name"] if isinstance(x, dict) else x for x in skills_to_add]
|
|
217
189
|
retrieval_skill_added = any(
|
|
218
|
-
skills_controller.get_skill(skill_name).type ==
|
|
190
|
+
skills_controller.get_skill(skill_name).type == "retrieval"
|
|
219
191
|
for skill_name in skills_names
|
|
220
192
|
if skills_controller.get_skill(skill_name) is not None
|
|
221
193
|
)
|
|
222
194
|
elif len(skills_to_rewrite) > 0:
|
|
223
195
|
retrieval_skill_added = any(
|
|
224
|
-
skills_controller.get_skill(skill_meta[
|
|
196
|
+
skills_controller.get_skill(skill_meta["name"]).type == "retrieval"
|
|
225
197
|
for skill_meta in skills_to_rewrite
|
|
226
|
-
if skills_controller.get_skill(skill_meta[
|
|
198
|
+
if skills_controller.get_skill(skill_meta["name"]) is not None
|
|
227
199
|
)
|
|
228
200
|
|
|
229
|
-
if retrieval_skill_added and
|
|
230
|
-
params[
|
|
201
|
+
if retrieval_skill_added and "mode" not in params:
|
|
202
|
+
params["mode"] = "retrieval"
|
|
231
203
|
|
|
232
204
|
updated_agent = agents_controller.update_agent(
|
|
233
205
|
agent_name,
|
|
@@ -238,64 +210,45 @@ class AgentResource(Resource):
|
|
|
238
210
|
skills_to_remove=skills_to_remove,
|
|
239
211
|
skills_to_rewrite=skills_to_rewrite,
|
|
240
212
|
provider=provider,
|
|
241
|
-
params=params
|
|
213
|
+
params=params,
|
|
242
214
|
)
|
|
243
215
|
|
|
244
216
|
return updated_agent.as_dict()
|
|
245
217
|
except EntityExistsError as e:
|
|
246
|
-
return http_error(
|
|
247
|
-
HTTPStatus.NOT_FOUND,
|
|
248
|
-
'Resource should not exists',
|
|
249
|
-
str(e)
|
|
250
|
-
)
|
|
218
|
+
return http_error(HTTPStatus.NOT_FOUND, "Resource should not exists", str(e))
|
|
251
219
|
except EntityNotExistsError as e:
|
|
252
220
|
# Agent or skill doesn't exist.
|
|
253
|
-
return http_error(
|
|
254
|
-
HTTPStatus.NOT_FOUND,
|
|
255
|
-
'Resource not found',
|
|
256
|
-
str(e)
|
|
257
|
-
)
|
|
221
|
+
return http_error(HTTPStatus.NOT_FOUND, "Resource not found", str(e))
|
|
258
222
|
except ValueError as e:
|
|
259
|
-
return http_error(
|
|
260
|
-
HTTPStatus.BAD_REQUEST,
|
|
261
|
-
'Wrong arguments',
|
|
262
|
-
str(e)
|
|
263
|
-
)
|
|
223
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Wrong arguments", str(e))
|
|
264
224
|
|
|
265
|
-
@ns_conf.doc(
|
|
266
|
-
@api_endpoint_metrics(
|
|
225
|
+
@ns_conf.doc("delete_agent")
|
|
226
|
+
@api_endpoint_metrics("DELETE", "/agents/agent")
|
|
267
227
|
def delete(self, project_name, agent_name):
|
|
268
|
-
|
|
228
|
+
"""Deletes a agent by name"""
|
|
269
229
|
agents_controller = AgentsController()
|
|
270
230
|
|
|
271
231
|
try:
|
|
272
232
|
existing_agent = agents_controller.get_agent(agent_name, project_name=project_name)
|
|
273
233
|
if existing_agent is None:
|
|
274
234
|
return http_error(
|
|
275
|
-
HTTPStatus.NOT_FOUND,
|
|
276
|
-
'Agent not found',
|
|
277
|
-
f'Agent with name {agent_name} does not exist'
|
|
235
|
+
HTTPStatus.NOT_FOUND, "Agent not found", f"Agent with name {agent_name} does not exist"
|
|
278
236
|
)
|
|
279
237
|
except (ValueError, EntityNotExistsError):
|
|
280
238
|
# Project needs to exist.
|
|
281
239
|
return http_error(
|
|
282
|
-
HTTPStatus.NOT_FOUND,
|
|
283
|
-
'Project not found',
|
|
284
|
-
f'Project with name {project_name} does not exist'
|
|
240
|
+
HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist"
|
|
285
241
|
)
|
|
286
242
|
|
|
287
243
|
agents_controller.delete_agent(agent_name, project_name=project_name)
|
|
288
|
-
return
|
|
244
|
+
return "", HTTPStatus.NO_CONTENT
|
|
289
245
|
|
|
290
246
|
|
|
291
|
-
def _completion_event_generator(
|
|
292
|
-
agent_name: str,
|
|
293
|
-
messages: List[Dict],
|
|
294
|
-
project_name: str) -> Iterable[str]:
|
|
247
|
+
def _completion_event_generator(agent_name: str, messages: List[Dict], project_name: str) -> Iterable[str]:
|
|
295
248
|
logger.info(f"Starting completion event generator for agent {agent_name}")
|
|
296
249
|
|
|
297
250
|
def json_serialize(data):
|
|
298
|
-
return f
|
|
251
|
+
return f"data: {json.dumps(data)}\n\n"
|
|
299
252
|
|
|
300
253
|
try:
|
|
301
254
|
# Populate API key by default if not present.
|
|
@@ -303,40 +256,36 @@ def _completion_event_generator(
|
|
|
303
256
|
existing_agent = session.agents_controller.get_agent(agent_name, project_name=project_name)
|
|
304
257
|
if not existing_agent.params:
|
|
305
258
|
existing_agent.params = {}
|
|
306
|
-
existing_agent.params[
|
|
307
|
-
|
|
259
|
+
existing_agent.params["openai_api_key"] = existing_agent.params.get(
|
|
260
|
+
"openai_api_key", os.getenv("OPENAI_API_KEY")
|
|
261
|
+
)
|
|
308
262
|
# Have to commit/flush here so DB isn't locked while streaming.
|
|
309
263
|
db.session.commit()
|
|
310
264
|
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
and any(rel.skill.type == 'retrieval' for rel in existing_agent.skills_relationships)
|
|
265
|
+
if "mode" not in existing_agent.params and any(
|
|
266
|
+
rel.skill.type == "retrieval" for rel in existing_agent.skills_relationships
|
|
314
267
|
):
|
|
315
|
-
existing_agent.params[
|
|
268
|
+
existing_agent.params["mode"] = "retrieval"
|
|
316
269
|
|
|
317
270
|
completion_stream = session.agents_controller.get_completion(
|
|
318
|
-
existing_agent,
|
|
319
|
-
messages,
|
|
320
|
-
project_name=project_name,
|
|
321
|
-
tools=[],
|
|
322
|
-
stream=True
|
|
271
|
+
existing_agent, messages, project_name=project_name, tools=[], stream=True
|
|
323
272
|
)
|
|
324
273
|
|
|
325
274
|
for chunk in completion_stream:
|
|
326
|
-
if isinstance(chunk, str) and chunk.startswith(
|
|
275
|
+
if isinstance(chunk, str) and chunk.startswith("data: "):
|
|
327
276
|
# The chunk is already formatted correctly, yield it as is
|
|
328
277
|
yield chunk
|
|
329
278
|
elif isinstance(chunk, dict):
|
|
330
|
-
if
|
|
279
|
+
if "error" in chunk:
|
|
331
280
|
# Handle error chunks
|
|
332
281
|
logger.error(f"Error in completion stream: {chunk['error']}")
|
|
333
|
-
yield json_serialize({"error": chunk[
|
|
334
|
-
elif chunk.get(
|
|
282
|
+
yield json_serialize({"error": chunk["error"]})
|
|
283
|
+
elif chunk.get("type") == "context":
|
|
335
284
|
# Handle context message
|
|
336
|
-
yield json_serialize({"type": "context", "content": chunk.get(
|
|
337
|
-
elif chunk.get(
|
|
285
|
+
yield json_serialize({"type": "context", "content": chunk.get("content")})
|
|
286
|
+
elif chunk.get("type") == "sql":
|
|
338
287
|
# Handle SQL query message
|
|
339
|
-
yield json_serialize({"type": "sql", "content": chunk.get(
|
|
288
|
+
yield json_serialize({"type": "sql", "content": chunk.get("content")})
|
|
340
289
|
else:
|
|
341
290
|
# Chunk should already be formatted by agent stream.
|
|
342
291
|
yield json_serialize(chunk)
|
|
@@ -349,31 +298,28 @@ def _completion_event_generator(
|
|
|
349
298
|
logger.info("Completion stream finished")
|
|
350
299
|
|
|
351
300
|
except Exception as e:
|
|
352
|
-
|
|
353
|
-
logger.error(error_message)
|
|
301
|
+
logger.error(f"Error in completion event generator: {e}")
|
|
354
302
|
logger.error(traceback.format_exc())
|
|
355
|
-
yield json_serialize({"error":
|
|
303
|
+
yield json_serialize({"error": "error in completion event generator"})
|
|
356
304
|
|
|
357
305
|
finally:
|
|
358
306
|
yield json_serialize({"type": "end"})
|
|
359
307
|
|
|
360
308
|
|
|
361
|
-
@ns_conf.route(
|
|
362
|
-
@ns_conf.param(
|
|
363
|
-
@ns_conf.param(
|
|
309
|
+
@ns_conf.route("/<project_name>/agents/<agent_name>/completions/stream")
|
|
310
|
+
@ns_conf.param("project_name", "Name of the project")
|
|
311
|
+
@ns_conf.param("agent_name", "Name of the agent")
|
|
364
312
|
class AgentCompletionsStream(Resource):
|
|
365
|
-
@ns_conf.doc(
|
|
366
|
-
@api_endpoint_metrics(
|
|
313
|
+
@ns_conf.doc("agent_completions_stream")
|
|
314
|
+
@api_endpoint_metrics("POST", "/agents/agent/completions/stream")
|
|
367
315
|
def post(self, project_name, agent_name):
|
|
368
316
|
logger.info(f"Received streaming request for agent {agent_name} in project {project_name}")
|
|
369
317
|
|
|
370
318
|
# Check for required parameters.
|
|
371
|
-
if
|
|
319
|
+
if "messages" not in request.json:
|
|
372
320
|
logger.error("Missing 'messages' parameter in request body")
|
|
373
321
|
return http_error(
|
|
374
|
-
HTTPStatus.BAD_REQUEST,
|
|
375
|
-
'Missing parameter',
|
|
376
|
-
'Must provide "messages" parameter in POST body'
|
|
322
|
+
HTTPStatus.BAD_REQUEST, "Missing parameter", 'Must provide "messages" parameter in POST body'
|
|
377
323
|
)
|
|
378
324
|
|
|
379
325
|
session = SessionController()
|
|
@@ -382,52 +328,40 @@ class AgentCompletionsStream(Resource):
|
|
|
382
328
|
if existing_agent is None:
|
|
383
329
|
logger.error(f"Agent {agent_name} not found in project {project_name}")
|
|
384
330
|
return http_error(
|
|
385
|
-
HTTPStatus.NOT_FOUND,
|
|
386
|
-
'Agent not found',
|
|
387
|
-
f'Agent with name {agent_name} does not exist'
|
|
331
|
+
HTTPStatus.NOT_FOUND, "Agent not found", f"Agent with name {agent_name} does not exist"
|
|
388
332
|
)
|
|
389
333
|
except ValueError as e:
|
|
390
334
|
logger.error(f"Project {project_name} not found: {str(e)}")
|
|
391
335
|
return http_error(
|
|
392
|
-
HTTPStatus.NOT_FOUND,
|
|
393
|
-
'Project not found',
|
|
394
|
-
f'Project with name {project_name} does not exist'
|
|
336
|
+
HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist"
|
|
395
337
|
)
|
|
396
338
|
|
|
397
|
-
messages = request.json[
|
|
339
|
+
messages = request.json["messages"]
|
|
398
340
|
|
|
399
341
|
try:
|
|
400
|
-
gen = _completion_event_generator(
|
|
401
|
-
agent_name,
|
|
402
|
-
messages,
|
|
403
|
-
project_name
|
|
404
|
-
)
|
|
342
|
+
gen = _completion_event_generator(agent_name, messages, project_name)
|
|
405
343
|
logger.info(f"Starting streaming response for agent {agent_name}")
|
|
406
|
-
return Response(gen, mimetype=
|
|
344
|
+
return Response(gen, mimetype="text/event-stream")
|
|
407
345
|
except Exception as e:
|
|
408
346
|
logger.error(f"Error during streaming for agent {agent_name}: {str(e)}")
|
|
409
347
|
logger.error(traceback.format_exc())
|
|
410
348
|
return http_error(
|
|
411
|
-
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
412
|
-
'Streaming error',
|
|
413
|
-
f'An error occurred during streaming: {str(e)}'
|
|
349
|
+
HTTPStatus.INTERNAL_SERVER_ERROR, "Streaming error", f"An error occurred during streaming: {str(e)}"
|
|
414
350
|
)
|
|
415
351
|
|
|
416
352
|
|
|
417
|
-
@ns_conf.route(
|
|
418
|
-
@ns_conf.param(
|
|
419
|
-
@ns_conf.param(
|
|
353
|
+
@ns_conf.route("/<project_name>/agents/<agent_name>/completions")
|
|
354
|
+
@ns_conf.param("project_name", "Name of the project")
|
|
355
|
+
@ns_conf.param("agent_name", "Name of the agent")
|
|
420
356
|
class AgentCompletions(Resource):
|
|
421
|
-
@ns_conf.doc(
|
|
422
|
-
@api_endpoint_metrics(
|
|
357
|
+
@ns_conf.doc("agent_completions")
|
|
358
|
+
@api_endpoint_metrics("POST", "/agents/agent/completions")
|
|
423
359
|
def post(self, project_name, agent_name):
|
|
424
|
-
|
|
360
|
+
"""Queries an agent given a list of messages"""
|
|
425
361
|
# Check for required parameters.
|
|
426
|
-
if
|
|
362
|
+
if "messages" not in request.json:
|
|
427
363
|
return http_error(
|
|
428
|
-
HTTPStatus.BAD_REQUEST,
|
|
429
|
-
'Missing parameter',
|
|
430
|
-
'Must provide "messages" parameter in POST body'
|
|
364
|
+
HTTPStatus.BAD_REQUEST, "Missing parameter", 'Must provide "messages" parameter in POST body'
|
|
431
365
|
)
|
|
432
366
|
agents_controller = AgentsController()
|
|
433
367
|
|
|
@@ -435,31 +369,28 @@ class AgentCompletions(Resource):
|
|
|
435
369
|
existing_agent = agents_controller.get_agent(agent_name, project_name=project_name)
|
|
436
370
|
if existing_agent is None:
|
|
437
371
|
return http_error(
|
|
438
|
-
HTTPStatus.NOT_FOUND,
|
|
439
|
-
'Agent not found',
|
|
440
|
-
f'Agent with name {agent_name} does not exist'
|
|
372
|
+
HTTPStatus.NOT_FOUND, "Agent not found", f"Agent with name {agent_name} does not exist"
|
|
441
373
|
)
|
|
442
374
|
except (ValueError, EntityNotExistsError):
|
|
443
375
|
# Project needs to exist.
|
|
444
376
|
return http_error(
|
|
445
|
-
HTTPStatus.NOT_FOUND,
|
|
446
|
-
'Project not found',
|
|
447
|
-
f'Project with name {project_name} does not exist'
|
|
377
|
+
HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist"
|
|
448
378
|
)
|
|
449
379
|
|
|
450
380
|
# Add OpenAI API key to agent params if not already present.
|
|
451
381
|
if not existing_agent.params:
|
|
452
382
|
existing_agent.params = {}
|
|
453
|
-
existing_agent.params[
|
|
383
|
+
existing_agent.params["openai_api_key"] = existing_agent.params.get(
|
|
384
|
+
"openai_api_key", os.getenv("OPENAI_API_KEY")
|
|
385
|
+
)
|
|
454
386
|
|
|
455
387
|
# set mode to `retrieval` if agent has a skill of type `retrieval` and mode is not set
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
and any(rel.skill.type == 'retrieval' for rel in existing_agent.skills_relationships)
|
|
388
|
+
if "mode" not in existing_agent.params and any(
|
|
389
|
+
rel.skill.type == "retrieval" for rel in existing_agent.skills_relationships
|
|
459
390
|
):
|
|
460
|
-
existing_agent.params[
|
|
391
|
+
existing_agent.params["mode"] = "retrieval"
|
|
461
392
|
|
|
462
|
-
messages = request.json[
|
|
393
|
+
messages = request.json["messages"]
|
|
463
394
|
|
|
464
395
|
completion = agents_controller.get_completion(
|
|
465
396
|
existing_agent,
|
|
@@ -467,32 +398,26 @@ class AgentCompletions(Resource):
|
|
|
467
398
|
project_name=project_name,
|
|
468
399
|
# Don't need to include backoffice_db related tools into this endpoint.
|
|
469
400
|
# Underlying handler (e.g. Langchain) will handle default tools like mdb_read, mdb_write, etc.
|
|
470
|
-
tools=[]
|
|
401
|
+
tools=[],
|
|
471
402
|
)
|
|
472
403
|
|
|
473
404
|
output_col = agents_controller.assistant_column
|
|
474
405
|
model_output = completion.iloc[-1][output_col]
|
|
475
|
-
trace_id = completion.iloc[-1][
|
|
406
|
+
trace_id = completion.iloc[-1]["trace_id"]
|
|
476
407
|
|
|
477
|
-
response = {
|
|
478
|
-
'message': {
|
|
479
|
-
'content': model_output,
|
|
480
|
-
'role': 'assistant',
|
|
481
|
-
'trace_id': trace_id
|
|
482
|
-
}
|
|
483
|
-
}
|
|
408
|
+
response = {"message": {"content": model_output, "role": "assistant", "trace_id": trace_id}}
|
|
484
409
|
|
|
485
|
-
if existing_agent.params.get(
|
|
410
|
+
if existing_agent.params.get("return_context", False):
|
|
486
411
|
context = []
|
|
487
|
-
if
|
|
412
|
+
if "context" in completion.columns:
|
|
488
413
|
try:
|
|
489
|
-
last_context = completion.iloc[-1][
|
|
414
|
+
last_context = completion.iloc[-1]["context"]
|
|
490
415
|
if last_context:
|
|
491
416
|
context = json.loads(last_context)
|
|
492
417
|
except (json.JSONDecodeError, IndexError) as e:
|
|
493
|
-
logger.error(f
|
|
418
|
+
logger.error(f"Error decoding context: {e}")
|
|
494
419
|
pass # Keeping context as an empty list in case of error
|
|
495
420
|
|
|
496
|
-
response[
|
|
421
|
+
response["message"]["context"] = context
|
|
497
422
|
|
|
498
423
|
return response
|
|
@@ -28,10 +28,12 @@ class GetConfig(Resource):
|
|
|
28
28
|
def get(self):
|
|
29
29
|
config = Config()
|
|
30
30
|
resp = {"auth": {"http_auth_enabled": config["auth"]["http_auth_enabled"]}}
|
|
31
|
-
for key in ["default_llm", "default_embedding_model", "default_reranking_model"
|
|
31
|
+
for key in ["default_llm", "default_embedding_model", "default_reranking_model"]:
|
|
32
32
|
value = config.get(key)
|
|
33
33
|
if value is not None:
|
|
34
34
|
resp[key] = value
|
|
35
|
+
if "a2a" in config["api"]:
|
|
36
|
+
resp["a2a"] = config["api"]["a2a"]
|
|
35
37
|
return resp
|
|
36
38
|
|
|
37
39
|
@ns_conf.doc("put_config")
|
|
@@ -53,6 +55,15 @@ class GetConfig(Resource):
|
|
|
53
55
|
HTTPStatus.BAD_REQUEST, "Wrong arguments", f"Unknown argumens: {unknown_arguments}"
|
|
54
56
|
)
|
|
55
57
|
|
|
58
|
+
overwrite_arguments = {"default_llm", "default_embedding_model", "default_reranking_model"}
|
|
59
|
+
overwrite_data = {k: data[k] for k in overwrite_arguments if k in data}
|
|
60
|
+
merge_data = {k: data[k] for k in data if k not in overwrite_arguments}
|
|
61
|
+
|
|
62
|
+
if len(overwrite_data) > 0:
|
|
63
|
+
Config().update(overwrite_data, overwrite=True)
|
|
64
|
+
if len(merge_data) > 0:
|
|
65
|
+
Config().update(merge_data)
|
|
66
|
+
|
|
56
67
|
Config().update(data)
|
|
57
68
|
|
|
58
69
|
return "", 200
|