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
@@ -39,22 +39,36 @@ def get_api_key(
39
39
  if "using" in create_args and f"{api_name.lower()}_api_key" in create_args["using"]:
40
40
  return create_args["using"][f"{api_name.lower()}_api_key"]
41
41
 
42
+ # 1.5 - Check for generic api_key in using
43
+ if "using" in create_args and "api_key" in create_args["using"]:
44
+ return create_args["using"]["api_key"]
45
+
42
46
  # 2
43
47
  if f"{api_name.lower()}_api_key" in create_args:
44
48
  return create_args[f"{api_name.lower()}_api_key"]
45
49
 
46
- # 2.5 - Check in params dictionary if it exists (for agents)
50
+ # 2.5 - Check for generic api_key
51
+ if "api_key" in create_args:
52
+ return create_args["api_key"]
53
+
54
+ # 3 - Check in params dictionary if it exists (for agents)
47
55
  if "params" in create_args and create_args["params"] is not None:
48
56
  if f"{api_name.lower()}_api_key" in create_args["params"]:
49
57
  return create_args["params"][f"{api_name.lower()}_api_key"]
58
+ # 3.5 - Check for generic api_key in params
59
+ if "api_key" in create_args["params"]:
60
+ return create_args["params"]["api_key"]
50
61
 
51
- # 3
62
+ # 4
52
63
  if engine_storage is not None:
53
64
  connection_args = engine_storage.get_connection_args()
54
65
  if f"{api_name.lower()}_api_key" in connection_args:
55
66
  return connection_args[f"{api_name.lower()}_api_key"]
67
+ # 4.5 - Check for generic api_key in connection_args
68
+ if "api_key" in connection_args:
69
+ return connection_args["api_key"]
56
70
 
57
- # 4
71
+ # 5
58
72
  api_key = os.getenv(f"{api_name.lower()}_api_key")
59
73
  if api_key is not None:
60
74
  return api_key
@@ -62,15 +76,15 @@ def get_api_key(
62
76
  if api_key is not None:
63
77
  return api_key
64
78
 
65
- # 5
79
+ # 6
66
80
  config = Config()
67
81
  api_cfg = config.get(api_name, {})
68
82
  if f"{api_name.lower()}_api_key" in api_cfg:
69
83
  return api_cfg[f"{api_name.lower()}_api_key"]
70
84
 
71
- # 6
72
- if 'api_keys' in create_args and api_name in create_args['api_keys']:
73
- return create_args['api_keys'][api_name]
85
+ # 7
86
+ if "api_keys" in create_args and api_name in create_args["api_keys"]:
87
+ return create_args["api_keys"][api_name]
74
88
 
75
89
  if strict:
76
90
  provider_upper = api_name.upper()
@@ -79,8 +93,9 @@ def get_api_key(
79
93
  error_message = (
80
94
  f"API key for {api_name} not found. Please provide it using one of the following methods:\n"
81
95
  f"1. Set the {api_key_env_var} environment variable\n"
82
- f"2. Provide it as '{api_key_arg}' parameter when creating an agent using the CREATE AGENT syntax\n"
96
+ f"2. Provide it as '{api_key_arg}' parameter or 'api_key' parameter when creating an agent using the CREATE AGENT syntax\n"
83
97
  f" Example: CREATE AGENT my_agent USING model='gpt-4', provider='{api_name}', {api_key_arg}='your-api-key';\n"
98
+ f" Or: CREATE AGENT my_agent USING model='gpt-4', provider='{api_name}', api_key='your-api-key';\n"
84
99
  )
85
100
  raise Exception(error_message)
86
101
  return None
@@ -46,11 +46,7 @@ class FilterCondition:
46
46
 
47
47
  def __eq__(self, __value: object) -> bool:
48
48
  if isinstance(__value, FilterCondition):
49
- return (
50
- self.column == __value.column
51
- and self.op == __value.op
52
- and self.value == __value.value
53
- )
49
+ return self.column == __value.column and self.op == __value.op and self.value == __value.value
54
50
  else:
55
51
  return False
56
52
 
@@ -75,7 +71,7 @@ def make_sql_session():
75
71
  from mindsdb.api.executor.controllers.session_controller import SessionController
76
72
 
77
73
  sql_session = SessionController()
78
- sql_session.database = config.get('default_project')
74
+ sql_session.database = config.get("default_project")
79
75
  return sql_session
80
76
 
81
77
 
@@ -84,44 +80,50 @@ def conditions_to_filter(binary_op: ASTNode):
84
80
 
85
81
  filters = {}
86
82
  for op, arg1, arg2 in conditions:
87
- if op != '=':
83
+ if op != "=":
88
84
  raise NotImplementedError
89
85
  filters[arg1] = arg2
90
86
  return filters
91
87
 
92
88
 
93
- def extract_comparison_conditions(binary_op: ASTNode):
94
- '''Extracts all simple comparison conditions that must be true from an AST node.
89
+ def extract_comparison_conditions(binary_op: ASTNode, ignore_functions=False):
90
+ """Extracts all simple comparison conditions that must be true from an AST node.
95
91
  Does NOT support 'or' conditions.
96
- '''
92
+ """
97
93
  conditions = []
98
94
 
99
95
  def _extract_comparison_conditions(node: ASTNode, **kwargs):
100
96
  if isinstance(node, ast.BinaryOperation):
101
97
  op = node.op.lower()
102
- if op == 'and':
98
+ if op == "and":
103
99
  # Want to separate individual conditions, not include 'and' as its own condition.
104
100
  return
105
- elif not isinstance(node.args[0], ast.Identifier):
101
+
102
+ arg1, arg2 = node.args
103
+ if ignore_functions and isinstance(arg1, ast.Function):
104
+ # handle lower/upper
105
+ if arg1.op.lower() in ("lower", "upper"):
106
+ if isinstance(arg1.args[0], ast.Identifier):
107
+ arg1 = arg1.args[0]
108
+
109
+ if not isinstance(arg1, ast.Identifier):
106
110
  # Only support [identifier] =/</>/>=/<=/etc [constant] comparisons.
107
- raise NotImplementedError(f'Not implemented arg1: {node.args[0]}')
111
+ raise NotImplementedError(f"Not implemented arg1: {arg1}")
108
112
 
109
- if isinstance(node.args[1], ast.Constant):
110
- value = node.args[1].value
111
- elif isinstance(node.args[1], ast.Tuple):
112
- value = [i.value for i in node.args[1].items]
113
+ if isinstance(arg2, ast.Constant):
114
+ value = arg2.value
115
+ elif isinstance(arg2, ast.Tuple):
116
+ value = [i.value for i in arg2.items]
113
117
  else:
114
- raise NotImplementedError(f'Not implemented arg2: {node.args[1]}')
118
+ raise NotImplementedError(f"Not implemented arg2: {arg2}")
115
119
 
116
- conditions.append([op, node.args[0].parts[-1], value])
120
+ conditions.append([op, arg1.parts[-1], value])
117
121
  if isinstance(node, ast.BetweenOperation):
118
122
  var, up, down = node.args
119
123
  if not (
120
- isinstance(var, ast.Identifier)
121
- and isinstance(up, ast.Constant)
122
- and isinstance(down, ast.Constant)
124
+ isinstance(var, ast.Identifier) and isinstance(up, ast.Constant) and isinstance(down, ast.Constant)
123
125
  ):
124
- raise NotImplementedError(f'Not implemented: {node}')
126
+ raise NotImplementedError(f"Not implemented: {node}")
125
127
 
126
128
  op = node.op.lower()
127
129
  conditions.append([op, var.parts[-1], (up.value, down.value)])
@@ -131,16 +133,13 @@ def extract_comparison_conditions(binary_op: ASTNode):
131
133
 
132
134
 
133
135
  def project_dataframe(df, targets, table_columns):
134
- '''
135
- case-insensitive projection
136
- 'select A' and 'select a' return different column case but with the same content
137
- '''
136
+ """
137
+ case-insensitive projection
138
+ 'select A' and 'select a' return different column case but with the same content
139
+ """
138
140
 
139
141
  columns = []
140
- df_cols_idx = {
141
- col.lower(): col
142
- for col in df.columns
143
- }
142
+ df_cols_idx = {col.lower(): col for col in df.columns}
144
143
  df_col_rename = {}
145
144
 
146
145
  for target in targets:
@@ -156,10 +155,7 @@ def project_dataframe(df, targets, table_columns):
156
155
  col = target.parts[-1]
157
156
  col_df = df_cols_idx.get(col.lower())
158
157
  if col_df is not None:
159
- if (
160
- hasattr(target, 'alias')
161
- and isinstance(target.alias, ast.Identifier)
162
- ):
158
+ if hasattr(target, "alias") and isinstance(target.alias, ast.Identifier):
163
159
  df_col_rename[col_df] = target.alias.parts[0]
164
160
  else:
165
161
  df_col_rename[col_df] = col
@@ -184,14 +180,13 @@ def project_dataframe(df, targets, table_columns):
184
180
 
185
181
 
186
182
  def filter_dataframe(df: pd.DataFrame, conditions: list):
187
-
188
183
  # convert list of conditions to ast.
189
184
  # assumes that list was got from extract_comparison_conditions
190
185
  where_query = None
191
186
  for op, arg1, arg2 in conditions:
192
187
  op = op.lower()
193
188
 
194
- if op == 'between':
189
+ if op == "between":
195
190
  item = ast.BetweenOperation(args=[ast.Identifier(arg1), ast.Constant(arg2[0]), ast.Constant(arg2[1])])
196
191
  else:
197
192
  if isinstance(arg2, (tuple, list)):
@@ -201,9 +196,9 @@ def filter_dataframe(df: pd.DataFrame, conditions: list):
201
196
  if where_query is None:
202
197
  where_query = item
203
198
  else:
204
- where_query = ast.BinaryOperation(op='and', args=[where_query, item])
199
+ where_query = ast.BinaryOperation(op="and", args=[where_query, item])
205
200
 
206
- query = ast.Select(targets=[ast.Star()], from_table=ast.Identifier('df'), where=where_query)
201
+ query = ast.Select(targets=[ast.Star()], from_table=ast.Identifier("df"), where=where_query)
207
202
 
208
203
  return query_df(df, query)
209
204
 
@@ -220,7 +215,7 @@ def sort_dataframe(df, order_by: list):
220
215
  continue
221
216
 
222
217
  cols.append(col)
223
- ascending.append(False if order.direction.lower() == 'desc' else True)
218
+ ascending.append(False if order.direction.lower() == "desc" else True)
224
219
  if len(cols) > 0:
225
220
  df = df.sort_values(by=cols, ascending=ascending)
226
221
  return df