MindsDB 25.9.2.0a1__py3-none-any.whl → 25.10.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 (164) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +40 -29
  3. mindsdb/api/a2a/__init__.py +1 -1
  4. mindsdb/api/a2a/agent.py +16 -10
  5. mindsdb/api/a2a/common/server/server.py +7 -3
  6. mindsdb/api/a2a/common/server/task_manager.py +12 -5
  7. mindsdb/api/a2a/common/types.py +66 -0
  8. mindsdb/api/a2a/task_manager.py +65 -17
  9. mindsdb/api/common/middleware.py +10 -12
  10. mindsdb/api/executor/command_executor.py +51 -40
  11. mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
  12. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -13
  13. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +101 -49
  14. mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
  15. mindsdb/api/executor/datahub/datanodes/system_tables.py +3 -2
  16. mindsdb/api/executor/exceptions.py +29 -10
  17. mindsdb/api/executor/planner/plan_join.py +17 -3
  18. mindsdb/api/executor/planner/query_prepare.py +2 -20
  19. mindsdb/api/executor/sql_query/sql_query.py +74 -74
  20. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +1 -2
  21. mindsdb/api/executor/sql_query/steps/subselect_step.py +0 -1
  22. mindsdb/api/executor/utilities/functions.py +6 -6
  23. mindsdb/api/executor/utilities/sql.py +37 -20
  24. mindsdb/api/http/gui.py +5 -11
  25. mindsdb/api/http/initialize.py +75 -61
  26. mindsdb/api/http/namespaces/agents.py +10 -15
  27. mindsdb/api/http/namespaces/analysis.py +13 -20
  28. mindsdb/api/http/namespaces/auth.py +1 -1
  29. mindsdb/api/http/namespaces/chatbots.py +0 -5
  30. mindsdb/api/http/namespaces/config.py +15 -11
  31. mindsdb/api/http/namespaces/databases.py +140 -201
  32. mindsdb/api/http/namespaces/file.py +17 -4
  33. mindsdb/api/http/namespaces/handlers.py +17 -7
  34. mindsdb/api/http/namespaces/knowledge_bases.py +28 -7
  35. mindsdb/api/http/namespaces/models.py +94 -126
  36. mindsdb/api/http/namespaces/projects.py +13 -22
  37. mindsdb/api/http/namespaces/sql.py +33 -25
  38. mindsdb/api/http/namespaces/tab.py +27 -37
  39. mindsdb/api/http/namespaces/views.py +1 -1
  40. mindsdb/api/http/start.py +16 -10
  41. mindsdb/api/mcp/__init__.py +2 -1
  42. mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +15 -20
  43. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +26 -50
  44. mindsdb/api/mysql/mysql_proxy/utilities/__init__.py +0 -1
  45. mindsdb/api/mysql/mysql_proxy/utilities/dump.py +8 -2
  46. mindsdb/integrations/handlers/byom_handler/byom_handler.py +165 -190
  47. mindsdb/integrations/handlers/databricks_handler/databricks_handler.py +98 -46
  48. mindsdb/integrations/handlers/druid_handler/druid_handler.py +32 -40
  49. mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
  50. mindsdb/integrations/handlers/gitlab_handler/gitlab_handler.py +5 -2
  51. mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
  52. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +438 -100
  53. mindsdb/integrations/handlers/mssql_handler/requirements_odbc.txt +3 -0
  54. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +235 -3
  55. mindsdb/integrations/handlers/oracle_handler/__init__.py +2 -0
  56. mindsdb/integrations/handlers/oracle_handler/connection_args.py +7 -1
  57. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +321 -16
  58. mindsdb/integrations/handlers/oracle_handler/requirements.txt +1 -1
  59. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +14 -2
  60. mindsdb/integrations/handlers/shopify_handler/requirements.txt +1 -0
  61. mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +80 -13
  62. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +2 -1
  63. mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
  64. mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
  65. mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +4 -4
  66. mindsdb/integrations/handlers/zendesk_handler/zendesk_tables.py +144 -111
  67. mindsdb/integrations/libs/api_handler.py +10 -10
  68. mindsdb/integrations/libs/base.py +4 -4
  69. mindsdb/integrations/libs/llm/utils.py +2 -2
  70. mindsdb/integrations/libs/ml_handler_process/create_engine_process.py +4 -7
  71. mindsdb/integrations/libs/ml_handler_process/func_call_process.py +2 -7
  72. mindsdb/integrations/libs/ml_handler_process/learn_process.py +37 -47
  73. mindsdb/integrations/libs/ml_handler_process/update_engine_process.py +4 -7
  74. mindsdb/integrations/libs/ml_handler_process/update_process.py +2 -7
  75. mindsdb/integrations/libs/process_cache.py +132 -140
  76. mindsdb/integrations/libs/response.py +18 -12
  77. mindsdb/integrations/libs/vectordatabase_handler.py +26 -0
  78. mindsdb/integrations/utilities/files/file_reader.py +6 -7
  79. mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/__init__.py +1 -0
  80. mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/snowflake_jwt_gen.py +151 -0
  81. mindsdb/integrations/utilities/rag/config_loader.py +37 -26
  82. mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +83 -30
  83. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +4 -4
  84. mindsdb/integrations/utilities/rag/retrievers/sql_retriever.py +55 -133
  85. mindsdb/integrations/utilities/rag/settings.py +58 -133
  86. mindsdb/integrations/utilities/rag/splitters/file_splitter.py +5 -15
  87. mindsdb/interfaces/agents/agents_controller.py +2 -3
  88. mindsdb/interfaces/agents/constants.py +0 -2
  89. mindsdb/interfaces/agents/litellm_server.py +34 -58
  90. mindsdb/interfaces/agents/mcp_client_agent.py +10 -10
  91. mindsdb/interfaces/agents/mindsdb_database_agent.py +5 -5
  92. mindsdb/interfaces/agents/run_mcp_agent.py +12 -21
  93. mindsdb/interfaces/chatbot/chatbot_task.py +20 -23
  94. mindsdb/interfaces/chatbot/polling.py +30 -18
  95. mindsdb/interfaces/data_catalog/data_catalog_loader.py +16 -17
  96. mindsdb/interfaces/data_catalog/data_catalog_reader.py +15 -4
  97. mindsdb/interfaces/database/data_handlers_cache.py +190 -0
  98. mindsdb/interfaces/database/database.py +3 -3
  99. mindsdb/interfaces/database/integrations.py +7 -110
  100. mindsdb/interfaces/database/projects.py +2 -6
  101. mindsdb/interfaces/database/views.py +1 -4
  102. mindsdb/interfaces/file/file_controller.py +6 -6
  103. mindsdb/interfaces/functions/controller.py +1 -1
  104. mindsdb/interfaces/functions/to_markdown.py +2 -2
  105. mindsdb/interfaces/jobs/jobs_controller.py +5 -9
  106. mindsdb/interfaces/jobs/scheduler.py +3 -9
  107. mindsdb/interfaces/knowledge_base/controller.py +244 -128
  108. mindsdb/interfaces/knowledge_base/evaluate.py +36 -41
  109. mindsdb/interfaces/knowledge_base/executor.py +11 -0
  110. mindsdb/interfaces/knowledge_base/llm_client.py +51 -17
  111. mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
  112. mindsdb/interfaces/model/model_controller.py +172 -168
  113. mindsdb/interfaces/query_context/context_controller.py +14 -2
  114. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +10 -14
  115. mindsdb/interfaces/skills/retrieval_tool.py +43 -50
  116. mindsdb/interfaces/skills/skill_tool.py +2 -2
  117. mindsdb/interfaces/skills/skills_controller.py +1 -4
  118. mindsdb/interfaces/skills/sql_agent.py +25 -19
  119. mindsdb/interfaces/storage/db.py +16 -6
  120. mindsdb/interfaces/storage/fs.py +114 -169
  121. mindsdb/interfaces/storage/json.py +19 -18
  122. mindsdb/interfaces/tabs/tabs_controller.py +49 -72
  123. mindsdb/interfaces/tasks/task_monitor.py +3 -9
  124. mindsdb/interfaces/tasks/task_thread.py +7 -9
  125. mindsdb/interfaces/triggers/trigger_task.py +7 -13
  126. mindsdb/interfaces/triggers/triggers_controller.py +47 -52
  127. mindsdb/migrations/migrate.py +16 -16
  128. mindsdb/utilities/api_status.py +58 -0
  129. mindsdb/utilities/config.py +68 -2
  130. mindsdb/utilities/exception.py +40 -1
  131. mindsdb/utilities/fs.py +0 -1
  132. mindsdb/utilities/hooks/profiling.py +17 -14
  133. mindsdb/utilities/json_encoder.py +24 -10
  134. mindsdb/utilities/langfuse.py +40 -45
  135. mindsdb/utilities/log.py +272 -0
  136. mindsdb/utilities/ml_task_queue/consumer.py +52 -58
  137. mindsdb/utilities/ml_task_queue/producer.py +26 -30
  138. mindsdb/utilities/render/sqlalchemy_render.py +22 -20
  139. mindsdb/utilities/starters.py +0 -10
  140. mindsdb/utilities/utils.py +2 -2
  141. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0.dist-info}/METADATA +286 -267
  142. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0.dist-info}/RECORD +145 -159
  143. mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
  144. mindsdb/api/postgres/__init__.py +0 -0
  145. mindsdb/api/postgres/postgres_proxy/__init__.py +0 -0
  146. mindsdb/api/postgres/postgres_proxy/executor/__init__.py +0 -1
  147. mindsdb/api/postgres/postgres_proxy/executor/executor.py +0 -189
  148. mindsdb/api/postgres/postgres_proxy/postgres_packets/__init__.py +0 -0
  149. mindsdb/api/postgres/postgres_proxy/postgres_packets/errors.py +0 -322
  150. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_fields.py +0 -34
  151. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message.py +0 -31
  152. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +0 -1265
  153. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_identifiers.py +0 -31
  154. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +0 -253
  155. mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +0 -477
  156. mindsdb/api/postgres/postgres_proxy/utilities/__init__.py +0 -10
  157. mindsdb/api/postgres/start.py +0 -11
  158. mindsdb/integrations/handlers/mssql_handler/tests/__init__.py +0 -0
  159. mindsdb/integrations/handlers/mssql_handler/tests/test_mssql_handler.py +0 -169
  160. mindsdb/integrations/handlers/oracle_handler/tests/__init__.py +0 -0
  161. mindsdb/integrations/handlers/oracle_handler/tests/test_oracle_handler.py +0 -32
  162. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0.dist-info}/WHEEL +0 -0
  163. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0.dist-info}/licenses/LICENSE +0 -0
  164. {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
- from alembic.command import upgrade, autogen # noqa
3
+ from alembic.command import upgrade, autogen # noqa
4
4
  from alembic.config import Config
5
5
  from alembic.script import ScriptDirectory
6
6
  from alembic.script.revision import ResolutionError
@@ -15,20 +15,19 @@ logger = log.getLogger(__name__)
15
15
 
16
16
  # This is a migration that is like a 'base version'. Applying only this
17
17
  # migration to a fresh DB is equivalent to applying all previous migrations.
18
- current_checkpoint = '9f150e4f9a05'
18
+ current_checkpoint = "9f150e4f9a05"
19
19
 
20
20
 
21
21
  def apply_checkpoint_migration(script) -> None:
22
- """Apply the checkpoint migration to the database.
23
- """
22
+ """Apply the checkpoint migration to the database."""
24
23
  with db.engine.begin() as connection:
25
24
  context = MigrationContext.configure(
26
25
  connection,
27
26
  opts={
28
- 'as_sql': False,
29
- 'starting_rev': None, # ignore current version
30
- 'destination_rev': current_checkpoint,
31
- }
27
+ "as_sql": False,
28
+ "starting_rev": None, # ignore current version
29
+ "destination_rev": current_checkpoint,
30
+ },
32
31
  )
33
32
  revision = script.get_revision(current_checkpoint)
34
33
  if not revision:
@@ -41,7 +40,7 @@ def apply_checkpoint_migration(script) -> None:
41
40
 
42
41
 
43
42
  def get_current_revision() -> str | None:
44
- """ Get the current revision of the database.
43
+ """Get the current revision of the database.
45
44
 
46
45
  Returns:
47
46
  str | None: The current revision of the database.
@@ -52,17 +51,18 @@ def get_current_revision() -> str | None:
52
51
 
53
52
 
54
53
  def migrate_to_head():
55
- """ Trying to update database to head revision.
56
- If alembic unable to recognize current revision (In case when database version is newer than backend)
57
- then do nothing.
54
+ """Trying to update database to head revision.
55
+ If alembic unable to recognize current revision (In case when database version is newer than backend)
56
+ then do nothing.
58
57
  """
58
+ logger.debug("Applying database migrations")
59
59
 
60
- config_file = Path(__file__).parent / 'alembic.ini'
60
+ config_file = Path(__file__).parent / "alembic.ini"
61
61
  config = Config(config_file)
62
62
 
63
63
  # mindsdb can runs not from project directory
64
- script_location_abc = config_file.parent / config.get_main_option('script_location')
65
- config.set_main_option('script_location', str(script_location_abc))
64
+ script_location_abc = config_file.parent / config.get_main_option("script_location")
65
+ config.set_main_option("script_location", str(script_location_abc))
66
66
 
67
67
  script = ScriptDirectory.from_config(config)
68
68
  cur_revision = get_current_revision()
@@ -81,7 +81,7 @@ def migrate_to_head():
81
81
  return
82
82
 
83
83
  logger.info("Migrations are available. Applying updates to the database.")
84
- upgrade(config=config, revision='head')
84
+ upgrade(config=config, revision="head")
85
85
 
86
86
 
87
87
  if __name__ == "__main__":
@@ -0,0 +1,58 @@
1
+ import os
2
+ import json
3
+
4
+ from mindsdb.utilities.config import config
5
+ from mindsdb.utilities import log
6
+
7
+
8
+ logger = log.getLogger(__name__)
9
+ _api_status_file = None
10
+
11
+
12
+ def _get_api_status_file():
13
+ global _api_status_file
14
+ if _api_status_file is None:
15
+ # Use a temporary file that can be shared across processes.
16
+ temp_dir = config["paths"]["tmp"]
17
+ _api_status_file = os.path.join(temp_dir, "mindsdb_api_status.json")
18
+ # Overwrite the file if it exists.
19
+ if os.path.exists(_api_status_file):
20
+ try:
21
+ os.remove(_api_status_file)
22
+ except OSError:
23
+ logger.exception(f"Error removing existing API status file: {_api_status_file}")
24
+
25
+ return _api_status_file
26
+
27
+
28
+ def get_api_status():
29
+ """Get the current API status from the shared file."""
30
+ status_file = _get_api_status_file()
31
+ try:
32
+ if os.path.exists(status_file):
33
+ with open(status_file, "r") as f:
34
+ return json.load(f)
35
+ except (json.JSONDecodeError, IOError):
36
+ pass
37
+ return {}
38
+
39
+
40
+ def set_api_status(api_name: str, status: bool):
41
+ """Set the status of an API in the shared file."""
42
+ status_file = _get_api_status_file()
43
+ current_status = get_api_status()
44
+ current_status[api_name] = status
45
+
46
+ # Write atomically to avoid race conditions.
47
+ temp_file = status_file + ".tmp"
48
+ try:
49
+ with open(temp_file, "w") as f:
50
+ json.dump(current_status, f)
51
+ os.replace(temp_file, status_file)
52
+ except IOError:
53
+ # Clean up temp file if it exists.
54
+ if os.path.exists(temp_file):
55
+ try:
56
+ os.remove(temp_file)
57
+ except OSError:
58
+ pass
@@ -170,6 +170,7 @@ class Config:
170
170
  "restart_on_failure": True,
171
171
  "max_restart_count": 1,
172
172
  "max_restart_interval_seconds": 60,
173
+ "a2wsgi": {"workers": 10, "send_queue_size": 10},
173
174
  },
174
175
  "mysql": {
175
176
  "host": api_host,
@@ -180,7 +181,6 @@ class Config:
180
181
  "max_restart_count": 1,
181
182
  "max_restart_interval_seconds": 60,
182
183
  },
183
- "postgres": {"host": api_host, "port": "55432", "database": "mindsdb"},
184
184
  "litellm": {
185
185
  "host": "0.0.0.0", # API server binds to all interfaces by default
186
186
  "port": "8000",
@@ -233,7 +233,17 @@ class Config:
233
233
 
234
234
  # region storage root path
235
235
  if os.environ.get("MINDSDB_STORAGE_DIR", "") != "":
236
- self._env_config["paths"] = {"root": Path(os.environ["MINDSDB_STORAGE_DIR"])}
236
+ storage_root_path = Path(os.environ["MINDSDB_STORAGE_DIR"])
237
+ self._env_config["paths"] = {
238
+ "root": storage_root_path,
239
+ "content": storage_root_path / "content",
240
+ "storage": storage_root_path / "storage",
241
+ "static": storage_root_path / "static",
242
+ "tmp": storage_root_path / "tmp",
243
+ "log": storage_root_path / "log",
244
+ "cache": storage_root_path / "cache",
245
+ "locks": storage_root_path / "locks",
246
+ }
237
247
  # endregion
238
248
 
239
249
  # region vars: permanent storage disabled?
@@ -300,6 +310,54 @@ class Config:
300
310
  self._env_config["default_reranking_model"] = {
301
311
  "api_key": os.environ["MINDSDB_DEFAULT_RERANKING_MODEL_API_KEY"]
302
312
  }
313
+
314
+ # Reranker configuration from environment variables
315
+ reranker_config = {}
316
+ if os.environ.get("MINDSDB_RERANKER_N", "") != "":
317
+ try:
318
+ reranker_config["n"] = int(os.environ["MINDSDB_RERANKER_N"])
319
+ except ValueError:
320
+ raise ValueError(f"MINDSDB_RERANKER_N must be an integer, got: {os.environ['MINDSDB_RERANKER_N']}")
321
+
322
+ if os.environ.get("MINDSDB_RERANKER_LOGPROBS", "") != "":
323
+ logprobs_value = os.environ["MINDSDB_RERANKER_LOGPROBS"].lower()
324
+ if logprobs_value in ("true", "1", "yes", "y"):
325
+ reranker_config["logprobs"] = True
326
+ elif logprobs_value in ("false", "0", "no", "n"):
327
+ reranker_config["logprobs"] = False
328
+ else:
329
+ raise ValueError(
330
+ f"MINDSDB_RERANKER_LOGPROBS must be a boolean value, got: {os.environ['MINDSDB_RERANKER_LOGPROBS']}"
331
+ )
332
+
333
+ if os.environ.get("MINDSDB_RERANKER_TOP_LOGPROBS", "") != "":
334
+ try:
335
+ reranker_config["top_logprobs"] = int(os.environ["MINDSDB_RERANKER_TOP_LOGPROBS"])
336
+ except ValueError:
337
+ raise ValueError(
338
+ f"MINDSDB_RERANKER_TOP_LOGPROBS must be an integer, got: {os.environ['MINDSDB_RERANKER_TOP_LOGPROBS']}"
339
+ )
340
+
341
+ if os.environ.get("MINDSDB_RERANKER_MAX_TOKENS", "") != "":
342
+ try:
343
+ reranker_config["max_tokens"] = int(os.environ["MINDSDB_RERANKER_MAX_TOKENS"])
344
+ except ValueError:
345
+ raise ValueError(
346
+ f"MINDSDB_RERANKER_MAX_TOKENS must be an integer, got: {os.environ['MINDSDB_RERANKER_MAX_TOKENS']}"
347
+ )
348
+
349
+ if os.environ.get("MINDSDB_RERANKER_VALID_CLASS_TOKENS", "") != "":
350
+ try:
351
+ reranker_config["valid_class_tokens"] = os.environ["MINDSDB_RERANKER_VALID_CLASS_TOKENS"].split(",")
352
+ except ValueError:
353
+ raise ValueError(
354
+ f"MINDSDB_RERANKER_VALID_CLASS_TOKENS must be a comma-separated list of strings, got: {os.environ['MINDSDB_RERANKER_VALID_CLASS_TOKENS']}"
355
+ )
356
+
357
+ if reranker_config:
358
+ if "default_reranking_model" not in self._env_config:
359
+ self._env_config["default_reranking_model"] = {}
360
+ self._env_config["default_reranking_model"].update(reranker_config)
303
361
  if os.environ.get("MINDSDB_DATA_CATALOG_ENABLED", "").lower() in ("1", "true"):
304
362
  self._env_config["data_catalog"] = {"enabled": True}
305
363
 
@@ -307,6 +365,14 @@ class Config:
307
365
  self._env_config["gui"]["open_on_start"] = False
308
366
  self._env_config["gui"]["autoupdate"] = False
309
367
 
368
+ mindsdb_gui_autoupdate = os.environ.get("MINDSDB_GUI_AUTOUPDATE", "").lower()
369
+ if mindsdb_gui_autoupdate in ("0", "false"):
370
+ self._env_config["gui"]["autoupdate"] = False
371
+ elif mindsdb_gui_autoupdate in ("1", "true"):
372
+ self._env_config["gui"]["autoupdate"] = True
373
+ elif mindsdb_gui_autoupdate != "":
374
+ raise ValueError(f"Wrong value of env var MINDSDB_GUI_AUTOUPDATE={mindsdb_gui_autoupdate}")
375
+
310
376
  def fetch_auto_config(self) -> bool:
311
377
  """Load dict readed from config.auto.json to `auto_config`.
312
378
  Do it only if `auto_config` was not loaded before or config.auto.json been changed.
@@ -1,7 +1,14 @@
1
1
  from textwrap import indent
2
2
 
3
3
 
4
- class BaseEntityException(Exception):
4
+ from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import ERR
5
+
6
+
7
+ class MindsDBError(Exception):
8
+ pass
9
+
10
+
11
+ class BaseEntityException(MindsDBError):
5
12
  """Base exception for entitys errors
6
13
 
7
14
  Attributes:
@@ -35,6 +42,38 @@ class EntityNotExistsError(BaseEntityException):
35
42
  super().__init__(message, entity_name)
36
43
 
37
44
 
45
+ class ParsingError(MindsDBError):
46
+ pass
47
+
48
+
49
+ class QueryError(MindsDBError):
50
+ def __init__(
51
+ self,
52
+ db_name: str | None = None,
53
+ db_type: str | None = None,
54
+ db_error_msg: str | None = None,
55
+ failed_query: str | None = None,
56
+ is_external: bool = True,
57
+ is_expected: bool = False,
58
+ ) -> None:
59
+ self.mysql_error_code = ERR.ER_UNKNOWN_ERROR
60
+ self.db_name = db_name
61
+ self.db_type = db_type
62
+ self.db_error_msg = db_error_msg
63
+ self.failed_query = failed_query
64
+ self.is_external = is_external
65
+ self.is_expected = is_expected
66
+
67
+ def __str__(self) -> str:
68
+ return format_db_error_message(
69
+ db_name=self.db_name,
70
+ db_type=self.db_type,
71
+ db_error_msg=self.db_error_msg,
72
+ failed_query=self.failed_query,
73
+ is_external=self.is_external,
74
+ )
75
+
76
+
38
77
  def format_db_error_message(
39
78
  db_name: str | None = None,
40
79
  db_type: str | None = None,
mindsdb/utilities/fs.py CHANGED
@@ -118,7 +118,6 @@ def clean_unlinked_process_marks() -> List[int]:
118
118
 
119
119
  except psutil.AccessDenied:
120
120
  logger.warning(f"access to {process_id} denied")
121
-
122
121
  continue
123
122
 
124
123
  except psutil.NoSuchProcess:
@@ -46,28 +46,31 @@ def send_profiling_results(profiling_data: dict):
46
46
  user=MINDSDB_PROFILING_DB_USER,
47
47
  password=MINDSDB_PROFILING_DB_PASSWORD,
48
48
  dbname="postgres",
49
- connect_timeout=5
49
+ connect_timeout=5,
50
50
  )
51
51
  except Exception:
52
- logger.error('cant get acceess to profiling database')
52
+ logger.warning("cant get acceess to profiling database")
53
53
  return
54
54
  cur = connection.cursor()
55
- cur.execute("""
55
+ cur.execute(
56
+ """
56
57
  insert into profiling
57
58
  (data, query, time, hostname, environment, api, total_time, company_id, instance_id)
58
59
  values
59
60
  (%s, %s, %s, %s, %s, %s, %s, %s, %s)
60
- """, (
61
- json.dumps(profiling["tree"]),
62
- profiling.get("query", "?"),
63
- time_start_at,
64
- profiling["hostname"],
65
- profiling.get("environment", "?"),
66
- profiling.get("api", "?"),
67
- profiling["tree"]["value"],
68
- profiling["company_id"],
69
- profiling["instance_id"]
70
- ))
61
+ """,
62
+ (
63
+ json.dumps(profiling["tree"]),
64
+ profiling.get("query", "?"),
65
+ time_start_at,
66
+ profiling["hostname"],
67
+ profiling.get("environment", "?"),
68
+ profiling.get("api", "?"),
69
+ profiling["tree"]["value"],
70
+ profiling["company_id"],
71
+ profiling["instance_id"],
72
+ ),
73
+ )
71
74
 
72
75
  connection.commit()
73
76
  cur.close()
@@ -1,13 +1,12 @@
1
1
  from datetime import datetime, date, timedelta
2
2
  from decimal import Decimal
3
- import numpy as np
4
3
  import pandas as pd
5
- import json
6
-
4
+ import numpy as np
5
+ import orjson
7
6
  from flask.json.provider import DefaultJSONProvider
8
7
 
9
8
 
10
- class CustomJSONEncoder(json.JSONEncoder):
9
+ class CustomJSONEncoder:
11
10
  def default(self, obj):
12
11
  if isinstance(obj, timedelta):
13
12
  return str(obj)
@@ -15,12 +14,10 @@ class CustomJSONEncoder(json.JSONEncoder):
15
14
  return obj.strftime("%Y-%m-%d %H:%M:%S.%f")
16
15
  if isinstance(obj, date):
17
16
  return obj.strftime("%Y-%m-%d")
17
+ if isinstance(obj, Decimal):
18
+ return float(obj)
18
19
  if isinstance(obj, np.bool_):
19
20
  return bool(obj)
20
- if isinstance(obj, np.int8) or isinstance(obj, np.int16) or isinstance(obj, np.int32) or isinstance(obj, np.int64):
21
- return int(obj)
22
- if isinstance(obj, np.float16) or isinstance(obj, np.float32) or isinstance(obj, np.float64) or isinstance(obj, Decimal):
23
- return float(obj)
24
21
  if isinstance(obj, np.ndarray):
25
22
  return obj.tolist()
26
23
  if pd.isnull(obj):
@@ -29,5 +26,22 @@ class CustomJSONEncoder(json.JSONEncoder):
29
26
  return str(obj)
30
27
 
31
28
 
32
- class CustomJSONProvider(CustomJSONEncoder, DefaultJSONProvider):
33
- ...
29
+ class ORJSONProvider(DefaultJSONProvider):
30
+ """
31
+ Use orjson to serialize data instead of flask json provider.
32
+ """
33
+
34
+ def dumps(self, obj, **kwargs):
35
+ return orjson.dumps(
36
+ obj,
37
+ option=(
38
+ orjson.OPT_SERIALIZE_NUMPY
39
+ | orjson.OPT_NON_STR_KEYS
40
+ # keep this for using CustomJSON encoder
41
+ | orjson.OPT_PASSTHROUGH_DATETIME
42
+ ),
43
+ default=CustomJSONEncoder().default,
44
+ ).decode("utf-8")
45
+
46
+ def loads(self, s, **kwargs):
47
+ return orjson.loads(s)
@@ -44,17 +44,19 @@ class LangfuseClientWrapper:
44
44
  Langfuse client wrapper. Defines Langfuse client configuration and initializes Langfuse client.
45
45
  """
46
46
 
47
- def __init__(self,
48
- public_key: str = LANGFUSE_PUBLIC_KEY,
49
- secret_key: str = LANGFUSE_SECRET_KEY,
50
- host: str = LANGFUSE_HOST,
51
- environment: str = LANGFUSE_ENVIRONMENT,
52
- release: str = LANGFUSE_RELEASE,
53
- debug: bool = LANGFUSE_DEBUG,
54
- timeout: int = LANGFUSE_TIMEOUT,
55
- sample_rate: float = LANGFUSE_SAMPLE_RATE,
56
- disable: bool = LANGFUSE_DISABLED,
57
- force_run: bool = LANGFUSE_FORCE_RUN) -> None:
47
+ def __init__(
48
+ self,
49
+ public_key: str = LANGFUSE_PUBLIC_KEY,
50
+ secret_key: str = LANGFUSE_SECRET_KEY,
51
+ host: str = LANGFUSE_HOST,
52
+ environment: str = LANGFUSE_ENVIRONMENT,
53
+ release: str = LANGFUSE_RELEASE,
54
+ debug: bool = LANGFUSE_DEBUG,
55
+ timeout: int = LANGFUSE_TIMEOUT,
56
+ sample_rate: float = LANGFUSE_SAMPLE_RATE,
57
+ disable: bool = LANGFUSE_DISABLED,
58
+ force_run: bool = LANGFUSE_FORCE_RUN,
59
+ ) -> None:
58
60
  """
59
61
  Initialize Langfuse client.
60
62
 
@@ -112,16 +114,18 @@ class LangfuseClientWrapper:
112
114
  release=release,
113
115
  debug=debug,
114
116
  timeout=timeout,
115
- sample_rate=sample_rate
117
+ sample_rate=sample_rate,
116
118
  )
117
119
 
118
- def setup_trace(self,
119
- name: str,
120
- input: typing.Optional[typing.Any] = None,
121
- tags: typing.Optional[typing.List] = None,
122
- metadata: typing.Optional[typing.Dict] = None,
123
- user_id: str = None,
124
- session_id: str = None) -> None:
120
+ def setup_trace(
121
+ self,
122
+ name: str,
123
+ input: typing.Optional[typing.Any] = None,
124
+ tags: typing.Optional[typing.List] = None,
125
+ metadata: typing.Optional[typing.Dict] = None,
126
+ user_id: str = None,
127
+ session_id: str = None,
128
+ ) -> None:
125
129
  """
126
130
  Setup trace. If Langfuse is disabled, nothing will be done.
127
131
  Args:
@@ -142,15 +146,10 @@ class LangfuseClientWrapper:
142
146
 
143
147
  try:
144
148
  self.trace = self.client.trace(
145
- name=name,
146
- input=input,
147
- metadata=self.metadata,
148
- tags=self.tags,
149
- user_id=user_id,
150
- session_id=session_id
149
+ name=name, input=input, metadata=self.metadata, tags=self.tags, user_id=user_id, session_id=session_id
151
150
  )
152
- except Exception as e:
153
- logger.error(f'Something went wrong while processing Langfuse trace {self.trace.id}: {str(e)}')
151
+ except Exception:
152
+ logger.exception(f"Something went wrong while processing Langfuse trace {self.trace.id}:")
154
153
 
155
154
  logger.info(f"Langfuse trace configured with ID: {self.trace.id}")
156
155
 
@@ -169,9 +168,7 @@ class LangfuseClientWrapper:
169
168
 
170
169
  return self.trace.id
171
170
 
172
- def start_span(self,
173
- name: str,
174
- input: typing.Optional[typing.Any] = None) -> typing.Optional['StatefulSpanClient']:
171
+ def start_span(self, name: str, input: typing.Optional[typing.Any] = None) -> typing.Optional["StatefulSpanClient"]:
175
172
  """
176
173
  Create span. If Langfuse is disabled, nothing will be done.
177
174
 
@@ -186,8 +183,7 @@ class LangfuseClientWrapper:
186
183
 
187
184
  return self.trace.span(name=name, input=input)
188
185
 
189
- def end_span_stream(self,
190
- span: typing.Optional['StatefulSpanClient'] = None) -> None:
186
+ def end_span_stream(self, span: typing.Optional["StatefulSpanClient"] = None) -> None:
191
187
  """
192
188
  End span. If Langfuse is disabled, nothing will happen.
193
189
  Args:
@@ -201,9 +197,9 @@ class LangfuseClientWrapper:
201
197
  span.end()
202
198
  self.trace.update()
203
199
 
204
- def end_span(self,
205
- span: typing.Optional['StatefulSpanClient'] = None,
206
- output: typing.Optional[typing.Any] = None) -> None:
200
+ def end_span(
201
+ self, span: typing.Optional["StatefulSpanClient"] = None, output: typing.Optional[typing.Any] = None
202
+ ) -> None:
207
203
  """
208
204
  End trace. If Langfuse is disabled, nothing will be done.
209
205
 
@@ -228,13 +224,12 @@ class LangfuseClientWrapper:
228
224
  try:
229
225
  # Ensure all batched traces are sent before fetching.
230
226
  self.client.flush()
231
- metadata['tool_usage'] = self._get_tool_usage()
227
+ metadata["tool_usage"] = self._get_tool_usage()
232
228
  self.trace.update(metadata=metadata)
229
+ except Exception:
230
+ logger.exception(f"Something went wrong while processing Langfuse trace {self.trace.id}:")
233
231
 
234
- except Exception as e:
235
- logger.error(f'Something went wrong while processing Langfuse trace {self.trace.id}: {str(e)}')
236
-
237
- def get_langchain_handler(self) -> typing.Optional['CallbackHandler']:
232
+ def get_langchain_handler(self) -> typing.Optional["CallbackHandler"]:
238
233
  """
239
234
  Get Langchain handler. If Langfuse is disabled, returns None.
240
235
  """
@@ -275,14 +270,14 @@ class LangfuseClientWrapper:
275
270
  fetched_trace = self.client.get_trace(self.trace.id)
276
271
  steps = [s.name for s in fetched_trace.observations]
277
272
  for step in steps:
278
- if 'AgentAction' in step:
279
- tool_name = step.split('-')[1]
273
+ if "AgentAction" in step:
274
+ tool_name = step.split("-")[1]
280
275
  if tool_name not in tool_usage:
281
276
  tool_usage[tool_name] = 0
282
277
  tool_usage[tool_name] += 1
283
278
  except TraceNotFoundError:
284
- logger.warning(f'Langfuse trace {self.trace.id} not found')
285
- except Exception as e:
286
- logger.error(f'Something went wrong while processing Langfuse trace {self.trace.id}: {str(e)}')
279
+ logger.warning(f"Langfuse trace {self.trace.id} not found")
280
+ except Exception:
281
+ logger.exception(f"Something went wrong while processing Langfuse trace {self.trace.id}:")
287
282
 
288
283
  return tool_usage