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,14 +1,14 @@
1
- from typing import Any, Dict, Text
1
+ from typing import Any, Dict, List, Optional, Text
2
2
 
3
3
  import pandas as pd
4
4
  import salesforce_api
5
5
  from salesforce_api.exceptions import AuthenticationError, RestRequestCouldNotBeUnderstoodError
6
6
 
7
- from mindsdb.integrations.libs.api_handler import APIHandler
7
+ from mindsdb.integrations.libs.api_handler import MetaAPIHandler
8
8
  from mindsdb.integrations.libs.response import (
9
9
  HandlerResponse as Response,
10
10
  HandlerStatusResponse as StatusResponse,
11
- RESPONSE_TYPE
11
+ RESPONSE_TYPE,
12
12
  )
13
13
  from mindsdb.integrations.handlers.salesforce_handler.salesforce_tables import create_table_class
14
14
  from mindsdb.utilities import log
@@ -17,12 +17,12 @@ from mindsdb.utilities import log
17
17
  logger = log.getLogger(__name__)
18
18
 
19
19
 
20
- class SalesforceHandler(APIHandler):
20
+ class SalesforceHandler(MetaAPIHandler):
21
21
  """
22
22
  This handler handles the connection and execution of SQL statements on Salesforce.
23
23
  """
24
24
 
25
- name = 'salesforce'
25
+ name = "salesforce"
26
26
 
27
27
  def __init__(self, name: Text, connection_data: Dict, **kwargs: Any) -> None:
28
28
  """
@@ -57,23 +57,23 @@ class SalesforceHandler(APIHandler):
57
57
  return self.connection
58
58
 
59
59
  # Mandatory connection parameters.
60
- if not all(key in self.connection_data for key in ['username', 'password', 'client_id', 'client_secret']):
60
+ if not all(key in self.connection_data for key in ["username", "password", "client_id", "client_secret"]):
61
61
  raise ValueError("Required parameters (username, password, client_id, client_secret) must be provided.")
62
62
 
63
63
  try:
64
64
  self.connection = salesforce_api.Salesforce(
65
- username=self.connection_data['username'],
66
- password=self.connection_data['password'],
67
- client_id=self.connection_data['client_id'],
68
- client_secret=self.connection_data['client_secret'],
69
- is_sandbox=self.connection_data.get('is_sandbox', False)
65
+ username=self.connection_data["username"],
66
+ password=self.connection_data["password"],
67
+ client_id=self.connection_data["client_id"],
68
+ client_secret=self.connection_data["client_secret"],
69
+ is_sandbox=self.connection_data.get("is_sandbox", False),
70
70
  )
71
71
  self.is_connected = True
72
72
 
73
73
  # Register Salesforce tables.
74
74
  for resource_name in self._get_resource_names():
75
75
  table_class = create_table_class(resource_name)
76
- self._register_table(resource_name.lower(), table_class(self))
76
+ self._register_table(resource_name, table_class(self))
77
77
 
78
78
  return self.connection
79
79
  except AuthenticationError as auth_error:
@@ -96,10 +96,10 @@ class SalesforceHandler(APIHandler):
96
96
  self.connect()
97
97
  response.success = True
98
98
  except (AuthenticationError, ValueError) as known_error:
99
- logger.error(f'Connection check to Salesforce failed, {known_error}!')
99
+ logger.error(f"Connection check to Salesforce failed, {known_error}!")
100
100
  response.error_message = str(known_error)
101
101
  except Exception as unknown_error:
102
- logger.error(f'Connection check to Salesforce failed due to an unknown error, {unknown_error}!')
102
+ logger.error(f"Connection check to Salesforce failed due to an unknown error, {unknown_error}!")
103
103
  response.error_message = str(unknown_error)
104
104
 
105
105
  self.is_connected = response.success
@@ -123,7 +123,7 @@ class SalesforceHandler(APIHandler):
123
123
 
124
124
  parsed_results = []
125
125
  for result in results:
126
- del result['attributes']
126
+ del result["attributes"]
127
127
 
128
128
  # Check if the result contains any of the other Salesforce resources.
129
129
  if any(key in self.resource_names for key in result.keys()):
@@ -131,8 +131,10 @@ class SalesforceHandler(APIHandler):
131
131
  parsed_result = {}
132
132
  for key, value in result.items():
133
133
  if key in self.resource_names:
134
- del value['attributes']
135
- parsed_result.update({f'{key}_{sub_key}': sub_value for sub_key, sub_value in value.items()})
134
+ del value["attributes"]
135
+ parsed_result.update(
136
+ {f"{key}_{sub_key}": sub_value for sub_key, sub_value in value.items()}
137
+ )
136
138
 
137
139
  else:
138
140
  parsed_result[key] = value
@@ -142,24 +144,13 @@ class SalesforceHandler(APIHandler):
142
144
  else:
143
145
  parsed_results.append(result)
144
146
 
145
- response = Response(
146
- RESPONSE_TYPE.TABLE,
147
- pd.DataFrame(parsed_results)
148
- )
147
+ response = Response(RESPONSE_TYPE.TABLE, pd.DataFrame(parsed_results))
149
148
  except RestRequestCouldNotBeUnderstoodError as rest_error:
150
- logger.error(f'Error running query: {query} on Salesforce, {rest_error}!')
151
- response = Response(
152
- RESPONSE_TYPE.ERROR,
153
- error_code=0,
154
- error_message=str(rest_error)
155
- )
149
+ logger.error(f"Error running query: {query} on Salesforce, {rest_error}!")
150
+ response = Response(RESPONSE_TYPE.ERROR, error_code=0, error_message=str(rest_error))
156
151
  except Exception as unknown_error:
157
- logger.error(f'Error running query: {query} on Salesforce, {unknown_error}!')
158
- response = Response(
159
- RESPONSE_TYPE.ERROR,
160
- error_code=0,
161
- error_message=str(unknown_error)
162
- )
152
+ logger.error(f"Error running query: {query} on Salesforce, {unknown_error}!")
153
+ response = Response(RESPONSE_TYPE.ERROR, error_code=0, error_message=str(unknown_error))
163
154
 
164
155
  return response
165
156
 
@@ -171,6 +162,34 @@ class SalesforceHandler(APIHandler):
171
162
  None
172
163
  """
173
164
  if not self.resource_names:
174
- self.resource_names = [resource['name'] for resource in self.connection.sobjects.describe()['sobjects']]
165
+ # Fetch the queryable list of Salesforce resources (sobjects).
166
+ self.resource_names = [
167
+ resource["name"]
168
+ for resource in self.connection.sobjects.describe()["sobjects"]
169
+ if resource.get("queryable", False)
170
+ ]
175
171
 
176
172
  return self.resource_names
173
+
174
+ def meta_get_tables(self, table_names: Optional[List[str]] = None) -> Response:
175
+ """
176
+ Retrieves metadata for the specified tables (or all tables if no list is provided).
177
+
178
+ Args:
179
+ table_names (List): A list of table names for which to retrieve metadata.
180
+
181
+ Returns:
182
+ Response: A response object containing the table metadata.
183
+ """
184
+ connection = self.connect()
185
+
186
+ # Retrieve the metadata for all Salesforce resources.
187
+ main_metadata = connection.sobjects.describe()
188
+
189
+ if table_names:
190
+ # Filter the metadata for the specified tables.
191
+ main_metadata = [resource for resource in main_metadata["sobjects"] if resource["name"] in table_names]
192
+ else:
193
+ main_metadata = main_metadata["sobjects"]
194
+
195
+ return super().meta_get_tables(table_names=table_names, main_metadata=main_metadata)
@@ -2,8 +2,9 @@ from typing import Dict, List, Text
2
2
 
3
3
  from mindsdb_sql_parser.ast import Select, Star, Identifier
4
4
  import pandas as pd
5
+ from salesforce_api.exceptions import RestRequestCouldNotBeUnderstoodError
5
6
 
6
- from mindsdb.integrations.libs.api_handler import APIResource
7
+ from mindsdb.integrations.libs.api_handler import MetaAPIResource
7
8
  from mindsdb.integrations.utilities.sql_utils import FilterCondition, FilterOperator
8
9
  from mindsdb.utilities import log
9
10
 
@@ -11,16 +12,28 @@ from mindsdb.utilities import log
11
12
  logger = log.getLogger(__name__)
12
13
 
13
14
 
14
- def create_table_class(resource_name: Text) -> APIResource:
15
+ def create_table_class(resource_name: Text) -> MetaAPIResource:
15
16
  """
16
17
  Creates a table class for the given Salesforce resource.
17
18
  """
18
19
 
19
- class AnyTable(APIResource):
20
+ class AnyTable(MetaAPIResource):
20
21
  """
21
22
  This is the table abstraction for any resource of the Salesforce API.
22
23
  """
23
24
 
25
+ def __init__(self, *args, table_name=None, **kwargs):
26
+ """
27
+ Initializes the AnyTable class.
28
+
29
+ Args:
30
+ *args: Variable length argument list.
31
+ table_name (str): The name of the table that represents the Salesforce resource.
32
+ **kwargs: Arbitrary keyword arguments.
33
+ """
34
+ super().__init__(*args, table_name=table_name, **kwargs)
35
+ self.resource_metadata = None
36
+
24
37
  def select(self, query: Select) -> pd.DataFrame:
25
38
  """
26
39
  Executes a SELECT SQL query represented by an ASTNode object on the Salesforce resource and retrieves the data (if any).
@@ -53,7 +66,7 @@ def create_table_class(resource_name: Text) -> APIResource:
53
66
  results = client.sobjects.query(query_str)
54
67
 
55
68
  for result in results:
56
- del result['attributes']
69
+ del result["attributes"]
57
70
 
58
71
  df = pd.DataFrame(results)
59
72
  df.rename(columns=column_aliases, inplace=True)
@@ -107,7 +120,7 @@ def create_table_class(resource_name: Text) -> APIResource:
107
120
  conditions (List[FilterCondition]): The conditions to be validated.
108
121
  """
109
122
  # Salesforce API does not support filtering items based on attributes other than 'Id'. Raise an error if any other column is used.
110
- if len(conditions) != 1 or conditions[0].column != 'Id':
123
+ if len(conditions) != 1 or conditions[0].column != "Id":
111
124
  raise ValueError("Only the 'Id' column can be used to filter items.")
112
125
 
113
126
  # Only the 'equals' and 'in' operators can be used on the 'Id' column for deletion. Raise an error if any other operator is used.
@@ -116,6 +129,19 @@ def create_table_class(resource_name: Text) -> APIResource:
116
129
 
117
130
  return conditions[0].value if isinstance(conditions[0].value, list) else [conditions[0].value]
118
131
 
132
+ def _get_resource_metadata(self) -> Dict:
133
+ """
134
+ Retrieves metadata about the Salesforce resource.
135
+
136
+ Returns:
137
+ Dict: A dictionary containing metadata about the Salesforce resource.
138
+ """
139
+ if self.resource_metadata:
140
+ return self.resource_metadata
141
+
142
+ client = self.handler.connect()
143
+ return getattr(client.sobjects, resource_name).describe()
144
+
119
145
  def get_columns(self) -> List[Text]:
120
146
  """
121
147
  Retrieves the attributes (columns) of the Salesforce resource.
@@ -123,7 +149,111 @@ def create_table_class(resource_name: Text) -> APIResource:
123
149
  Returns:
124
150
  List[Text]: A list of Attributes (columns) of the Salesforce resource.
125
151
  """
152
+ return [field["name"] for field in self._get_resource_metadata()["fields"]]
153
+
154
+ def meta_get_tables(self, table_name: str, main_metadata: Dict) -> Dict:
155
+ """
156
+ Retrieves table metadata for the Salesforce resource.
157
+
158
+ Args:
159
+ table_name (str): The name given to the table that represents the Salesforce resource.
160
+ main_metadata (Dict): The main metadata dictionary containing information about all Salesforce resources.
161
+
162
+ Returns:
163
+ Dict: A dictionary containing table metadata for the Salesforce resource.
164
+ """
126
165
  client = self.handler.connect()
127
- return [field['name'] for field in getattr(client.sobjects, resource_name).describe()['fields']]
166
+
167
+ resource_metadata = next(
168
+ (resource for resource in main_metadata if resource["name"] == resource_name),
169
+ )
170
+
171
+ # Get row count if Id column is aggregatable.
172
+ row_count = None
173
+ # if next(field for field in resource_metadata['fields'] if field['name'] == 'Id').get('aggregatable', False):
174
+ try:
175
+ row_count = client.sobjects.query(f"SELECT COUNT(Id) FROM {resource_name}")[0]["expr0"]
176
+ except RestRequestCouldNotBeUnderstoodError as request_error:
177
+ logger.warning(f"Failed to get row count for {resource_name}: {request_error}")
178
+
179
+ return {
180
+ "table_name": table_name,
181
+ "table_type": "BASE TABLE",
182
+ "table_description": resource_metadata.get("label", ""),
183
+ "row_count": row_count,
184
+ }
185
+
186
+ def meta_get_columns(self, table_name: str) -> List[Dict]:
187
+ """
188
+ Retrieves column metadata for the Salesforce resource.
189
+
190
+ Args:
191
+ table_name (str): The name given to the table that represents the Salesforce resource.
192
+
193
+ Returns:
194
+ List[Dict]: A list of dictionaries containing column metadata for the Salesforce resource.
195
+ """
196
+ resource_metadata = self._get_resource_metadata()
197
+
198
+ column_metadata = []
199
+ for field in resource_metadata["fields"]:
200
+ column_metadata.append(
201
+ {
202
+ "table_name": table_name,
203
+ "column_name": field["name"],
204
+ "data_type": field["type"],
205
+ "is_nullable": field.get("nillable", False),
206
+ }
207
+ )
208
+
209
+ return column_metadata
210
+
211
+ def meta_get_primary_keys(self, table_name: str) -> List[Dict]:
212
+ """
213
+ Retrieves the primary keys for the Salesforce resource.
214
+
215
+ Args:
216
+ table_name (str): The name given to the table that represents the Salesforce resource.
217
+
218
+ Returns:
219
+ List[Dict]: A list of dictionaries containing primary key metadata for the Salesforce resource.
220
+ """
221
+ return [
222
+ {
223
+ "table_name": table_name,
224
+ "column_name": "Id",
225
+ }
226
+ ]
227
+
228
+ def meta_get_foreign_keys(self, table_name: str, all_tables: List[str]) -> List[Dict]:
229
+ """
230
+ Retrieves the foreign keys for the Salesforce resource.
231
+
232
+ Args:
233
+ table_name (str): The name given to the table that represents the Salesforce resource.
234
+ all_tables (List[str]): A list of all table names in the Salesforce database.
235
+
236
+ Returns:
237
+ List[Dict]: A list of dictionaries containing foreign key metadata for the Salesforce resource.
238
+ """
239
+ resource_metadata = self._get_resource_metadata()
240
+
241
+ foreign_key_metadata = []
242
+ for child_relationship in resource_metadata.get("childRelationships", []):
243
+ # Skip if the child relationship is not one of the supported tables.
244
+ child_table_name = child_relationship["childSObject"]
245
+ if child_table_name not in all_tables:
246
+ continue
247
+
248
+ foreign_key_metadata.append(
249
+ {
250
+ "parent_table_name": table_name,
251
+ "parent_column_name": "Id",
252
+ "child_table_name": child_table_name,
253
+ "child_column_name": child_relationship["field"],
254
+ }
255
+ )
256
+
257
+ return foreign_key_metadata
128
258
 
129
259
  return AnyTable