MindsDB 25.5.4.1__py3-none-any.whl → 25.6.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/api/a2a/agent.py +28 -25
- mindsdb/api/a2a/common/server/server.py +32 -26
- mindsdb/api/a2a/run_a2a.py +1 -1
- mindsdb/api/executor/command_executor.py +69 -14
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +49 -65
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +29 -48
- mindsdb/api/executor/datahub/datanodes/system_tables.py +35 -61
- mindsdb/api/executor/planner/plan_join.py +67 -77
- mindsdb/api/executor/planner/query_planner.py +176 -155
- mindsdb/api/executor/planner/steps.py +37 -12
- mindsdb/api/executor/sql_query/result_set.py +45 -64
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +14 -18
- mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +17 -18
- mindsdb/api/executor/sql_query/steps/insert_step.py +13 -33
- mindsdb/api/executor/sql_query/steps/subselect_step.py +43 -35
- mindsdb/api/executor/utilities/sql.py +42 -48
- mindsdb/api/http/namespaces/config.py +1 -1
- mindsdb/api/http/namespaces/file.py +14 -23
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_datum.py +12 -28
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/binary_resultset_row_package.py +59 -50
- mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/resultset_row_package.py +9 -8
- mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +449 -461
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +87 -36
- mindsdb/integrations/handlers/file_handler/file_handler.py +15 -9
- mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py +43 -24
- mindsdb/integrations/handlers/litellm_handler/litellm_handler.py +10 -3
- mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +26 -33
- mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +74 -51
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +305 -98
- mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +53 -34
- mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +136 -6
- mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +334 -83
- mindsdb/integrations/libs/api_handler.py +261 -57
- mindsdb/integrations/libs/base.py +100 -29
- mindsdb/integrations/utilities/files/file_reader.py +99 -73
- mindsdb/integrations/utilities/handler_utils.py +23 -8
- mindsdb/integrations/utilities/sql_utils.py +35 -40
- mindsdb/interfaces/agents/agents_controller.py +196 -192
- mindsdb/interfaces/agents/constants.py +7 -1
- mindsdb/interfaces/agents/langchain_agent.py +42 -11
- mindsdb/interfaces/agents/mcp_client_agent.py +29 -21
- mindsdb/interfaces/data_catalog/__init__.py +0 -0
- mindsdb/interfaces/data_catalog/base_data_catalog.py +54 -0
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +359 -0
- mindsdb/interfaces/data_catalog/data_catalog_reader.py +34 -0
- mindsdb/interfaces/database/database.py +81 -57
- mindsdb/interfaces/database/integrations.py +220 -234
- mindsdb/interfaces/database/log.py +72 -104
- mindsdb/interfaces/database/projects.py +156 -193
- mindsdb/interfaces/file/file_controller.py +21 -65
- mindsdb/interfaces/knowledge_base/controller.py +63 -10
- mindsdb/interfaces/knowledge_base/evaluate.py +519 -0
- mindsdb/interfaces/knowledge_base/llm_client.py +75 -0
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_kb_tools.py +83 -43
- mindsdb/interfaces/skills/skills_controller.py +54 -36
- mindsdb/interfaces/skills/sql_agent.py +109 -86
- mindsdb/interfaces/storage/db.py +223 -79
- mindsdb/migrations/versions/2025-05-28_a44643042fe8_added_data_catalog_tables.py +118 -0
- mindsdb/migrations/versions/2025-06-09_608e376c19a7_updated_data_catalog_data_types.py +58 -0
- mindsdb/utilities/config.py +9 -2
- mindsdb/utilities/log.py +35 -26
- mindsdb/utilities/ml_task_queue/task.py +19 -22
- mindsdb/utilities/render/sqlalchemy_render.py +129 -181
- mindsdb/utilities/starters.py +49 -1
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/METADATA +268 -268
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/RECORD +70 -62
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import datetime
|
|
2
|
-
from typing import Dict, Iterator, List, Union, Tuple, Optional
|
|
2
|
+
from typing import Dict, Iterator, List, Union, Tuple, Optional, Any
|
|
3
|
+
import copy
|
|
3
4
|
|
|
4
5
|
from langchain_core.tools import BaseTool
|
|
5
6
|
from sqlalchemy.orm.attributes import flag_modified
|
|
@@ -14,18 +15,20 @@ from mindsdb.interfaces.model.functions import PredictorRecordNotFound
|
|
|
14
15
|
from mindsdb.interfaces.model.model_controller import ModelController
|
|
15
16
|
from mindsdb.interfaces.skills.skills_controller import SkillsController
|
|
16
17
|
from mindsdb.utilities.config import config
|
|
18
|
+
from mindsdb.utilities import log
|
|
19
|
+
|
|
17
20
|
from mindsdb.utilities.exception import EntityExistsError, EntityNotExistsError
|
|
18
21
|
|
|
19
|
-
from .constants import ASSISTANT_COLUMN, SUPPORTED_PROVIDERS, PROVIDER_TO_MODELS
|
|
22
|
+
from .constants import ASSISTANT_COLUMN, SUPPORTED_PROVIDERS, PROVIDER_TO_MODELS, DEFAULT_TEXT2SQL_DATABASE
|
|
20
23
|
from .langchain_agent import get_llm_provider
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
logger = log.getLogger(__name__)
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
default_project = config.get("default_project")
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class AgentsController:
|
|
28
|
-
|
|
31
|
+
"""Handles CRUD operations at the database level for Agents"""
|
|
29
32
|
|
|
30
33
|
assistant_column = ASSISTANT_COLUMN
|
|
31
34
|
|
|
@@ -33,7 +36,7 @@ class AgentsController:
|
|
|
33
36
|
self,
|
|
34
37
|
project_controller: ProjectController = None,
|
|
35
38
|
skills_controller: SkillsController = None,
|
|
36
|
-
model_controller: ModelController = None
|
|
39
|
+
model_controller: ModelController = None,
|
|
37
40
|
):
|
|
38
41
|
if project_controller is None:
|
|
39
42
|
project_controller = ProjectController()
|
|
@@ -46,7 +49,7 @@ class AgentsController:
|
|
|
46
49
|
self.model_controller = model_controller
|
|
47
50
|
|
|
48
51
|
def check_model_provider(self, model_name: str, provider: str = None) -> Tuple[dict, str]:
|
|
49
|
-
|
|
52
|
+
"""
|
|
50
53
|
Checks if a model exists, and gets the provider of the model.
|
|
51
54
|
|
|
52
55
|
The provider is either the provider of the model, or the provider given as an argument.
|
|
@@ -58,25 +61,29 @@ class AgentsController:
|
|
|
58
61
|
Returns:
|
|
59
62
|
model (dict): The model object
|
|
60
63
|
provider (str): The provider of the model
|
|
61
|
-
|
|
64
|
+
"""
|
|
62
65
|
model = None
|
|
63
66
|
|
|
67
|
+
# Handle the case when model_name is None (using default LLM)
|
|
68
|
+
if model_name is None:
|
|
69
|
+
return model, provider
|
|
70
|
+
|
|
64
71
|
try:
|
|
65
72
|
model_name_no_version, model_version = Predictor.get_name_and_version(model_name)
|
|
66
73
|
model = self.model_controller.get_model(model_name_no_version, version=model_version)
|
|
67
|
-
provider =
|
|
74
|
+
provider = "mindsdb" if model.get("provider") is None else model.get("provider")
|
|
68
75
|
except PredictorRecordNotFound:
|
|
69
76
|
if not provider:
|
|
70
77
|
# If provider is not given, get it from the model name
|
|
71
78
|
provider = get_llm_provider({"model_name": model_name})
|
|
72
79
|
|
|
73
80
|
elif provider not in SUPPORTED_PROVIDERS and model_name not in PROVIDER_TO_MODELS.get(provider, []):
|
|
74
|
-
raise ValueError(f
|
|
81
|
+
raise ValueError(f"Model with name does not exist for provider {provider}: {model_name}")
|
|
75
82
|
|
|
76
83
|
return model, provider
|
|
77
84
|
|
|
78
85
|
def get_agent(self, agent_name: str, project_name: str = default_project) -> Optional[db.Agents]:
|
|
79
|
-
|
|
86
|
+
"""
|
|
80
87
|
Gets an agent by name.
|
|
81
88
|
|
|
82
89
|
Parameters:
|
|
@@ -85,19 +92,19 @@ class AgentsController:
|
|
|
85
92
|
|
|
86
93
|
Returns:
|
|
87
94
|
agent (Optional[db.Agents]): The database agent object
|
|
88
|
-
|
|
95
|
+
"""
|
|
89
96
|
|
|
90
97
|
project = self.project_controller.get(name=project_name)
|
|
91
98
|
agent = db.Agents.query.filter(
|
|
92
99
|
db.Agents.name == agent_name,
|
|
93
100
|
db.Agents.project_id == project.id,
|
|
94
101
|
db.Agents.company_id == ctx.company_id,
|
|
95
|
-
db.Agents.deleted_at == null()
|
|
102
|
+
db.Agents.deleted_at == null(),
|
|
96
103
|
).first()
|
|
97
104
|
return agent
|
|
98
105
|
|
|
99
106
|
def get_agent_by_id(self, id: int, project_name: str = default_project) -> db.Agents:
|
|
100
|
-
|
|
107
|
+
"""
|
|
101
108
|
Gets an agent by id.
|
|
102
109
|
|
|
103
110
|
Parameters:
|
|
@@ -106,14 +113,14 @@ class AgentsController:
|
|
|
106
113
|
|
|
107
114
|
Returns:
|
|
108
115
|
agent (db.Agents): The database agent object
|
|
109
|
-
|
|
116
|
+
"""
|
|
110
117
|
|
|
111
118
|
project = self.project_controller.get(name=project_name)
|
|
112
119
|
agent = db.Agents.query.filter(
|
|
113
120
|
db.Agents.id == id,
|
|
114
121
|
db.Agents.project_id == project.id,
|
|
115
122
|
db.Agents.company_id == ctx.company_id,
|
|
116
|
-
db.Agents.deleted_at == null()
|
|
123
|
+
db.Agents.deleted_at == null(),
|
|
117
124
|
).first()
|
|
118
125
|
return agent
|
|
119
126
|
|
|
@@ -128,10 +135,7 @@ class AgentsController:
|
|
|
128
135
|
all-agents (List[db.Agents]): List of database agent object
|
|
129
136
|
"""
|
|
130
137
|
|
|
131
|
-
all_agents = db.Agents.query.filter(
|
|
132
|
-
db.Agents.company_id == ctx.company_id,
|
|
133
|
-
db.Agents.deleted_at == null()
|
|
134
|
-
)
|
|
138
|
+
all_agents = db.Agents.query.filter(db.Agents.company_id == ctx.company_id, db.Agents.deleted_at == null())
|
|
135
139
|
|
|
136
140
|
if project_name is not None:
|
|
137
141
|
project = self.project_controller.get(name=project_name)
|
|
@@ -141,14 +145,15 @@ class AgentsController:
|
|
|
141
145
|
return all_agents.all()
|
|
142
146
|
|
|
143
147
|
def add_agent(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
self,
|
|
149
|
+
name: str,
|
|
150
|
+
project_name: str = None,
|
|
151
|
+
model_name: str = None,
|
|
152
|
+
skills: List[Union[str, dict]] = None,
|
|
153
|
+
provider: str = None,
|
|
154
|
+
params: Dict[str, Any] = None,
|
|
155
|
+
) -> db.Agents:
|
|
156
|
+
"""
|
|
152
157
|
Adds an agent to the database.
|
|
153
158
|
|
|
154
159
|
Parameters:
|
|
@@ -172,7 +177,7 @@ class AgentsController:
|
|
|
172
177
|
|
|
173
178
|
Raises:
|
|
174
179
|
ValueError: Agent with given name already exists, or skill/model with given name does not exist.
|
|
175
|
-
|
|
180
|
+
"""
|
|
176
181
|
if project_name is None:
|
|
177
182
|
project_name = default_project
|
|
178
183
|
project = self.project_controller.get(name=project_name)
|
|
@@ -180,126 +185,117 @@ class AgentsController:
|
|
|
180
185
|
agent = self.get_agent(name, project_name)
|
|
181
186
|
|
|
182
187
|
if agent is not None:
|
|
183
|
-
raise ValueError(f
|
|
188
|
+
raise ValueError(f"Agent with name already exists: {name}")
|
|
189
|
+
|
|
190
|
+
if model_name is not None:
|
|
191
|
+
_, provider = self.check_model_provider(model_name, provider)
|
|
192
|
+
|
|
193
|
+
# No need to copy params since we're not preserving the original reference
|
|
194
|
+
params = params or {}
|
|
184
195
|
|
|
185
|
-
|
|
196
|
+
if model_name is None:
|
|
197
|
+
logger.warning("'model_name' param is not provided. Using default global llm model at runtime.")
|
|
198
|
+
|
|
199
|
+
# If model_name is not provided, we use default global llm model at runtime
|
|
200
|
+
# Default parameters will be applied at runtime via get_agent_llm_params
|
|
201
|
+
# This allows global default updates to apply to all agents immediately
|
|
186
202
|
|
|
187
203
|
# Extract API key if provided in the format <provider>_api_key
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
if provider is not None:
|
|
205
|
+
provider_api_key_param = f"{provider.lower()}_api_key"
|
|
206
|
+
if provider_api_key_param in params:
|
|
207
|
+
# Keep the API key in params for the agent to use
|
|
208
|
+
# It will be picked up by get_api_key() in handler_utils.py
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
# Handle generic api_key parameter if provided
|
|
212
|
+
if "api_key" in params:
|
|
213
|
+
# Keep the generic API key in params for the agent to use
|
|
191
214
|
# It will be picked up by get_api_key() in handler_utils.py
|
|
192
215
|
pass
|
|
193
216
|
|
|
194
217
|
# Extract table and knowledge base parameters from params
|
|
195
|
-
database = params.pop(
|
|
196
|
-
knowledge_base_database = params.pop(
|
|
197
|
-
include_tables = params.pop(
|
|
198
|
-
ignore_tables = params.pop(
|
|
199
|
-
include_knowledge_bases = params.pop(
|
|
200
|
-
ignore_knowledge_bases = params.pop(
|
|
218
|
+
database = params.pop("database", None)
|
|
219
|
+
knowledge_base_database = params.pop("knowledge_base_database", DEFAULT_TEXT2SQL_DATABASE)
|
|
220
|
+
include_tables = params.pop("include_tables", None)
|
|
221
|
+
ignore_tables = params.pop("ignore_tables", None)
|
|
222
|
+
include_knowledge_bases = params.pop("include_knowledge_bases", None)
|
|
223
|
+
ignore_knowledge_bases = params.pop("ignore_knowledge_bases", None)
|
|
201
224
|
|
|
202
225
|
# Save the extracted parameters back to params for API responses only if they were explicitly provided
|
|
203
226
|
# or if they're needed for skills
|
|
204
227
|
need_params = include_tables or ignore_tables or include_knowledge_bases or ignore_knowledge_bases
|
|
205
228
|
|
|
206
|
-
if
|
|
207
|
-
params[
|
|
229
|
+
if "database" in params or need_params:
|
|
230
|
+
params["database"] = database
|
|
208
231
|
|
|
209
|
-
if
|
|
210
|
-
params[
|
|
232
|
+
if "knowledge_base_database" in params or include_knowledge_bases or ignore_knowledge_bases:
|
|
233
|
+
params["knowledge_base_database"] = knowledge_base_database
|
|
211
234
|
|
|
212
235
|
if include_tables is not None:
|
|
213
|
-
params[
|
|
236
|
+
params["include_tables"] = include_tables
|
|
214
237
|
if ignore_tables is not None:
|
|
215
|
-
params[
|
|
238
|
+
params["ignore_tables"] = ignore_tables
|
|
216
239
|
if include_knowledge_bases is not None:
|
|
217
|
-
params[
|
|
240
|
+
params["include_knowledge_bases"] = include_knowledge_bases
|
|
218
241
|
if ignore_knowledge_bases is not None:
|
|
219
|
-
params[
|
|
242
|
+
params["ignore_knowledge_bases"] = ignore_knowledge_bases
|
|
220
243
|
|
|
221
244
|
# Convert string parameters to lists if needed
|
|
222
245
|
if isinstance(include_tables, str):
|
|
223
|
-
include_tables = [t.strip() for t in include_tables.split(
|
|
246
|
+
include_tables = [t.strip() for t in include_tables.split(",")]
|
|
224
247
|
if isinstance(ignore_tables, str):
|
|
225
|
-
ignore_tables = [t.strip() for t in ignore_tables.split(
|
|
248
|
+
ignore_tables = [t.strip() for t in ignore_tables.split(",")]
|
|
226
249
|
if isinstance(include_knowledge_bases, str):
|
|
227
|
-
include_knowledge_bases = [kb.strip() for kb in include_knowledge_bases.split(
|
|
250
|
+
include_knowledge_bases = [kb.strip() for kb in include_knowledge_bases.split(",")]
|
|
228
251
|
if isinstance(ignore_knowledge_bases, str):
|
|
229
|
-
ignore_knowledge_bases = [kb.strip() for kb in ignore_knowledge_bases.split(
|
|
252
|
+
ignore_knowledge_bases = [kb.strip() for kb in ignore_knowledge_bases.split(",")]
|
|
230
253
|
|
|
231
254
|
# Auto-create SQL skill if no skills are provided but include_tables or include_knowledge_bases params are provided
|
|
232
255
|
if not skills and (include_tables or include_knowledge_bases):
|
|
233
|
-
# Determine database to use (default to 'mindsdb')
|
|
234
|
-
db_name = database
|
|
235
|
-
kb_db_name = knowledge_base_database
|
|
236
|
-
|
|
237
|
-
# If database is not explicitly provided but tables are, try to extract the database name from the first table
|
|
238
|
-
if not database and include_tables and len(include_tables) > 0:
|
|
239
|
-
parts = include_tables[0].split('.')
|
|
240
|
-
if len(parts) >= 2:
|
|
241
|
-
db_name = parts[0]
|
|
242
|
-
elif not database and ignore_tables and len(ignore_tables) > 0:
|
|
243
|
-
parts = ignore_tables[0].split('.')
|
|
244
|
-
if len(parts) >= 2:
|
|
245
|
-
db_name = parts[0]
|
|
246
|
-
|
|
247
256
|
# Create a default SQL skill
|
|
248
257
|
skill_name = f"{name}_sql_skill"
|
|
249
258
|
skill_params = {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
259
|
+
"type": "sql",
|
|
260
|
+
"database": database,
|
|
261
|
+
"description": f"Auto-generated SQL skill for agent {name}",
|
|
253
262
|
}
|
|
254
263
|
|
|
255
|
-
# Add
|
|
256
|
-
if
|
|
257
|
-
skill_params[
|
|
264
|
+
# Add table restrictions if provided
|
|
265
|
+
if include_tables:
|
|
266
|
+
skill_params["include_tables"] = include_tables
|
|
267
|
+
if ignore_tables:
|
|
268
|
+
skill_params["ignore_tables"] = ignore_tables
|
|
258
269
|
|
|
259
270
|
# Add knowledge base parameters if provided
|
|
271
|
+
if knowledge_base_database:
|
|
272
|
+
skill_params["knowledge_base_database"] = knowledge_base_database
|
|
260
273
|
if include_knowledge_bases:
|
|
261
|
-
skill_params[
|
|
274
|
+
skill_params["include_knowledge_bases"] = include_knowledge_bases
|
|
262
275
|
if ignore_knowledge_bases:
|
|
263
|
-
skill_params[
|
|
264
|
-
|
|
276
|
+
skill_params["ignore_knowledge_bases"] = ignore_knowledge_bases
|
|
265
277
|
try:
|
|
266
278
|
# Check if skill already exists
|
|
267
279
|
existing_skill = self.skills_controller.get_skill(skill_name, project_name)
|
|
268
280
|
if existing_skill is None:
|
|
269
281
|
# Create the skill
|
|
270
|
-
skill_type = skill_params.pop(
|
|
282
|
+
skill_type = skill_params.pop("type")
|
|
271
283
|
self.skills_controller.add_skill(
|
|
272
|
-
name=skill_name,
|
|
273
|
-
project_name=project_name,
|
|
274
|
-
type=skill_type,
|
|
275
|
-
params=skill_params
|
|
284
|
+
name=skill_name, project_name=project_name, type=skill_type, params=skill_params
|
|
276
285
|
)
|
|
277
286
|
else:
|
|
278
287
|
# Update the skill if parameters have changed
|
|
279
288
|
params_changed = False
|
|
280
289
|
|
|
281
|
-
# Check if
|
|
282
|
-
|
|
283
|
-
existing_skill.params
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
# Check if knowledge base database has changed
|
|
287
|
-
if knowledge_base_database and existing_skill.params.get('knowledge_base_database') != kb_db_name:
|
|
288
|
-
existing_skill.params['knowledge_base_database'] = kb_db_name
|
|
289
|
-
params_changed = True
|
|
290
|
-
|
|
291
|
-
# Check if knowledge base parameters have changed
|
|
292
|
-
if include_knowledge_bases and existing_skill.params.get('include_knowledge_bases') != include_knowledge_bases:
|
|
293
|
-
existing_skill.params['include_knowledge_bases'] = include_knowledge_bases
|
|
294
|
-
params_changed = True
|
|
295
|
-
|
|
296
|
-
if ignore_knowledge_bases and existing_skill.params.get('ignore_knowledge_bases') != ignore_knowledge_bases:
|
|
297
|
-
existing_skill.params['ignore_knowledge_bases'] = ignore_knowledge_bases
|
|
298
|
-
params_changed = True
|
|
290
|
+
# Check if skill parameters need to be updated
|
|
291
|
+
for param_key, param_value in skill_params.items():
|
|
292
|
+
if existing_skill.params.get(param_key) != param_value:
|
|
293
|
+
existing_skill.params[param_key] = param_value
|
|
294
|
+
params_changed = True
|
|
299
295
|
|
|
300
296
|
# Update the skill if needed
|
|
301
297
|
if params_changed:
|
|
302
|
-
flag_modified(existing_skill,
|
|
298
|
+
flag_modified(existing_skill, "params")
|
|
303
299
|
db.session.commit()
|
|
304
300
|
|
|
305
301
|
skills = [skill_name]
|
|
@@ -322,49 +318,45 @@ class AgentsController:
|
|
|
322
318
|
parameters = {}
|
|
323
319
|
else:
|
|
324
320
|
parameters = skill.copy()
|
|
325
|
-
skill_name = parameters.pop(
|
|
321
|
+
skill_name = parameters.pop("name")
|
|
326
322
|
|
|
327
323
|
existing_skill = self.skills_controller.get_skill(skill_name, project_name)
|
|
328
324
|
if existing_skill is None:
|
|
329
325
|
db.session.rollback()
|
|
330
|
-
raise ValueError(f
|
|
326
|
+
raise ValueError(f"Skill with name does not exist: {skill_name}")
|
|
331
327
|
|
|
332
328
|
# Add table restrictions if this is a text2sql skill
|
|
333
|
-
if existing_skill.type ==
|
|
334
|
-
parameters[
|
|
329
|
+
if existing_skill.type == "sql" and (include_tables or ignore_tables):
|
|
330
|
+
parameters["tables"] = include_tables or ignore_tables
|
|
335
331
|
|
|
336
332
|
# Add knowledge base restrictions if this is a text2sql skill
|
|
337
|
-
if existing_skill.type ==
|
|
333
|
+
if existing_skill.type == "sql":
|
|
338
334
|
# Pass database parameter if provided
|
|
339
|
-
if database and
|
|
340
|
-
parameters[
|
|
335
|
+
if database and "database" not in parameters:
|
|
336
|
+
parameters["database"] = database
|
|
341
337
|
|
|
342
338
|
# Pass knowledge base database parameter if provided
|
|
343
|
-
if knowledge_base_database and
|
|
344
|
-
parameters[
|
|
339
|
+
if knowledge_base_database and "knowledge_base_database" not in parameters:
|
|
340
|
+
parameters["knowledge_base_database"] = knowledge_base_database
|
|
345
341
|
|
|
346
342
|
# Add knowledge base parameters to both the skill and the association parameters
|
|
347
343
|
if include_knowledge_bases:
|
|
348
|
-
parameters[
|
|
349
|
-
if
|
|
350
|
-
existing_skill.params[
|
|
351
|
-
flag_modified(existing_skill,
|
|
344
|
+
parameters["include_knowledge_bases"] = include_knowledge_bases
|
|
345
|
+
if "include_knowledge_bases" not in existing_skill.params:
|
|
346
|
+
existing_skill.params["include_knowledge_bases"] = include_knowledge_bases
|
|
347
|
+
flag_modified(existing_skill, "params")
|
|
352
348
|
elif ignore_knowledge_bases:
|
|
353
|
-
parameters[
|
|
354
|
-
if
|
|
355
|
-
existing_skill.params[
|
|
356
|
-
flag_modified(existing_skill,
|
|
349
|
+
parameters["ignore_knowledge_bases"] = ignore_knowledge_bases
|
|
350
|
+
if "ignore_knowledge_bases" not in existing_skill.params:
|
|
351
|
+
existing_skill.params["ignore_knowledge_bases"] = ignore_knowledge_bases
|
|
352
|
+
flag_modified(existing_skill, "params")
|
|
357
353
|
|
|
358
354
|
# Ensure knowledge_base_database is set in the skill's params
|
|
359
|
-
if knowledge_base_database and
|
|
360
|
-
existing_skill.params[
|
|
361
|
-
flag_modified(existing_skill,
|
|
362
|
-
|
|
363
|
-
association = db.AgentSkillsAssociation(
|
|
364
|
-
parameters=parameters,
|
|
365
|
-
agent=agent,
|
|
366
|
-
skill=existing_skill
|
|
367
|
-
)
|
|
355
|
+
if knowledge_base_database and "knowledge_base_database" not in existing_skill.params:
|
|
356
|
+
existing_skill.params["knowledge_base_database"] = knowledge_base_database
|
|
357
|
+
flag_modified(existing_skill, "params")
|
|
358
|
+
|
|
359
|
+
association = db.AgentSkillsAssociation(parameters=parameters, agent=agent, skill=existing_skill)
|
|
368
360
|
db.session.add(association)
|
|
369
361
|
|
|
370
362
|
db.session.add(agent)
|
|
@@ -373,17 +365,18 @@ class AgentsController:
|
|
|
373
365
|
return agent
|
|
374
366
|
|
|
375
367
|
def update_agent(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
368
|
+
self,
|
|
369
|
+
agent_name: str,
|
|
370
|
+
project_name: str = default_project,
|
|
371
|
+
name: str = None,
|
|
372
|
+
model_name: str = None,
|
|
373
|
+
skills_to_add: List[Union[str, dict]] = None,
|
|
374
|
+
skills_to_remove: List[str] = None,
|
|
375
|
+
skills_to_rewrite: List[Union[str, dict]] = None,
|
|
376
|
+
provider: str = None,
|
|
377
|
+
params: Dict[str, str] = None,
|
|
378
|
+
):
|
|
379
|
+
"""
|
|
387
380
|
Updates an agent in the database.
|
|
388
381
|
|
|
389
382
|
Parameters:
|
|
@@ -405,7 +398,7 @@ class AgentsController:
|
|
|
405
398
|
EntityExistsError: if agent with new name already exists
|
|
406
399
|
EntityNotExistsError: if agent with name or skill not found
|
|
407
400
|
ValueError: if conflict in skills list
|
|
408
|
-
|
|
401
|
+
"""
|
|
409
402
|
|
|
410
403
|
skills_to_add = skills_to_add or []
|
|
411
404
|
skills_to_remove = skills_to_remove or []
|
|
@@ -418,15 +411,13 @@ class AgentsController:
|
|
|
418
411
|
|
|
419
412
|
existing_agent = self.get_agent(agent_name, project_name=project_name)
|
|
420
413
|
if existing_agent is None:
|
|
421
|
-
raise EntityNotExistsError(f
|
|
422
|
-
is_demo = (existing_agent.params or {}).get(
|
|
423
|
-
if (
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
or (isinstance(params, dict) and len(params) > 0 and 'prompt_template' not in params)
|
|
429
|
-
)
|
|
414
|
+
raise EntityNotExistsError(f"Agent with name not found: {agent_name}")
|
|
415
|
+
is_demo = (existing_agent.params or {}).get("is_demo", False)
|
|
416
|
+
if is_demo and (
|
|
417
|
+
(name is not None and name != agent_name)
|
|
418
|
+
or (model_name is not None and existing_agent.model_name != model_name)
|
|
419
|
+
or (provider is not None and existing_agent.provider != provider)
|
|
420
|
+
or (isinstance(params, dict) and len(params) > 0 and "prompt_template" not in params)
|
|
430
421
|
):
|
|
431
422
|
raise ValueError("It is forbidden to change properties of the demo object")
|
|
432
423
|
|
|
@@ -434,7 +425,7 @@ class AgentsController:
|
|
|
434
425
|
# Check to see if updated name already exists
|
|
435
426
|
agent_with_new_name = self.get_agent(name, project_name=project_name)
|
|
436
427
|
if agent_with_new_name is not None:
|
|
437
|
-
raise EntityExistsError(f
|
|
428
|
+
raise EntityExistsError(f"Agent with updated name already exists: {name}")
|
|
438
429
|
existing_agent.name = name
|
|
439
430
|
|
|
440
431
|
if model_name or provider:
|
|
@@ -446,21 +437,21 @@ class AgentsController:
|
|
|
446
437
|
|
|
447
438
|
# check that all skills exist
|
|
448
439
|
skill_name_to_record_map = {}
|
|
449
|
-
for skill_meta in
|
|
450
|
-
skill_name = skill_meta[
|
|
440
|
+
for skill_meta in skills_to_add + skills_to_remove + skills_to_rewrite:
|
|
441
|
+
skill_name = skill_meta["name"] if isinstance(skill_meta, dict) else skill_meta
|
|
451
442
|
if skill_name not in skill_name_to_record_map:
|
|
452
443
|
skill_record = self.skills_controller.get_skill(skill_name, project_name)
|
|
453
444
|
if skill_record is None:
|
|
454
|
-
raise EntityNotExistsError(f
|
|
445
|
+
raise EntityNotExistsError(f"Skill with name does not exist: {skill_name}")
|
|
455
446
|
skill_name_to_record_map[skill_name] = skill_record
|
|
456
447
|
|
|
457
448
|
if len(skills_to_add) > 0 or len(skills_to_remove) > 0:
|
|
458
|
-
skills_to_add = [{
|
|
459
|
-
skills_to_add_names = [x[
|
|
449
|
+
skills_to_add = [{"name": x} if isinstance(x, str) else x for x in skills_to_add]
|
|
450
|
+
skills_to_add_names = [x["name"] for x in skills_to_add]
|
|
460
451
|
|
|
461
452
|
# there are no intersection between lists
|
|
462
453
|
if not set(skills_to_add_names).isdisjoint(set(skills_to_remove)):
|
|
463
|
-
raise ValueError(
|
|
454
|
+
raise ValueError("Conflict between skills to add and skills to remove.")
|
|
464
455
|
|
|
465
456
|
existing_agent_skills_names = [rel.skill.name for rel in existing_agent.skills_relationships]
|
|
466
457
|
|
|
@@ -471,19 +462,17 @@ class AgentsController:
|
|
|
471
462
|
db.session.delete(rel)
|
|
472
463
|
|
|
473
464
|
# add skills
|
|
474
|
-
for skill_name in
|
|
475
|
-
skill_parameters = next(x for x in skills_to_add if x[
|
|
476
|
-
del skill_parameters[
|
|
465
|
+
for skill_name in set(skills_to_add_names) - set(existing_agent_skills_names):
|
|
466
|
+
skill_parameters = next(x for x in skills_to_add if x["name"] == skill_name).copy()
|
|
467
|
+
del skill_parameters["name"]
|
|
477
468
|
association = db.AgentSkillsAssociation(
|
|
478
|
-
parameters=skill_parameters,
|
|
479
|
-
agent=existing_agent,
|
|
480
|
-
skill=skill_name_to_record_map[skill_name]
|
|
469
|
+
parameters=skill_parameters, agent=existing_agent, skill=skill_name_to_record_map[skill_name]
|
|
481
470
|
)
|
|
482
471
|
db.session.add(association)
|
|
483
472
|
elif len(skills_to_rewrite) > 0:
|
|
484
|
-
skill_name_to_parameters = {
|
|
485
|
-
k: v for k, v in x.items() if k !=
|
|
486
|
-
}
|
|
473
|
+
skill_name_to_parameters = {
|
|
474
|
+
x["name"]: {k: v for k, v in x.items() if k != "name"} for x in skills_to_rewrite
|
|
475
|
+
}
|
|
487
476
|
existing_skill_names = set()
|
|
488
477
|
for rel in existing_agent.skills_relationships:
|
|
489
478
|
if rel.skill.name not in skill_name_to_parameters:
|
|
@@ -491,12 +480,12 @@ class AgentsController:
|
|
|
491
480
|
else:
|
|
492
481
|
existing_skill_names.add(rel.skill.name)
|
|
493
482
|
rel.parameters = skill_name_to_parameters[rel.skill.name]
|
|
494
|
-
flag_modified(rel,
|
|
495
|
-
for new_skill_name in
|
|
483
|
+
flag_modified(rel, "parameters")
|
|
484
|
+
for new_skill_name in set(skill_name_to_parameters) - existing_skill_names:
|
|
496
485
|
association = db.AgentSkillsAssociation(
|
|
497
486
|
parameters=skill_name_to_parameters[new_skill_name],
|
|
498
487
|
agent=existing_agent,
|
|
499
|
-
skill=skill_name_to_record_map[new_skill_name]
|
|
488
|
+
skill=skill_name_to_record_map[new_skill_name],
|
|
500
489
|
)
|
|
501
490
|
db.session.add(association)
|
|
502
491
|
|
|
@@ -509,13 +498,13 @@ class AgentsController:
|
|
|
509
498
|
existing_agent.params = params
|
|
510
499
|
# Some versions of SQL Alchemy won't handle JSON updates correctly without this.
|
|
511
500
|
# See: https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.attributes.flag_modified
|
|
512
|
-
flag_modified(existing_agent,
|
|
501
|
+
flag_modified(existing_agent, "params")
|
|
513
502
|
db.session.commit()
|
|
514
503
|
|
|
515
504
|
return existing_agent
|
|
516
505
|
|
|
517
506
|
def delete_agent(self, agent_name: str, project_name: str = default_project):
|
|
518
|
-
|
|
507
|
+
"""
|
|
519
508
|
Deletes an agent by name.
|
|
520
509
|
|
|
521
510
|
Parameters:
|
|
@@ -524,23 +513,36 @@ class AgentsController:
|
|
|
524
513
|
|
|
525
514
|
Raises:
|
|
526
515
|
ValueError: Agent does not exist.
|
|
527
|
-
|
|
516
|
+
"""
|
|
528
517
|
|
|
529
518
|
agent = self.get_agent(agent_name, project_name)
|
|
530
519
|
if agent is None:
|
|
531
|
-
raise ValueError(f
|
|
532
|
-
if isinstance(agent.params, dict) and agent.params.get(
|
|
533
|
-
raise ValueError(
|
|
520
|
+
raise ValueError(f"Agent with name does not exist: {agent_name}")
|
|
521
|
+
if isinstance(agent.params, dict) and agent.params.get("is_demo") is True:
|
|
522
|
+
raise ValueError("Unable to delete demo object")
|
|
534
523
|
agent.deleted_at = datetime.datetime.now()
|
|
535
524
|
db.session.commit()
|
|
536
525
|
|
|
526
|
+
def get_agent_llm_params(self, model_params: dict):
|
|
527
|
+
"""
|
|
528
|
+
Get agent LLM parameters by combining default config with user provided parameters.
|
|
529
|
+
Similar to how knowledge bases handle default parameters.
|
|
530
|
+
"""
|
|
531
|
+
combined_model_params = copy.deepcopy(config.get("default_llm", {}))
|
|
532
|
+
|
|
533
|
+
if model_params:
|
|
534
|
+
combined_model_params.update(model_params)
|
|
535
|
+
|
|
536
|
+
return combined_model_params
|
|
537
|
+
|
|
537
538
|
def get_completion(
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
539
|
+
self,
|
|
540
|
+
agent: db.Agents,
|
|
541
|
+
messages: List[Dict[str, str]],
|
|
542
|
+
project_name: str = default_project,
|
|
543
|
+
tools: List[BaseTool] = None,
|
|
544
|
+
stream: bool = False,
|
|
545
|
+
) -> Union[Iterator[object], pd.DataFrame]:
|
|
544
546
|
"""
|
|
545
547
|
Queries an agent to get a completion.
|
|
546
548
|
|
|
@@ -558,12 +560,7 @@ class AgentsController:
|
|
|
558
560
|
ValueError: Agent's model does not exist.
|
|
559
561
|
"""
|
|
560
562
|
if stream:
|
|
561
|
-
return self._get_completion_stream(
|
|
562
|
-
agent,
|
|
563
|
-
messages,
|
|
564
|
-
project_name=project_name,
|
|
565
|
-
tools=tools
|
|
566
|
-
)
|
|
563
|
+
return self._get_completion_stream(agent, messages, project_name=project_name, tools=tools)
|
|
567
564
|
from .langchain_agent import LangchainAgent
|
|
568
565
|
|
|
569
566
|
model, provider = self.check_model_provider(agent.model_name, agent.provider)
|
|
@@ -572,16 +569,20 @@ class AgentsController:
|
|
|
572
569
|
agent.provider = provider
|
|
573
570
|
db.session.commit()
|
|
574
571
|
|
|
575
|
-
|
|
572
|
+
# Get agent parameters and combine with default LLM parameters at runtime
|
|
573
|
+
agent_params = self.get_agent_llm_params(agent.params)
|
|
574
|
+
|
|
575
|
+
lang_agent = LangchainAgent(agent, model, params=agent_params)
|
|
576
576
|
return lang_agent.get_completion(messages)
|
|
577
577
|
|
|
578
578
|
def _get_completion_stream(
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
579
|
+
self,
|
|
580
|
+
agent: db.Agents,
|
|
581
|
+
messages: List[Dict[str, str]],
|
|
582
|
+
project_name: str = default_project,
|
|
583
|
+
tools: List[BaseTool] = None,
|
|
584
|
+
) -> Iterator[object]:
|
|
585
|
+
"""
|
|
585
586
|
Queries an agent to get a stream of completion chunks.
|
|
586
587
|
|
|
587
588
|
Parameters:
|
|
@@ -597,7 +598,7 @@ class AgentsController:
|
|
|
597
598
|
|
|
598
599
|
Raises:
|
|
599
600
|
ValueError: Agent's model does not exist.
|
|
600
|
-
|
|
601
|
+
"""
|
|
601
602
|
# For circular dependency.
|
|
602
603
|
from .langchain_agent import LangchainAgent
|
|
603
604
|
|
|
@@ -608,5 +609,8 @@ class AgentsController:
|
|
|
608
609
|
agent.provider = provider
|
|
609
610
|
db.session.commit()
|
|
610
611
|
|
|
611
|
-
|
|
612
|
+
# Get agent parameters and combine with default LLM parameters at runtime
|
|
613
|
+
agent_params = self.get_agent_llm_params(agent.params)
|
|
614
|
+
|
|
615
|
+
lang_agent = LangchainAgent(agent, model=model, params=agent_params)
|
|
612
616
|
return lang_agent.get_completion(messages, stream=True)
|