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
@@ -7,12 +7,22 @@ from pathlib import Path
7
7
  import pandas
8
8
  import pytest
9
9
  from mindsdb_sql_parser.exceptions import ParsingException
10
- from mindsdb_sql_parser.ast import CreateTable, DropTables, Identifier, Insert, TableColumn, Update
10
+ from mindsdb_sql_parser.ast import (
11
+ CreateTable,
12
+ DropTables,
13
+ Identifier,
14
+ Insert,
15
+ TableColumn,
16
+ Update,
17
+ )
11
18
 
12
19
  from mindsdb.integrations.handlers.file_handler.file_handler import FileHandler
13
20
  from mindsdb.integrations.libs.response import RESPONSE_TYPE
14
21
 
15
- from mindsdb.integrations.utilities.files.file_reader import FileReader
22
+ from mindsdb.integrations.utilities.files.file_reader import (
23
+ FileReader,
24
+ FileProcessingError,
25
+ )
16
26
 
17
27
 
18
28
  # Define a table to use as content for all of the file types
@@ -103,21 +113,18 @@ class TestIsItX:
103
113
 
104
114
  def test_is_it_csv(self):
105
115
  # We can't test xlsx or parquet here because they're binary files
106
- for file_path, result in (
107
- (csv_file(), True),
108
- (json_file(), False)
109
- ):
116
+ for file_path, result in ((csv_file(), True), (json_file(), False)):
110
117
  with open(file_path, "r") as fh:
111
118
  assert FileReader.is_csv(StringIO(fh.read())) is result
112
119
 
113
120
  def test_format(self):
114
121
  for file_path, result in (
115
- (csv_file(), 'csv'),
116
- (xlsx_file(), 'xlsx'),
117
- (json_file(), 'json'),
118
- (parquet_file(), 'parquet'),
119
- (txt_file(), 'txt'),
120
- (pdf_file(), 'pdf'),
122
+ (csv_file(), "csv"),
123
+ (xlsx_file(), "xlsx"),
124
+ (json_file(), "json"),
125
+ (parquet_file(), "parquet"),
126
+ (txt_file(), "txt"),
127
+ (pdf_file(), "pdf"),
121
128
  ):
122
129
  assert FileReader(path=file_path).get_format() == result
123
130
 
@@ -182,6 +189,7 @@ class TestQuery:
182
189
 
183
190
  def mock_get_file_path(self, name):
184
191
  return csv_tmp
192
+
185
193
  monkeypatch.setattr(MockFileController, "get_file_path", mock_get_file_path)
186
194
 
187
195
  file_handler = FileHandler(file_controller=MockFileController())
@@ -255,14 +263,13 @@ class TestQuery:
255
263
 
256
264
 
257
265
  def test_handle_source():
258
-
259
266
  def get_reader(file_path):
260
267
  # using path
261
268
  reader = FileReader(path=file_path)
262
269
  yield reader
263
270
 
264
271
  # using file descriptor
265
- with open(file_path, 'rb') as fd:
272
+ with open(file_path, "rb") as fd:
266
273
  reader = FileReader(file=fd)
267
274
  yield reader
268
275
  fd.seek(0)
@@ -310,14 +317,31 @@ def test_check_valid_dialects(csv_string, delimiter):
310
317
  def test_tsv():
311
318
  file = BytesIO(b"example;csv;file\tname")
312
319
 
313
- reader = FileReader(file=file, name='test.tsv')
314
- assert reader.get_format() == 'csv'
315
- assert reader.parameters['delimiter'] == '\t'
320
+ reader = FileReader(file=file, name="test.tsv")
321
+ assert reader.get_format() == "csv"
322
+ assert reader.parameters["delimiter"] == "\t"
316
323
 
317
324
  df = reader.get_page_content()
318
325
  assert len(df.columns) == 2
319
326
 
320
327
 
328
+ def test_bad_csv_header():
329
+ file = BytesIO(b" a,b ,c\n1,2,3\n")
330
+ reader = FileReader(file=file, name="test.tsv")
331
+ df = reader.get_page_content()
332
+ assert set(df.columns) == set(["a", "b", "c"])
333
+
334
+ wrong_data = [
335
+ b"a, ,c\n1,2,3\n",
336
+ b"a, \t,c\n1,2,3\n",
337
+ b" ,b,c\n1,2,3\n",
338
+ ]
339
+ for data in wrong_data:
340
+ reader = FileReader(file=BytesIO(data), name="test.tsv")
341
+ with pytest.raises(FileProcessingError):
342
+ df = reader.get_page_content()
343
+
344
+
321
345
  def test_check_invalid_dialects():
322
346
  with pytest.raises(Exception):
323
347
  FileHandler._get_csv_dialect("example csv file")
@@ -334,10 +358,7 @@ def test_get_tables():
334
358
  assert response.type == RESPONSE_TYPE.TABLE
335
359
 
336
360
  expected_df = pandas.DataFrame(
337
- [
338
- {"TABLE_NAME": x[0], "TABLE_ROWS": x[1], "TABLE_TYPE": "BASE TABLE"}
339
- for x in file_records
340
- ]
361
+ [{"TABLE_NAME": x[0], "TABLE_ROWS": x[1], "TABLE_TYPE": "BASE TABLE"} for x in file_records]
341
362
  )
342
363
 
343
364
  assert response.data_frame.equals(expected_df)
@@ -349,8 +370,6 @@ def test_get_columns():
349
370
 
350
371
  assert response.type == RESPONSE_TYPE.TABLE
351
372
 
352
- expected_df = pandas.DataFrame(
353
- [{"Field": x, "Type": "str"} for x in file_records[0][2]]
354
- )
373
+ expected_df = pandas.DataFrame([{"Field": x, "Type": "str"} for x in file_records[0][2]])
355
374
 
356
375
  assert response.data_frame.equals(expected_df)
@@ -2,7 +2,7 @@ import ast
2
2
  from typing import Dict, Optional, List
3
3
 
4
4
 
5
- from litellm import completion, batch_completion, embedding
5
+ from litellm import completion, batch_completion, embedding, acompletion
6
6
  import pandas as pd
7
7
 
8
8
  from mindsdb.integrations.libs.base import BaseMLEngine
@@ -42,10 +42,17 @@ class LiteLLMHandler(BaseMLEngine):
42
42
  f"https://{args['snowflake_account_id']}.snowflakecomputing.com/api/v2/cortex/inference:complete"
43
43
  )
44
44
 
45
- from litellm import acompletion
46
-
47
45
  return await acompletion(model=model, messages=messages, stream=False, **args)
48
46
 
47
+ @staticmethod
48
+ def completion(model: str, messages: List[dict], args: dict):
49
+ if model.startswith("snowflake/") and "snowflake_account_id" in args:
50
+ args["api_base"] = (
51
+ f"https://{args['snowflake_account_id']}.snowflakecomputing.com/api/v2/cortex/inference:complete"
52
+ )
53
+
54
+ return completion(model=model, messages=messages, stream=False, **args)
55
+
49
56
  def create(
50
57
  self,
51
58
  target: str,
@@ -1,4 +1,4 @@
1
- llama-index==0.12.21
1
+ llama-index==0.12.28
2
2
  pydantic-settings >= 2.1.0
3
3
  llama-index-readers-web
4
4
  llama-index-embeddings-openai
@@ -31,9 +31,7 @@ def _map_type(mysql_type_text: str) -> MYSQL_DATA_TYPE:
31
31
  try:
32
32
  return MYSQL_DATA_TYPE(mysql_type_text.upper())
33
33
  except Exception:
34
- logger.warning(
35
- f"MySQL handler: unknown type: {mysql_type_text}, use TEXT as fallback."
36
- )
34
+ logger.warning(f"MySQL handler: unknown type: {mysql_type_text}, use TEXT as fallback.")
37
35
  return MYSQL_DATA_TYPE.TEXT
38
36
 
39
37
 
@@ -65,22 +63,23 @@ def _make_table_response(result: list[dict], cursor: mysql.connector.cursor.MySQ
65
63
  mysql_types.append(reverse_c_type_map[type_int])
66
64
  continue
67
65
 
68
- if type_int != C_TYPES.MYSQL_TYPE_BLOB:
69
- raise ValueError(f'Unknown MySQL type id={type_int} in column {col[0]}')
70
-
71
- # region determine text/blob type by flags
72
- # Unfortunately, there is no way to determine particular type of text/blob column by flags.
73
- # Subtype have to be determined by 8-s element of description tuple, but mysql.conector
74
- # return the same value for all text types (TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT), and for
75
- # all blob types (TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB).
76
- if col[7] == 16: # and col[8] == 45
77
- mysql_types.append(MYSQL_DATA_TYPE.TEXT)
78
- elif col[7] == 144: # and col[8] == 63
79
- mysql_types.append(MYSQL_DATA_TYPE.BLOB)
66
+ if type_int == C_TYPES.MYSQL_TYPE_BLOB:
67
+ # region determine text/blob type by flags
68
+ # Unfortunately, there is no way to determine particular type of text/blob column by flags.
69
+ # Subtype have to be determined by 8-s element of description tuple, but mysql.conector
70
+ # return the same value for all text types (TINYTEXT, TEXT, MEDIUMTEXT, LONGTEXT), and for
71
+ # all blob types (TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB).
72
+ if col[7] == 16: # and col[8] == 45
73
+ mysql_types.append(MYSQL_DATA_TYPE.TEXT)
74
+ elif col[7] == 144: # and col[8] == 63
75
+ mysql_types.append(MYSQL_DATA_TYPE.BLOB)
76
+ else:
77
+ logger.debug(f"MySQL handler: unknown type code {col[7]}, use TEXT as fallback.")
78
+ mysql_types.append(MYSQL_DATA_TYPE.TEXT)
79
+ # endregion
80
80
  else:
81
- logger.debug(f'MySQL handler: unknown type code {col[7]}, use TEXT as fallback.')
81
+ logger.warning(f"MySQL handler: unknown type id={type_int} in column {col[0]}, use TEXT as fallback.")
82
82
  mysql_types.append(MYSQL_DATA_TYPE.TEXT)
83
- # endregion
84
83
 
85
84
  # region cast int and bool to nullable types
86
85
  serieses = []
@@ -88,22 +87,20 @@ def _make_table_response(result: list[dict], cursor: mysql.connector.cursor.MySQ
88
87
  expected_dtype = None
89
88
  column_name = description[i][0]
90
89
  if mysql_type in (
91
- MYSQL_DATA_TYPE.SMALLINT, MYSQL_DATA_TYPE.INT, MYSQL_DATA_TYPE.MEDIUMINT,
92
- MYSQL_DATA_TYPE.BIGINT, MYSQL_DATA_TYPE.TINYINT
90
+ MYSQL_DATA_TYPE.SMALLINT,
91
+ MYSQL_DATA_TYPE.INT,
92
+ MYSQL_DATA_TYPE.MEDIUMINT,
93
+ MYSQL_DATA_TYPE.BIGINT,
94
+ MYSQL_DATA_TYPE.TINYINT,
93
95
  ):
94
- expected_dtype = 'Int64'
96
+ expected_dtype = "Int64"
95
97
  elif mysql_type in (MYSQL_DATA_TYPE.BOOL, MYSQL_DATA_TYPE.BOOLEAN):
96
- expected_dtype = 'boolean'
98
+ expected_dtype = "boolean"
97
99
  serieses.append(pd.Series([row[column_name] for row in result], dtype=expected_dtype, name=description[i][0]))
98
100
  df = pd.concat(serieses, axis=1, copy=False)
99
101
  # endregion
100
102
 
101
- response = Response(
102
- RESPONSE_TYPE.TABLE,
103
- df,
104
- affected_rows=cursor.rowcount,
105
- mysql_types=mysql_types
106
- )
103
+ response = Response(RESPONSE_TYPE.TABLE, df, affected_rows=cursor.rowcount, mysql_types=mysql_types)
107
104
  return response
108
105
 
109
106
 
@@ -182,6 +179,9 @@ class MySQLHandler(DatabaseHandler):
182
179
  config["ssl_cert"] = ssl_cert
183
180
  if ssl_key is not None:
184
181
  config["ssl_key"] = ssl_key
182
+ elif ssl is False:
183
+ config["ssl_disabled"] = True
184
+
185
185
  if "collation" not in config:
186
186
  config["collation"] = "utf8mb4_general_ci"
187
187
  if "use_pure" not in config:
@@ -219,9 +219,7 @@ class MySQLHandler(DatabaseHandler):
219
219
  connection = self.connect()
220
220
  result.success = connection.is_connected()
221
221
  except mysql.connector.Error as e:
222
- logger.error(
223
- f'Error connecting to MySQL {self.connection_data["database"]}, {e}!'
224
- )
222
+ logger.error(f"Error connecting to MySQL {self.connection_data['database']}, {e}!")
225
223
  result.error_message = str(e)
226
224
 
227
225
  if result.success and need_to_close:
@@ -252,9 +250,7 @@ class MySQLHandler(DatabaseHandler):
252
250
  else:
253
251
  response = Response(RESPONSE_TYPE.OK, affected_rows=cur.rowcount)
254
252
  except mysql.connector.Error as e:
255
- logger.error(
256
- f'Error running query: {query} on {self.connection_data["database"]}!'
257
- )
253
+ logger.error(f"Error running query: {query} on {self.connection_data['database']}!")
258
254
  response = Response(RESPONSE_TYPE.ERROR, error_message=str(e))
259
255
  if connection is not None and connection.is_connected():
260
256
  connection.rollback()