MindsDB 25.9.2.0a1__py3-none-any.whl → 25.10.0rc1__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.
- mindsdb/__about__.py +1 -1
- mindsdb/__main__.py +40 -29
- mindsdb/api/a2a/__init__.py +1 -1
- mindsdb/api/a2a/agent.py +16 -10
- mindsdb/api/a2a/common/server/server.py +7 -3
- mindsdb/api/a2a/common/server/task_manager.py +12 -5
- mindsdb/api/a2a/common/types.py +66 -0
- mindsdb/api/a2a/task_manager.py +65 -17
- mindsdb/api/common/middleware.py +10 -12
- mindsdb/api/executor/command_executor.py +51 -40
- mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
- mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +7 -13
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +101 -49
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
- mindsdb/api/executor/datahub/datanodes/system_tables.py +3 -2
- mindsdb/api/executor/exceptions.py +29 -10
- mindsdb/api/executor/planner/plan_join.py +17 -3
- mindsdb/api/executor/planner/query_prepare.py +2 -20
- mindsdb/api/executor/sql_query/sql_query.py +74 -74
- mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +1 -2
- mindsdb/api/executor/sql_query/steps/subselect_step.py +0 -1
- mindsdb/api/executor/utilities/functions.py +6 -6
- mindsdb/api/executor/utilities/sql.py +37 -20
- mindsdb/api/http/gui.py +5 -11
- mindsdb/api/http/initialize.py +75 -61
- mindsdb/api/http/namespaces/agents.py +10 -15
- mindsdb/api/http/namespaces/analysis.py +13 -20
- mindsdb/api/http/namespaces/auth.py +1 -1
- mindsdb/api/http/namespaces/chatbots.py +0 -5
- mindsdb/api/http/namespaces/config.py +15 -11
- mindsdb/api/http/namespaces/databases.py +140 -201
- mindsdb/api/http/namespaces/file.py +17 -4
- mindsdb/api/http/namespaces/handlers.py +17 -7
- mindsdb/api/http/namespaces/knowledge_bases.py +28 -7
- mindsdb/api/http/namespaces/models.py +94 -126
- mindsdb/api/http/namespaces/projects.py +13 -22
- mindsdb/api/http/namespaces/sql.py +33 -25
- mindsdb/api/http/namespaces/tab.py +27 -37
- mindsdb/api/http/namespaces/views.py +1 -1
- mindsdb/api/http/start.py +16 -10
- mindsdb/api/mcp/__init__.py +2 -1
- mindsdb/api/mysql/mysql_proxy/executor/mysql_executor.py +15 -20
- mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +26 -50
- mindsdb/api/mysql/mysql_proxy/utilities/__init__.py +0 -1
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +8 -2
- mindsdb/integrations/handlers/byom_handler/byom_handler.py +165 -190
- mindsdb/integrations/handlers/databricks_handler/databricks_handler.py +98 -46
- mindsdb/integrations/handlers/druid_handler/druid_handler.py +32 -40
- mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
- mindsdb/integrations/handlers/gitlab_handler/gitlab_handler.py +5 -2
- mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
- mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +438 -100
- mindsdb/integrations/handlers/mssql_handler/requirements_odbc.txt +3 -0
- mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +235 -3
- mindsdb/integrations/handlers/oracle_handler/__init__.py +2 -0
- mindsdb/integrations/handlers/oracle_handler/connection_args.py +7 -1
- mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +321 -16
- mindsdb/integrations/handlers/oracle_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +14 -2
- mindsdb/integrations/handlers/shopify_handler/shopify_handler.py +25 -12
- mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +2 -1
- mindsdb/integrations/handlers/statsforecast_handler/requirements.txt +1 -0
- mindsdb/integrations/handlers/statsforecast_handler/requirements_extra.txt +1 -0
- mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +4 -4
- mindsdb/integrations/handlers/zendesk_handler/zendesk_tables.py +144 -111
- mindsdb/integrations/libs/api_handler.py +10 -10
- mindsdb/integrations/libs/base.py +4 -4
- mindsdb/integrations/libs/llm/utils.py +2 -2
- mindsdb/integrations/libs/ml_handler_process/create_engine_process.py +4 -7
- mindsdb/integrations/libs/ml_handler_process/func_call_process.py +2 -7
- mindsdb/integrations/libs/ml_handler_process/learn_process.py +37 -47
- mindsdb/integrations/libs/ml_handler_process/update_engine_process.py +4 -7
- mindsdb/integrations/libs/ml_handler_process/update_process.py +2 -7
- mindsdb/integrations/libs/process_cache.py +132 -140
- mindsdb/integrations/libs/response.py +18 -12
- mindsdb/integrations/libs/vectordatabase_handler.py +26 -0
- mindsdb/integrations/utilities/files/file_reader.py +6 -7
- mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/__init__.py +1 -0
- mindsdb/integrations/utilities/handlers/auth_utilities/snowflake/snowflake_jwt_gen.py +151 -0
- mindsdb/integrations/utilities/rag/config_loader.py +37 -26
- mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +83 -30
- mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +4 -4
- mindsdb/integrations/utilities/rag/retrievers/sql_retriever.py +55 -133
- mindsdb/integrations/utilities/rag/settings.py +58 -133
- mindsdb/integrations/utilities/rag/splitters/file_splitter.py +5 -15
- mindsdb/interfaces/agents/agents_controller.py +2 -3
- mindsdb/interfaces/agents/constants.py +0 -2
- mindsdb/interfaces/agents/litellm_server.py +34 -58
- mindsdb/interfaces/agents/mcp_client_agent.py +10 -10
- mindsdb/interfaces/agents/mindsdb_database_agent.py +5 -5
- mindsdb/interfaces/agents/run_mcp_agent.py +12 -21
- mindsdb/interfaces/chatbot/chatbot_task.py +20 -23
- mindsdb/interfaces/chatbot/polling.py +30 -18
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +16 -17
- mindsdb/interfaces/data_catalog/data_catalog_reader.py +15 -4
- mindsdb/interfaces/database/data_handlers_cache.py +190 -0
- mindsdb/interfaces/database/database.py +3 -3
- mindsdb/interfaces/database/integrations.py +7 -110
- mindsdb/interfaces/database/projects.py +2 -6
- mindsdb/interfaces/database/views.py +1 -4
- mindsdb/interfaces/file/file_controller.py +6 -6
- mindsdb/interfaces/functions/controller.py +1 -1
- mindsdb/interfaces/functions/to_markdown.py +2 -2
- mindsdb/interfaces/jobs/jobs_controller.py +5 -9
- mindsdb/interfaces/jobs/scheduler.py +3 -9
- mindsdb/interfaces/knowledge_base/controller.py +244 -128
- mindsdb/interfaces/knowledge_base/evaluate.py +36 -41
- mindsdb/interfaces/knowledge_base/executor.py +11 -0
- mindsdb/interfaces/knowledge_base/llm_client.py +51 -17
- mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
- mindsdb/interfaces/model/model_controller.py +172 -168
- mindsdb/interfaces/query_context/context_controller.py +14 -2
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +10 -14
- mindsdb/interfaces/skills/retrieval_tool.py +43 -50
- mindsdb/interfaces/skills/skill_tool.py +2 -2
- mindsdb/interfaces/skills/skills_controller.py +1 -4
- mindsdb/interfaces/skills/sql_agent.py +25 -19
- mindsdb/interfaces/storage/db.py +16 -6
- mindsdb/interfaces/storage/fs.py +114 -169
- mindsdb/interfaces/storage/json.py +19 -18
- mindsdb/interfaces/tabs/tabs_controller.py +49 -72
- mindsdb/interfaces/tasks/task_monitor.py +3 -9
- mindsdb/interfaces/tasks/task_thread.py +7 -9
- mindsdb/interfaces/triggers/trigger_task.py +7 -13
- mindsdb/interfaces/triggers/triggers_controller.py +47 -52
- mindsdb/migrations/migrate.py +16 -16
- mindsdb/utilities/api_status.py +58 -0
- mindsdb/utilities/config.py +68 -2
- mindsdb/utilities/exception.py +40 -1
- mindsdb/utilities/fs.py +0 -1
- mindsdb/utilities/hooks/profiling.py +17 -14
- mindsdb/utilities/json_encoder.py +24 -10
- mindsdb/utilities/langfuse.py +40 -45
- mindsdb/utilities/log.py +272 -0
- mindsdb/utilities/ml_task_queue/consumer.py +52 -58
- mindsdb/utilities/ml_task_queue/producer.py +26 -30
- mindsdb/utilities/render/sqlalchemy_render.py +22 -20
- mindsdb/utilities/starters.py +0 -10
- mindsdb/utilities/utils.py +2 -2
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/METADATA +293 -276
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/RECORD +144 -158
- mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
- mindsdb/api/postgres/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/executor/__init__.py +0 -1
- mindsdb/api/postgres/postgres_proxy/executor/executor.py +0 -189
- mindsdb/api/postgres/postgres_proxy/postgres_packets/__init__.py +0 -0
- mindsdb/api/postgres/postgres_proxy/postgres_packets/errors.py +0 -322
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_fields.py +0 -34
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message.py +0 -31
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +0 -1265
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_identifiers.py +0 -31
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +0 -253
- mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +0 -477
- mindsdb/api/postgres/postgres_proxy/utilities/__init__.py +0 -10
- mindsdb/api/postgres/start.py +0 -11
- mindsdb/integrations/handlers/mssql_handler/tests/__init__.py +0 -0
- mindsdb/integrations/handlers/mssql_handler/tests/test_mssql_handler.py +0 -169
- mindsdb/integrations/handlers/oracle_handler/tests/__init__.py +0 -0
- mindsdb/integrations/handlers/oracle_handler/tests/test_oracle_handler.py +0 -32
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/WHEEL +0 -0
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.9.2.0a1.dist-info → mindsdb-25.10.0rc1.dist-info}/top_level.txt +0 -0
mindsdb/migrations/migrate.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
-
from alembic.command import upgrade, autogen
|
|
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 =
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
56
|
-
|
|
57
|
-
|
|
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 /
|
|
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(
|
|
65
|
-
config.set_main_option(
|
|
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=
|
|
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
|
mindsdb/utilities/config.py
CHANGED
|
@@ -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
|
-
|
|
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.
|
mindsdb/utilities/exception.py
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
from textwrap import indent
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
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
|
@@ -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.
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
6
|
-
|
|
4
|
+
import numpy as np
|
|
5
|
+
import orjson
|
|
7
6
|
from flask.json.provider import DefaultJSONProvider
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
class CustomJSONEncoder
|
|
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
|
|
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)
|
mindsdb/utilities/langfuse.py
CHANGED
|
@@ -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__(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
153
|
-
logger.
|
|
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(
|
|
205
|
-
|
|
206
|
-
|
|
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[
|
|
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
|
-
|
|
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
|
|
279
|
-
tool_name = step.split(
|
|
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
|
|
285
|
-
except Exception
|
|
286
|
-
logger.
|
|
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
|