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.

Files changed (70) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/api/a2a/agent.py +28 -25
  3. mindsdb/api/a2a/common/server/server.py +32 -26
  4. mindsdb/api/a2a/run_a2a.py +1 -1
  5. mindsdb/api/executor/command_executor.py +69 -14
  6. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +49 -65
  7. mindsdb/api/executor/datahub/datanodes/project_datanode.py +29 -48
  8. mindsdb/api/executor/datahub/datanodes/system_tables.py +35 -61
  9. mindsdb/api/executor/planner/plan_join.py +67 -77
  10. mindsdb/api/executor/planner/query_planner.py +176 -155
  11. mindsdb/api/executor/planner/steps.py +37 -12
  12. mindsdb/api/executor/sql_query/result_set.py +45 -64
  13. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +14 -18
  14. mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +17 -18
  15. mindsdb/api/executor/sql_query/steps/insert_step.py +13 -33
  16. mindsdb/api/executor/sql_query/steps/subselect_step.py +43 -35
  17. mindsdb/api/executor/utilities/sql.py +42 -48
  18. mindsdb/api/http/namespaces/config.py +1 -1
  19. mindsdb/api/http/namespaces/file.py +14 -23
  20. mindsdb/api/mysql/mysql_proxy/data_types/mysql_datum.py +12 -28
  21. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/binary_resultset_row_package.py +59 -50
  22. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/resultset_row_package.py +9 -8
  23. mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +449 -461
  24. mindsdb/api/mysql/mysql_proxy/utilities/dump.py +87 -36
  25. mindsdb/integrations/handlers/file_handler/file_handler.py +15 -9
  26. mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py +43 -24
  27. mindsdb/integrations/handlers/litellm_handler/litellm_handler.py +10 -3
  28. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +26 -33
  29. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +74 -51
  30. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +305 -98
  31. mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +53 -34
  32. mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +136 -6
  33. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +334 -83
  34. mindsdb/integrations/libs/api_handler.py +261 -57
  35. mindsdb/integrations/libs/base.py +100 -29
  36. mindsdb/integrations/utilities/files/file_reader.py +99 -73
  37. mindsdb/integrations/utilities/handler_utils.py +23 -8
  38. mindsdb/integrations/utilities/sql_utils.py +35 -40
  39. mindsdb/interfaces/agents/agents_controller.py +196 -192
  40. mindsdb/interfaces/agents/constants.py +7 -1
  41. mindsdb/interfaces/agents/langchain_agent.py +42 -11
  42. mindsdb/interfaces/agents/mcp_client_agent.py +29 -21
  43. mindsdb/interfaces/data_catalog/__init__.py +0 -0
  44. mindsdb/interfaces/data_catalog/base_data_catalog.py +54 -0
  45. mindsdb/interfaces/data_catalog/data_catalog_loader.py +359 -0
  46. mindsdb/interfaces/data_catalog/data_catalog_reader.py +34 -0
  47. mindsdb/interfaces/database/database.py +81 -57
  48. mindsdb/interfaces/database/integrations.py +220 -234
  49. mindsdb/interfaces/database/log.py +72 -104
  50. mindsdb/interfaces/database/projects.py +156 -193
  51. mindsdb/interfaces/file/file_controller.py +21 -65
  52. mindsdb/interfaces/knowledge_base/controller.py +63 -10
  53. mindsdb/interfaces/knowledge_base/evaluate.py +519 -0
  54. mindsdb/interfaces/knowledge_base/llm_client.py +75 -0
  55. mindsdb/interfaces/skills/custom/text2sql/mindsdb_kb_tools.py +83 -43
  56. mindsdb/interfaces/skills/skills_controller.py +54 -36
  57. mindsdb/interfaces/skills/sql_agent.py +109 -86
  58. mindsdb/interfaces/storage/db.py +223 -79
  59. mindsdb/migrations/versions/2025-05-28_a44643042fe8_added_data_catalog_tables.py +118 -0
  60. mindsdb/migrations/versions/2025-06-09_608e376c19a7_updated_data_catalog_data_types.py +58 -0
  61. mindsdb/utilities/config.py +9 -2
  62. mindsdb/utilities/log.py +35 -26
  63. mindsdb/utilities/ml_task_queue/task.py +19 -22
  64. mindsdb/utilities/render/sqlalchemy_render.py +129 -181
  65. mindsdb/utilities/starters.py +49 -1
  66. {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/METADATA +268 -268
  67. {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/RECORD +70 -62
  68. {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/WHEEL +0 -0
  69. {mindsdb-25.5.4.1.dist-info → mindsdb-25.6.2.0.dist-info}/licenses/LICENSE +0 -0
  70. {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
- default_project = config.get('default_project')
25
+ logger = log.getLogger(__name__)
23
26
 
24
- DEFAULT_TEXT2SQL_DATABASE = 'mindsdb'
27
+ default_project = config.get("default_project")
25
28
 
26
29
 
27
30
  class AgentsController:
28
- '''Handles CRUD operations at the database level for Agents'''
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 = 'mindsdb' if model.get('provider') is None else model.get('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'Model with name does not exist for provider {provider}: {model_name}')
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
- self,
145
- name: str,
146
- project_name: str,
147
- model_name: str,
148
- skills: List[Union[str, dict]],
149
- provider: str = None,
150
- params: Dict[str, str] = {}) -> db.Agents:
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'Agent with name already exists: {name}')
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
- _, provider = self.check_model_provider(model_name, provider)
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
- provider_api_key_param = f"{provider.lower()}_api_key"
189
- if provider_api_key_param in params:
190
- # Keep the API key in params for the agent to use
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('database', None)
196
- knowledge_base_database = params.pop('knowledge_base_database', DEFAULT_TEXT2SQL_DATABASE)
197
- include_tables = params.pop('include_tables', None)
198
- ignore_tables = params.pop('ignore_tables', None)
199
- include_knowledge_bases = params.pop('include_knowledge_bases', None)
200
- ignore_knowledge_bases = params.pop('ignore_knowledge_bases', None)
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 'database' in params or need_params:
207
- params['database'] = database
229
+ if "database" in params or need_params:
230
+ params["database"] = database
208
231
 
209
- if 'knowledge_base_database' in params or include_knowledge_bases or ignore_knowledge_bases:
210
- params['knowledge_base_database'] = knowledge_base_database
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['include_tables'] = include_tables
236
+ params["include_tables"] = include_tables
214
237
  if ignore_tables is not None:
215
- params['ignore_tables'] = ignore_tables
238
+ params["ignore_tables"] = ignore_tables
216
239
  if include_knowledge_bases is not None:
217
- params['include_knowledge_bases'] = include_knowledge_bases
240
+ params["include_knowledge_bases"] = include_knowledge_bases
218
241
  if ignore_knowledge_bases is not None:
219
- params['ignore_knowledge_bases'] = ignore_knowledge_bases
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
- 'type': 'sql',
251
- 'database': db_name,
252
- 'description': f'Auto-generated SQL skill for agent {name}'
259
+ "type": "sql",
260
+ "database": database,
261
+ "description": f"Auto-generated SQL skill for agent {name}",
253
262
  }
254
263
 
255
- # Add knowledge base database if provided
256
- if knowledge_base_database:
257
- skill_params['knowledge_base_database'] = knowledge_base_database
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['include_knowledge_bases'] = include_knowledge_bases
274
+ skill_params["include_knowledge_bases"] = include_knowledge_bases
262
275
  if ignore_knowledge_bases:
263
- skill_params['ignore_knowledge_bases'] = ignore_knowledge_bases
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('type')
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 database has changed
282
- if existing_skill.params.get('database') != db_name:
283
- existing_skill.params['database'] = db_name
284
- params_changed = True
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, 'params')
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('name')
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'Skill with name does not exist: {skill_name}')
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 == 'sql' and (include_tables or ignore_tables):
334
- parameters['tables'] = include_tables or ignore_tables
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 == 'sql':
333
+ if existing_skill.type == "sql":
338
334
  # Pass database parameter if provided
339
- if database and 'database' not in parameters:
340
- parameters['database'] = database
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 'knowledge_base_database' not in parameters:
344
- parameters['knowledge_base_database'] = knowledge_base_database
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['include_knowledge_bases'] = include_knowledge_bases
349
- if 'include_knowledge_bases' not in existing_skill.params:
350
- existing_skill.params['include_knowledge_bases'] = include_knowledge_bases
351
- flag_modified(existing_skill, 'params')
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['ignore_knowledge_bases'] = ignore_knowledge_bases
354
- if 'ignore_knowledge_bases' not in existing_skill.params:
355
- existing_skill.params['ignore_knowledge_bases'] = ignore_knowledge_bases
356
- flag_modified(existing_skill, 'params')
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 'knowledge_base_database' not in existing_skill.params:
360
- existing_skill.params['knowledge_base_database'] = knowledge_base_database
361
- flag_modified(existing_skill, 'params')
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
- self,
377
- agent_name: str,
378
- project_name: str = default_project,
379
- name: str = None,
380
- model_name: str = None,
381
- skills_to_add: List[Union[str, dict]] = None,
382
- skills_to_remove: List[str] = None,
383
- skills_to_rewrite: List[Union[str, dict]] = None,
384
- provider: str = None,
385
- params: Dict[str, str] = None):
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'Agent with name not found: {agent_name}')
422
- is_demo = (existing_agent.params or {}).get('is_demo', False)
423
- if (
424
- is_demo and (
425
- (name is not None and name != agent_name)
426
- or (model_name is not None and existing_agent.model_name != model_name)
427
- or (provider is not None and existing_agent.provider != provider)
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'Agent with updated name already exists: {name}')
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 (skills_to_add + skills_to_remove + skills_to_rewrite):
450
- skill_name = skill_meta['name'] if isinstance(skill_meta, dict) else 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'Skill with name does not exist: {skill_name}')
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 = [{'name': x} if isinstance(x, str) else x for x in skills_to_add]
459
- skills_to_add_names = [x['name'] for x in skills_to_add]
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('Conflict between skills to add and skills to remove.')
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 (set(skills_to_add_names) - set(existing_agent_skills_names)):
475
- skill_parameters = next(x for x in skills_to_add if x['name'] == skill_name).copy()
476
- del skill_parameters['name']
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 = {x['name']: {
485
- k: v for k, v in x.items() if k != 'name'
486
- } for x in skills_to_rewrite}
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, 'parameters')
495
- for new_skill_name in (set(skill_name_to_parameters) - existing_skill_names):
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, 'params')
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'Agent with name does not exist: {agent_name}')
532
- if isinstance(agent.params, dict) and agent.params.get('is_demo') is True:
533
- raise ValueError('Unable to delete demo object')
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
- self,
539
- agent: db.Agents,
540
- messages: List[Dict[str, str]],
541
- project_name: str = default_project,
542
- tools: List[BaseTool] = None,
543
- stream: bool = False) -> Union[Iterator[object], pd.DataFrame]:
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
- lang_agent = LangchainAgent(agent, model)
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
- self,
580
- agent: db.Agents,
581
- messages: List[Dict[str, str]],
582
- project_name: str = default_project,
583
- tools: List[BaseTool] = None) -> Iterator[object]:
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
- lang_agent = LangchainAgent(agent, model=model)
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)