MindsDB 25.5.4.2__py3-none-any.whl → 25.6.3.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 (76) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/api/a2a/agent.py +50 -26
  3. mindsdb/api/a2a/common/server/server.py +32 -26
  4. mindsdb/api/a2a/task_manager.py +68 -6
  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/mindsdb_tables.py +91 -84
  8. mindsdb/api/executor/datahub/datanodes/project_datanode.py +29 -48
  9. mindsdb/api/executor/datahub/datanodes/system_tables.py +35 -61
  10. mindsdb/api/executor/planner/plan_join.py +67 -77
  11. mindsdb/api/executor/planner/query_planner.py +176 -155
  12. mindsdb/api/executor/planner/steps.py +37 -12
  13. mindsdb/api/executor/sql_query/result_set.py +45 -64
  14. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +14 -18
  15. mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +17 -18
  16. mindsdb/api/executor/sql_query/steps/insert_step.py +13 -33
  17. mindsdb/api/executor/sql_query/steps/subselect_step.py +43 -35
  18. mindsdb/api/executor/utilities/sql.py +42 -48
  19. mindsdb/api/http/namespaces/config.py +1 -1
  20. mindsdb/api/http/namespaces/file.py +14 -23
  21. mindsdb/api/http/namespaces/knowledge_bases.py +132 -154
  22. mindsdb/api/mysql/mysql_proxy/data_types/mysql_datum.py +12 -28
  23. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/binary_resultset_row_package.py +59 -50
  24. mindsdb/api/mysql/mysql_proxy/data_types/mysql_packets/resultset_row_package.py +9 -8
  25. mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +449 -461
  26. mindsdb/api/mysql/mysql_proxy/utilities/dump.py +87 -36
  27. mindsdb/integrations/handlers/bigquery_handler/bigquery_handler.py +219 -28
  28. mindsdb/integrations/handlers/file_handler/file_handler.py +15 -9
  29. mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py +43 -24
  30. mindsdb/integrations/handlers/litellm_handler/litellm_handler.py +10 -3
  31. mindsdb/integrations/handlers/llama_index_handler/requirements.txt +1 -1
  32. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +29 -33
  33. mindsdb/integrations/handlers/openai_handler/openai_handler.py +277 -356
  34. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +74 -51
  35. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +305 -98
  36. mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +145 -40
  37. mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +136 -6
  38. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +352 -83
  39. mindsdb/integrations/libs/api_handler.py +279 -57
  40. mindsdb/integrations/libs/base.py +185 -30
  41. mindsdb/integrations/utilities/files/file_reader.py +99 -73
  42. mindsdb/integrations/utilities/handler_utils.py +23 -8
  43. mindsdb/integrations/utilities/sql_utils.py +35 -40
  44. mindsdb/interfaces/agents/agents_controller.py +226 -196
  45. mindsdb/interfaces/agents/constants.py +8 -1
  46. mindsdb/interfaces/agents/langchain_agent.py +42 -11
  47. mindsdb/interfaces/agents/mcp_client_agent.py +29 -21
  48. mindsdb/interfaces/agents/mindsdb_database_agent.py +23 -18
  49. mindsdb/interfaces/data_catalog/__init__.py +0 -0
  50. mindsdb/interfaces/data_catalog/base_data_catalog.py +54 -0
  51. mindsdb/interfaces/data_catalog/data_catalog_loader.py +375 -0
  52. mindsdb/interfaces/data_catalog/data_catalog_reader.py +38 -0
  53. mindsdb/interfaces/database/database.py +81 -57
  54. mindsdb/interfaces/database/integrations.py +222 -234
  55. mindsdb/interfaces/database/log.py +72 -104
  56. mindsdb/interfaces/database/projects.py +156 -193
  57. mindsdb/interfaces/file/file_controller.py +21 -65
  58. mindsdb/interfaces/knowledge_base/controller.py +66 -25
  59. mindsdb/interfaces/knowledge_base/evaluate.py +516 -0
  60. mindsdb/interfaces/knowledge_base/llm_client.py +75 -0
  61. mindsdb/interfaces/skills/custom/text2sql/mindsdb_kb_tools.py +83 -43
  62. mindsdb/interfaces/skills/skills_controller.py +31 -36
  63. mindsdb/interfaces/skills/sql_agent.py +113 -86
  64. mindsdb/interfaces/storage/db.py +242 -82
  65. mindsdb/migrations/versions/2025-05-28_a44643042fe8_added_data_catalog_tables.py +118 -0
  66. mindsdb/migrations/versions/2025-06-09_608e376c19a7_updated_data_catalog_data_types.py +58 -0
  67. mindsdb/utilities/config.py +13 -2
  68. mindsdb/utilities/log.py +35 -26
  69. mindsdb/utilities/ml_task_queue/task.py +19 -22
  70. mindsdb/utilities/render/sqlalchemy_render.py +129 -181
  71. mindsdb/utilities/starters.py +40 -0
  72. {mindsdb-25.5.4.2.dist-info → mindsdb-25.6.3.0.dist-info}/METADATA +257 -257
  73. {mindsdb-25.5.4.2.dist-info → mindsdb-25.6.3.0.dist-info}/RECORD +76 -68
  74. {mindsdb-25.5.4.2.dist-info → mindsdb-25.6.3.0.dist-info}/WHEEL +0 -0
  75. {mindsdb-25.5.4.2.dist-info → mindsdb-25.6.3.0.dist-info}/licenses/LICENSE +0 -0
  76. {mindsdb-25.5.4.2.dist-info → mindsdb-25.6.3.0.dist-info}/top_level.txt +0 -0
@@ -4,15 +4,27 @@ from dataclasses import dataclass, field
4
4
 
5
5
  from mindsdb_sql_parser import ast
6
6
  from mindsdb_sql_parser.ast import (
7
- Select, Identifier, BetweenOperation, Join, Star, BinaryOperation, Constant,
8
- NativeQuery, Parameter
7
+ Select,
8
+ Identifier,
9
+ BetweenOperation,
10
+ Join,
11
+ Star,
12
+ BinaryOperation,
13
+ Constant,
14
+ NativeQuery,
15
+ Parameter,
9
16
  )
10
17
 
11
18
  from mindsdb.integrations.utilities.query_traversal import query_traversal
12
19
 
13
20
  from mindsdb.api.executor.planner.exceptions import PlanningException
14
21
  from mindsdb.api.executor.planner.steps import (
15
- FetchDataframeStep, JoinStep, ApplyPredictorStep, SubSelectStep, QueryStep, MapReduceStep
22
+ FetchDataframeStep,
23
+ JoinStep,
24
+ ApplyPredictorStep,
25
+ SubSelectStep,
26
+ QueryStep,
27
+ MapReduceStep,
16
28
  )
17
29
  from mindsdb.api.executor.planner.utils import filters_to_bin_op
18
30
  from mindsdb.api.executor.planner.plan_join_ts import PlanJoinTSPredictorQuery
@@ -31,18 +43,16 @@ class TableInfo:
31
43
 
32
44
 
33
45
  class PlanJoin:
34
-
35
46
  def __init__(self, planner):
36
47
  self.planner = planner
37
48
 
38
49
  def is_timeseries(self, query):
39
-
40
50
  join = query.from_table
41
51
  l_predictor = self.planner.get_predictor(join.left) if isinstance(join.left, Identifier) else None
42
52
  r_predictor = self.planner.get_predictor(join.right) if isinstance(join.right, Identifier) else None
43
- if l_predictor and l_predictor.get('timeseries'):
53
+ if l_predictor and l_predictor.get("timeseries"):
44
54
  return True
45
- if r_predictor and r_predictor.get('timeseries'):
55
+ if r_predictor and r_predictor.get("timeseries"):
46
56
  return True
47
57
 
48
58
  def check_single_integration(self, query):
@@ -52,16 +62,14 @@ class PlanJoin:
52
62
 
53
63
  # one integration and not mindsdb objects in query
54
64
  if (
55
- len(query_info['mdb_entities']) == 0
56
- and len(query_info['integrations']) == 1
57
- and 'files' not in query_info['integrations']
58
- and 'views' not in query_info['integrations']
65
+ len(query_info["mdb_entities"]) == 0
66
+ and len(query_info["integrations"]) == 1
67
+ and "files" not in query_info["integrations"]
68
+ and "views" not in query_info["integrations"]
59
69
  ):
60
-
61
- int_name = list(query_info['integrations'])[0]
70
+ int_name = list(query_info["integrations"])[0]
62
71
  # if is sql database
63
- if self.planner.integrations.get(int_name, {}).get('class_type') != 'api':
64
-
72
+ if self.planner.integrations.get(int_name, {}).get("class_type") != "api":
65
73
  # send to this integration
66
74
  return int_name
67
75
  return None
@@ -77,7 +85,10 @@ class PlanJoin:
77
85
  if integration_to_send:
78
86
  self.planner.prepare_integration_select(integration_to_send, query)
79
87
 
80
- last_step = self.planner.plan.add_step(FetchDataframeStep(integration=integration_to_send, query=query))
88
+ fetch_params = self.planner.get_fetch_params(query.using)
89
+ last_step = self.planner.plan.add_step(
90
+ FetchDataframeStep(integration=integration_to_send, query=query, params=fetch_params)
91
+ )
81
92
  return last_step
82
93
  elif self.is_timeseries(query):
83
94
  return PlanJoinTSPredictorQuery(self.planner).plan(query, integration)
@@ -86,7 +97,6 @@ class PlanJoin:
86
97
 
87
98
 
88
99
  class PlanJoinTablesQuery:
89
-
90
100
  def __init__(self, planner):
91
101
  self.planner = planner
92
102
 
@@ -105,15 +115,15 @@ class PlanJoinTablesQuery:
105
115
  join_step = self.plan_join_tables(query)
106
116
 
107
117
  if (
108
- query.group_by is not None
109
- or query.order_by is not None
110
- or query.having is not None
111
- or query.distinct is True
112
- or query.where is not None
113
- or query.limit is not None
114
- or query.offset is not None
115
- or len(query.targets) != 1
116
- or not isinstance(query.targets[0], Star)
118
+ query.group_by is not None
119
+ or query.order_by is not None
120
+ or query.having is not None
121
+ or query.distinct is True
122
+ or query.where is not None
123
+ or query.limit is not None
124
+ or query.offset is not None
125
+ or len(query.targets) != 1
126
+ or not isinstance(query.targets[0], Star)
117
127
  ):
118
128
  query2 = copy.deepcopy(query)
119
129
  query2.from_table = None
@@ -147,10 +157,10 @@ class PlanJoinTablesQuery:
147
157
  else:
148
158
  integration = self.planner.default_namespace
149
159
 
150
- if integration is None and not hasattr(table, 'sub_select'):
151
- raise PlanningException(f'Integration not found for: {table}')
160
+ if integration is None and not hasattr(table, "sub_select"):
161
+ raise PlanningException(f"Integration not found for: {table}")
152
162
 
153
- sub_select = getattr(table, 'sub_select', None)
163
+ sub_select = getattr(table, "sub_select", None)
154
164
 
155
165
  return TableInfo(integration, table, aliases, conditions=[], sub_select=sub_select)
156
166
 
@@ -190,7 +200,7 @@ class PlanJoinTablesQuery:
190
200
 
191
201
  sequence2 = self.get_join_sequence(node.right, condition=node.condition)
192
202
  if len(sequence2) != 1:
193
- raise PlanningException('Unexpected join nesting behavior')
203
+ raise PlanningException("Unexpected join nesting behavior")
194
204
 
195
205
  # put next table
196
206
  sequence.append(sequence2[0])
@@ -203,7 +213,6 @@ class PlanJoinTablesQuery:
203
213
  return sequence
204
214
 
205
215
  def check_node_condition(self, node):
206
-
207
216
  col_idx = 0
208
217
  if len(node.args) == 2:
209
218
  if not isinstance(node.args[col_idx], Identifier):
@@ -230,7 +239,7 @@ class PlanJoinTablesQuery:
230
239
 
231
240
  table_info = self.get_table_for_column(arg1)
232
241
  if table_info is None:
233
- raise PlanningException(f'Table not found for identifier: {arg1.to_string()}')
242
+ raise PlanningException(f"Table not found for identifier: {arg1.to_string()}")
234
243
 
235
244
  # keep only column name
236
245
  arg1.parts = [arg1.parts[-1]]
@@ -253,28 +262,26 @@ class PlanJoinTablesQuery:
253
262
 
254
263
  query_traversal(query.where, _check_node_condition)
255
264
 
256
- self.query_context['binary_ops'] = binary_ops
265
+ self.query_context["binary_ops"] = binary_ops
257
266
 
258
267
  def check_use_limit(self, query_in, join_sequence):
259
268
  # use limit for first table?
260
269
  # if only models
261
270
  use_limit = False
262
271
  if query_in.having is None or query_in.group_by is None and query_in.limit is not None:
263
-
264
272
  join = None
265
273
  use_limit = True
266
274
  for item in join_sequence:
267
275
  if isinstance(item, TableInfo):
268
276
  if item.predictor_info is None and item.sub_select is None:
269
277
  if join is not None:
270
- if join.join_type.upper() != 'LEFT JOIN':
278
+ if join.join_type.upper() != "LEFT JOIN":
271
279
  use_limit = False
272
280
  elif isinstance(item, Join):
273
281
  join = item
274
- self.query_context['use_limit'] = use_limit
282
+ self.query_context["use_limit"] = use_limit
275
283
 
276
284
  def plan_join_tables(self, query_in):
277
-
278
285
  # plan all nested selects in 'where'
279
286
  find_selects = self.planner.get_nested_selects_plan_fnc(self.planner.default_namespace, force=True)
280
287
  query_in.targets = query_traversal(query_in.targets, find_selects)
@@ -285,7 +292,7 @@ class PlanJoinTablesQuery:
285
292
  # replace sub selects, with identifiers with links to original selects
286
293
  def replace_subselects(node, **args):
287
294
  if isinstance(node, Select) or isinstance(node, NativeQuery) or isinstance(node, ast.Data):
288
- name = f't_{id(node)}'
295
+ name = f"t_{id(node)}"
289
296
  node2 = Identifier(name, alias=node.alias)
290
297
 
291
298
  # save in attribute
@@ -306,7 +313,7 @@ class PlanJoinTablesQuery:
306
313
  if len(node.parts) > 1:
307
314
  table_info = self.get_table_for_column(node)
308
315
  if table_info is None:
309
- raise PlanningException(f'Table not found for identifier: {node.to_string()}')
316
+ raise PlanningException(f"Table not found for identifier: {node.to_string()}")
310
317
 
311
318
  # # replace identifies name
312
319
  col_parts = list(table_info.aliases[-1])
@@ -329,7 +336,6 @@ class PlanJoinTablesQuery:
329
336
  self.step_stack = []
330
337
  for item in join_sequence:
331
338
  if isinstance(item, TableInfo):
332
-
333
339
  if item.sub_select is not None:
334
340
  self.process_subselect(item)
335
341
  elif item.predictor_info is not None:
@@ -345,8 +351,8 @@ class PlanJoinTablesQuery:
345
351
  new_join = copy.deepcopy(item)
346
352
 
347
353
  # TODO
348
- new_join.left = Identifier('tab1')
349
- new_join.right = Identifier('tab2')
354
+ new_join.left = Identifier("tab1")
355
+ new_join.right = Identifier("tab2")
350
356
  new_join.implicit = False
351
357
 
352
358
  step = self.add_plan_step(JoinStep(left=step_left.result, right=step_right.result, query=new_join))
@@ -369,12 +375,11 @@ class PlanJoinTablesQuery:
369
375
  # apply table alias
370
376
  query2 = Select(targets=[Star()], where=where)
371
377
  if item.table.alias is None:
372
- raise PlanningException(f'Subselect in join have to be aliased: {item.sub_select.to_string()}')
378
+ raise PlanningException(f"Subselect in join have to be aliased: {item.sub_select.to_string()}")
373
379
  table_name = item.table.alias.parts[-1]
374
380
 
375
381
  add_absent_cols = False
376
- if hasattr(item.sub_select, 'from_table') and \
377
- isinstance(item.sub_select.from_table, ast.Data):
382
+ if hasattr(item.sub_select, "from_table") and isinstance(item.sub_select.from_table, ast.Data):
378
383
  add_absent_cols = True
379
384
 
380
385
  step2 = SubSelectStep(query2, step.result, table_name=table_name, add_absent_cols=add_absent_cols)
@@ -387,13 +392,13 @@ class PlanJoinTablesQuery:
387
392
  query2 = Select(from_table=table, targets=[Star()])
388
393
  # parts = tuple(map(str.lower, table_name.parts))
389
394
  conditions = item.conditions
390
- if 'or' in self.query_context['binary_ops']:
395
+ if "or" in self.query_context["binary_ops"]:
391
396
  # not use conditions
392
397
  conditions = []
393
398
 
394
399
  conditions += self.get_filters_from_join_conditions(item)
395
400
 
396
- if self.query_context['use_limit']:
401
+ if self.query_context["use_limit"]:
397
402
  order_by = None
398
403
  if query_in.order_by is not None:
399
404
  order_by = []
@@ -416,10 +421,10 @@ class PlanJoinTablesQuery:
416
421
  # copy order
417
422
  query2.order_by = order_by
418
423
 
419
- self.query_context['use_limit'] = False
424
+ self.query_context["use_limit"] = False
420
425
  for cond in conditions:
421
426
  if query2.where is not None:
422
- query2.where = BinaryOperation('and', args=[query2.where, cond])
427
+ query2.where = BinaryOperation("and", args=[query2.where, cond])
423
428
  else:
424
429
  query2.where = cond
425
430
 
@@ -430,7 +435,6 @@ class PlanJoinTablesQuery:
430
435
  self.step_stack.append(step)
431
436
 
432
437
  def join_condition_to_columns_map(self, model_table):
433
-
434
438
  columns_map = {}
435
439
 
436
440
  def _check_conditions(node, **kwargs):
@@ -461,7 +465,6 @@ class PlanJoinTablesQuery:
461
465
  return columns_map
462
466
 
463
467
  def get_filters_from_join_conditions(self, fetch_table):
464
-
465
468
  binary_ops = set()
466
469
  conditions = []
467
470
  data_conditions = []
@@ -470,7 +473,7 @@ class PlanJoinTablesQuery:
470
473
  if not isinstance(node, BinaryOperation):
471
474
  return
472
475
 
473
- if node.op != '=':
476
+ if node.op != "=":
474
477
  binary_ops.add(node.op.lower())
475
478
  return
476
479
 
@@ -492,7 +495,7 @@ class PlanJoinTablesQuery:
492
495
 
493
496
  query_traversal(fetch_table.join_condition, _check_conditions)
494
497
 
495
- binary_ops.discard('and')
498
+ binary_ops.discard("and")
496
499
  if len(binary_ops) > 0:
497
500
  # other operations exists, skip
498
501
  return []
@@ -514,25 +517,19 @@ class PlanJoinTablesQuery:
514
517
  subselect_step = SubSelectStep(query2, fetch_step.result)
515
518
  subselect_step = self.add_plan_step(subselect_step)
516
519
 
517
- conditions.append(BinaryOperation(
518
- op='in',
519
- args=[
520
- arg1,
521
- Parameter(subselect_step.result)
522
- ]
523
- ))
520
+ conditions.append(BinaryOperation(op="in", args=[arg1, Parameter(subselect_step.result)]))
524
521
 
525
522
  return conditions
526
523
 
527
524
  def process_predictor(self, item, query_in):
528
525
  if len(self.step_stack) == 0:
529
526
  raise NotImplementedError("Predictor can't be first element of join syntax")
530
- if item.predictor_info.get('timeseries'):
527
+ if item.predictor_info.get("timeseries"):
531
528
  raise NotImplementedError("TS predictor is not supported here yet")
532
529
  data_step = self.step_stack[-1]
533
530
  row_dict = None
534
531
 
535
- predict_target = item.predictor_info.get('to_predict')
532
+ predict_target = item.predictor_info.get("to_predict")
536
533
  if isinstance(predict_target, list) and len(predict_target) > 0:
537
534
  predict_target = predict_target[0]
538
535
  if predict_target is not None:
@@ -545,7 +542,7 @@ class PlanJoinTablesQuery:
545
542
  if item.conditions:
546
543
  row_dict = {}
547
544
  for i, el in enumerate(item.conditions):
548
- if isinstance(el.args[0], Identifier) and el.op == '=':
545
+ if isinstance(el.args[0], Identifier) and el.op == "=":
549
546
  col_name = el.args[0].parts[-1]
550
547
  if col_name.lower() == predict_target:
551
548
  # don't add predict target to parameters
@@ -563,15 +560,15 @@ class PlanJoinTablesQuery:
563
560
  if query_in.using is not None:
564
561
  model_params = {}
565
562
  for param, value in query_in.using.items():
566
- if '.' in param:
567
- alias = param.split('.')[0]
563
+ if "." in param:
564
+ alias = param.split(".")[0]
568
565
  if (alias,) in item.aliases:
569
- new_param = '.'.join(param.split('.')[1:])
566
+ new_param = ".".join(param.split(".")[1:])
570
567
  model_params[new_param.lower()] = value
571
568
  else:
572
569
  model_params[param.lower()] = value
573
570
 
574
- partition_size = model_params.pop('partition_size', None)
571
+ partition_size = model_params.pop("partition_size", None)
575
572
 
576
573
  predictor_step = ApplyPredictorStep(
577
574
  namespace=item.integration,
@@ -582,9 +579,7 @@ class PlanJoinTablesQuery:
582
579
  columns_map=columns_map,
583
580
  )
584
581
 
585
- self.step_stack.append(
586
- self.add_plan_step(predictor_step, partition_size=partition_size)
587
- )
582
+ self.step_stack.append(self.add_plan_step(predictor_step, partition_size=partition_size))
588
583
 
589
584
  def add_plan_step(self, step, partition_size=None):
590
585
  """
@@ -608,12 +603,7 @@ class PlanJoinTablesQuery:
608
603
  elif partition_size is not None:
609
604
  # create partition
610
605
 
611
- self.partition = MapReduceStep(
612
- values=step.dataframe,
613
- reduce='union',
614
- step=[],
615
- partition=partition_size
616
- )
606
+ self.partition = MapReduceStep(values=step.dataframe, reduce="union", step=[], partition=partition_size)
617
607
  self.planner.plan.add_step(self.partition)
618
608
 
619
609
  self.add_step_to_partition(step)
@@ -626,7 +616,7 @@ class PlanJoinTablesQuery:
626
616
  return self.planner.plan.add_step(step)
627
617
 
628
618
  def add_step_to_partition(self, step):
629
- step.step_num = f'{self.partition.step_num}_{len(self.partition.step)}'
619
+ step.step_num = f"{self.partition.step_num}_{len(self.partition.step)}"
630
620
  self.partition.step.append(step)
631
621
 
632
622
  def close_partition(self):