MindsDB 25.9.1.2__py3-none-any.whl → 25.9.3rc1__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 +39 -20
- mindsdb/api/a2a/agent.py +7 -9
- mindsdb/api/a2a/common/server/server.py +3 -3
- mindsdb/api/a2a/common/server/task_manager.py +4 -4
- mindsdb/api/a2a/task_manager.py +15 -17
- mindsdb/api/common/middleware.py +9 -11
- mindsdb/api/executor/command_executor.py +2 -4
- mindsdb/api/executor/datahub/datanodes/datanode.py +2 -2
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +100 -48
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +8 -4
- mindsdb/api/executor/datahub/datanodes/system_tables.py +1 -1
- mindsdb/api/executor/exceptions.py +29 -10
- mindsdb/api/executor/planner/plan_join.py +17 -3
- 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 +32 -16
- mindsdb/api/http/gui.py +5 -11
- mindsdb/api/http/initialize.py +8 -10
- mindsdb/api/http/namespaces/agents.py +10 -12
- mindsdb/api/http/namespaces/analysis.py +13 -20
- mindsdb/api/http/namespaces/auth.py +1 -1
- mindsdb/api/http/namespaces/config.py +15 -11
- mindsdb/api/http/namespaces/databases.py +140 -201
- mindsdb/api/http/namespaces/file.py +15 -4
- mindsdb/api/http/namespaces/handlers.py +7 -2
- mindsdb/api/http/namespaces/knowledge_bases.py +8 -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 +14 -8
- 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/postgres/postgres_proxy/executor/executor.py +6 -13
- mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_packets.py +40 -28
- mindsdb/integrations/handlers/byom_handler/byom_handler.py +168 -185
- mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +11 -5
- mindsdb/integrations/handlers/file_handler/file_handler.py +7 -0
- mindsdb/integrations/handlers/lightwood_handler/functions.py +45 -79
- mindsdb/integrations/handlers/openai_handler/openai_handler.py +1 -1
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +20 -2
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +18 -3
- 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/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/rag/config_loader.py +37 -26
- mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +59 -9
- 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 -1
- 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 +10 -10
- mindsdb/interfaces/database/integrations.py +19 -2
- 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 -5
- mindsdb/interfaces/jobs/scheduler.py +3 -8
- mindsdb/interfaces/knowledge_base/controller.py +54 -25
- mindsdb/interfaces/knowledge_base/preprocessing/json_chunker.py +40 -61
- mindsdb/interfaces/model/model_controller.py +170 -166
- mindsdb/interfaces/query_context/context_controller.py +14 -2
- mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +6 -4
- mindsdb/interfaces/skills/retrieval_tool.py +43 -50
- mindsdb/interfaces/skills/skill_tool.py +2 -2
- mindsdb/interfaces/skills/sql_agent.py +25 -19
- mindsdb/interfaces/storage/fs.py +114 -169
- mindsdb/interfaces/storage/json.py +19 -18
- mindsdb/interfaces/storage/model_fs.py +54 -92
- 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 -50
- mindsdb/migrations/migrate.py +16 -16
- mindsdb/utilities/api_status.py +58 -0
- mindsdb/utilities/config.py +49 -0
- mindsdb/utilities/exception.py +40 -1
- mindsdb/utilities/fs.py +0 -1
- mindsdb/utilities/hooks/profiling.py +17 -14
- 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 +8 -7
- mindsdb/utilities/utils.py +2 -2
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/METADATA +266 -261
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/RECORD +119 -119
- mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -14
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/WHEEL +0 -0
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.9.1.2.dist-info → mindsdb-25.9.3rc1.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,7 @@ from http import HTTPStatus
|
|
|
3
3
|
from flask import request
|
|
4
4
|
from flask_restx import Resource
|
|
5
5
|
from langchain_text_splitters import MarkdownHeaderTextSplitter
|
|
6
|
+
from mindsdb_sql_parser.ast import Identifier
|
|
6
7
|
|
|
7
8
|
from mindsdb.api.http.namespaces.configs.projects import ns_conf
|
|
8
9
|
from mindsdb.api.executor.controllers.session_controller import SessionController
|
|
@@ -24,12 +25,10 @@ from mindsdb.metrics.metrics import api_endpoint_metrics
|
|
|
24
25
|
from mindsdb.interfaces.database.projects import ProjectController
|
|
25
26
|
from mindsdb.interfaces.knowledge_base.controller import KnowledgeBaseTable
|
|
26
27
|
from mindsdb.utilities import log
|
|
27
|
-
from mindsdb.utilities.exception import EntityNotExistsError
|
|
28
|
+
from mindsdb.utilities.exception import EntityNotExistsError, EntityExistsError
|
|
28
29
|
from mindsdb.integrations.utilities.rag.settings import DEFAULT_LLM_MODEL, DEFAULT_RAG_PROMPT_TEMPLATE
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
from mindsdb_sql_parser.ast import Identifier
|
|
32
|
-
|
|
33
32
|
logger = log.getLogger(__name__)
|
|
34
33
|
|
|
35
34
|
|
|
@@ -133,6 +132,8 @@ class KnowledgeBasesResource(Resource):
|
|
|
133
132
|
)
|
|
134
133
|
except ValueError as e:
|
|
135
134
|
return http_error(HTTPStatus.BAD_REQUEST, "Invalid preprocessing configuration", str(e))
|
|
135
|
+
except EntityExistsError as e:
|
|
136
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Knowledge base already exists", str(e))
|
|
136
137
|
|
|
137
138
|
return new_kb.as_dict(session.show_secrets), HTTPStatus.CREATED
|
|
138
139
|
|
|
@@ -241,17 +242,17 @@ class KnowledgeBaseResource(Resource):
|
|
|
241
242
|
table.insert_query_result(kb_data["query"], project_name)
|
|
242
243
|
|
|
243
244
|
except ExecutorException as e:
|
|
244
|
-
logger.
|
|
245
|
+
logger.exception("Error during preprocessing and insertion:")
|
|
245
246
|
return http_error(
|
|
246
247
|
HTTPStatus.BAD_REQUEST,
|
|
247
248
|
"Invalid SELECT query",
|
|
248
|
-
f'Executing "query" failed. Needs to be a valid SELECT statement that returns data: {
|
|
249
|
+
f'Executing "query" failed. Needs to be a valid SELECT statement that returns data: {e}',
|
|
249
250
|
)
|
|
250
251
|
|
|
251
252
|
except Exception as e:
|
|
252
|
-
logger.
|
|
253
|
+
logger.exception("Error during preprocessing and insertion:")
|
|
253
254
|
return http_error(
|
|
254
|
-
HTTPStatus.BAD_REQUEST, "Preprocessing Error", f"Error during preprocessing and insertion: {
|
|
255
|
+
HTTPStatus.BAD_REQUEST, "Preprocessing Error", f"Error during preprocessing and insertion: {e}"
|
|
255
256
|
)
|
|
256
257
|
|
|
257
258
|
return "", HTTPStatus.OK
|
|
@@ -4,7 +4,6 @@ import json
|
|
|
4
4
|
|
|
5
5
|
from flask import request
|
|
6
6
|
from flask_restx import Resource
|
|
7
|
-
from sqlalchemy.exc import NoResultFound
|
|
8
7
|
import pandas as pd
|
|
9
8
|
|
|
10
9
|
from mindsdb.api.http.namespaces.configs.projects import ns_conf
|
|
@@ -15,71 +14,65 @@ from mindsdb.interfaces.model.functions import PredictorRecordNotFound
|
|
|
15
14
|
from mindsdb.interfaces.storage.db import Predictor
|
|
16
15
|
from mindsdb_sql_parser import parse_sql
|
|
17
16
|
from mindsdb_sql_parser.ast.mindsdb import CreatePredictor
|
|
17
|
+
from mindsdb.utilities.exception import EntityNotExistsError
|
|
18
|
+
from mindsdb.utilities import log
|
|
18
19
|
|
|
20
|
+
logger = log.getLogger(__name__)
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
@ns_conf.route("/<project_name>/models")
|
|
21
24
|
class ModelsList(Resource):
|
|
22
|
-
@ns_conf.doc(
|
|
23
|
-
@api_endpoint_metrics(
|
|
25
|
+
@ns_conf.doc("list_models")
|
|
26
|
+
@api_endpoint_metrics("GET", "/models")
|
|
24
27
|
def get(self, project_name):
|
|
25
|
-
|
|
28
|
+
"""List all models"""
|
|
26
29
|
session = SessionController()
|
|
27
30
|
|
|
28
31
|
try:
|
|
29
32
|
session.database_controller.get_project(project_name)
|
|
30
|
-
except
|
|
31
|
-
return http_error(
|
|
32
|
-
HTTPStatus.NOT_FOUND,
|
|
33
|
-
'Project not found',
|
|
34
|
-
f'Project name {project_name} does not exist')
|
|
33
|
+
except EntityNotExistsError:
|
|
34
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
35
35
|
|
|
36
36
|
return session.model_controller.get_models(with_versions=True, project_name=project_name)
|
|
37
37
|
|
|
38
|
-
@ns_conf.doc(
|
|
39
|
-
@api_endpoint_metrics(
|
|
38
|
+
@ns_conf.doc("train_model")
|
|
39
|
+
@api_endpoint_metrics("POST", "/models")
|
|
40
40
|
def post(self, project_name):
|
|
41
|
-
|
|
41
|
+
"""Creates a new model and trains it"""
|
|
42
42
|
session = SessionController()
|
|
43
43
|
|
|
44
|
-
if
|
|
45
|
-
return http_error(
|
|
46
|
-
|
|
47
|
-
'Query required',
|
|
48
|
-
'Missing "query" SQL statement')
|
|
49
|
-
query = request.json['query']
|
|
44
|
+
if "query" not in request.json:
|
|
45
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Query required", 'Missing "query" SQL statement')
|
|
46
|
+
query = request.json["query"]
|
|
50
47
|
|
|
51
48
|
project_datanode = session.datahub.get(project_name)
|
|
52
49
|
if project_datanode is None:
|
|
53
|
-
return http_error(
|
|
54
|
-
HTTPStatus.NOT_FOUND,
|
|
55
|
-
'Project not found',
|
|
56
|
-
f'Project name {project_name} does not exist')
|
|
50
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
57
51
|
|
|
58
52
|
try:
|
|
59
53
|
create_statement = parse_sql(query)
|
|
60
54
|
except Exception:
|
|
61
55
|
return http_error(
|
|
62
|
-
HTTPStatus.BAD_REQUEST,
|
|
63
|
-
|
|
64
|
-
f'SQL CREATE statement is invalid: {query}')
|
|
56
|
+
HTTPStatus.BAD_REQUEST, "Invalid query string", f"SQL CREATE statement is invalid: {query}"
|
|
57
|
+
)
|
|
65
58
|
|
|
66
59
|
if type(create_statement) is not CreatePredictor:
|
|
67
60
|
return http_error(
|
|
68
61
|
HTTPStatus.BAD_REQUEST,
|
|
69
|
-
|
|
70
|
-
f
|
|
62
|
+
"Invalid CREATE SQL statement",
|
|
63
|
+
f"SQL statement is not a CREATE model statement: {query}",
|
|
64
|
+
)
|
|
71
65
|
|
|
72
66
|
model_name = create_statement.name.parts[1].lower()
|
|
73
67
|
try:
|
|
74
68
|
session.model_controller.get_model(model_name, project_name=project_name)
|
|
75
69
|
return http_error(
|
|
76
|
-
HTTPStatus.CONFLICT,
|
|
77
|
-
|
|
78
|
-
f'Model with name {model_name} already exists')
|
|
70
|
+
HTTPStatus.CONFLICT, "Model already exists", f"Model with name {model_name} already exists"
|
|
71
|
+
)
|
|
79
72
|
except PredictorRecordNotFound:
|
|
80
73
|
pass
|
|
81
74
|
|
|
82
|
-
ml_integration =
|
|
75
|
+
ml_integration = "lightwood"
|
|
83
76
|
if create_statement.using is not None:
|
|
84
77
|
# Convert using to lowercase
|
|
85
78
|
create_statement.using = {k.lower(): v for k, v in create_statement.using.items()}
|
|
@@ -89,60 +82,55 @@ class ModelsList(Resource):
|
|
|
89
82
|
ml_handler = session.integration_controller.get_ml_handler(ml_integration)
|
|
90
83
|
except Exception:
|
|
91
84
|
return http_error(
|
|
92
|
-
HTTPStatus.NOT_FOUND,
|
|
93
|
-
|
|
94
|
-
f'Cannot find ML handler with name {ml_integration}')
|
|
85
|
+
HTTPStatus.NOT_FOUND, "ML handler not found", f"Cannot find ML handler with name {ml_integration}"
|
|
86
|
+
)
|
|
95
87
|
|
|
96
88
|
try:
|
|
97
89
|
model_df = session.model_controller.create_model(create_statement, ml_handler)
|
|
98
90
|
# Consistent format with GET /projects/<project_name>/models/<model_name>
|
|
99
91
|
return {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
92
|
+
"name": model_df.at[0, "NAME"],
|
|
93
|
+
"accuracy": None,
|
|
94
|
+
"active": model_df.at[0, "ACTIVE"],
|
|
95
|
+
"version": model_df.at[0, "VERSION"],
|
|
96
|
+
"status": model_df.at[0, "STATUS"],
|
|
97
|
+
"predict": model_df.at[0, "PREDICT"],
|
|
98
|
+
"mindsdb_version": model_df.at[0, "MINDSDB_VERSION"],
|
|
99
|
+
"error": model_df.at[0, "ERROR"],
|
|
100
|
+
"fetch_data_query": model_df.at[0, "SELECT_DATA_QUERY"],
|
|
101
|
+
"problem_definition": model_df.at[0, "TRAINING_OPTIONS"],
|
|
110
102
|
}, HTTPStatus.CREATED
|
|
111
103
|
except Exception as e:
|
|
104
|
+
logger.exception("Something went wrong while creating and training model")
|
|
112
105
|
return http_error(
|
|
113
106
|
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
114
|
-
|
|
115
|
-
f
|
|
107
|
+
"Unable to train model",
|
|
108
|
+
f"Something went wrong while creating and training model {model_name}: {e}",
|
|
109
|
+
)
|
|
116
110
|
|
|
117
111
|
|
|
118
|
-
@ns_conf.route(
|
|
119
|
-
@ns_conf.param(
|
|
120
|
-
@ns_conf.param(
|
|
112
|
+
@ns_conf.route("/<project_name>/models/<model_name>")
|
|
113
|
+
@ns_conf.param("project_name", "Name of the project")
|
|
114
|
+
@ns_conf.param("model_name", "Name of the model")
|
|
121
115
|
class ModelResource(Resource):
|
|
122
|
-
@ns_conf.doc(
|
|
123
|
-
@api_endpoint_metrics(
|
|
116
|
+
@ns_conf.doc("get_model")
|
|
117
|
+
@api_endpoint_metrics("GET", "/models/model")
|
|
124
118
|
def get(self, project_name, model_name):
|
|
125
|
-
|
|
119
|
+
"""Get a model by name and version"""
|
|
126
120
|
session = SessionController()
|
|
127
121
|
|
|
128
122
|
project_datanode = session.datahub.get(project_name)
|
|
129
123
|
if project_datanode is None:
|
|
130
|
-
return http_error(
|
|
131
|
-
HTTPStatus.NOT_FOUND,
|
|
132
|
-
'Project not found',
|
|
133
|
-
f'Project name {project_name} does not exist')
|
|
124
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
134
125
|
|
|
135
126
|
name_no_version, version = Predictor.get_name_and_version(model_name)
|
|
136
127
|
try:
|
|
137
128
|
return session.model_controller.get_model(name_no_version, version=version, project_name=project_name)
|
|
138
129
|
except PredictorRecordNotFound:
|
|
139
|
-
return http_error(
|
|
140
|
-
HTTPStatus.NOT_FOUND,
|
|
141
|
-
'Model not found',
|
|
142
|
-
f'Model with name {model_name} not found')
|
|
130
|
+
return http_error(HTTPStatus.NOT_FOUND, "Model not found", f"Model with name {model_name} not found")
|
|
143
131
|
|
|
144
|
-
@ns_conf.doc(
|
|
145
|
-
@api_endpoint_metrics(
|
|
132
|
+
@ns_conf.doc("update_model")
|
|
133
|
+
@api_endpoint_metrics("PUT", "/models/model")
|
|
146
134
|
def put(self, project_name, model_name):
|
|
147
135
|
"""Update model"""
|
|
148
136
|
|
|
@@ -150,18 +138,14 @@ class ModelResource(Resource):
|
|
|
150
138
|
|
|
151
139
|
project_datanode = session.datahub.get(project_name)
|
|
152
140
|
if project_datanode is None:
|
|
153
|
-
return http_error(
|
|
154
|
-
HTTPStatus.NOT_FOUND,
|
|
155
|
-
'Project not found',
|
|
156
|
-
f'Project name {project_name} does not exist')
|
|
141
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
157
142
|
|
|
158
|
-
if
|
|
143
|
+
if "problem_definition" not in request.json:
|
|
159
144
|
return http_error(
|
|
160
|
-
HTTPStatus.BAD_REQUEST,
|
|
161
|
-
|
|
162
|
-
'Missing "problem_definition" field')
|
|
145
|
+
HTTPStatus.BAD_REQUEST, "problem_definition required", 'Missing "problem_definition" field'
|
|
146
|
+
)
|
|
163
147
|
|
|
164
|
-
problem_definition = request.json[
|
|
148
|
+
problem_definition = request.json["problem_definition"]
|
|
165
149
|
|
|
166
150
|
model_name, version = Predictor.get_name_and_version(model_name)
|
|
167
151
|
|
|
@@ -174,48 +158,44 @@ class ModelResource(Resource):
|
|
|
174
158
|
)
|
|
175
159
|
return session.model_controller.get_model(model_name, version=version, project_name=project_name)
|
|
176
160
|
|
|
177
|
-
@ns_conf.doc(
|
|
178
|
-
@api_endpoint_metrics(
|
|
161
|
+
@ns_conf.doc("delete_model")
|
|
162
|
+
@api_endpoint_metrics("DELETE", "/models/model")
|
|
179
163
|
def delete(self, project_name, model_name):
|
|
180
|
-
|
|
164
|
+
"""Deletes a model by name"""
|
|
181
165
|
|
|
182
166
|
session = SessionController()
|
|
183
167
|
|
|
184
168
|
project_datanode = session.datahub.get(project_name)
|
|
185
169
|
if project_datanode is None:
|
|
186
|
-
return http_error(
|
|
187
|
-
HTTPStatus.NOT_FOUND,
|
|
188
|
-
'Project not found',
|
|
189
|
-
f'Project name {project_name} does not exist')
|
|
170
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
190
171
|
|
|
191
172
|
name_no_version, version = Predictor.get_name_and_version(model_name)
|
|
192
173
|
try:
|
|
193
174
|
session.model_controller.get_model(name_no_version, version=version, project_name=project_name)
|
|
194
175
|
except PredictorRecordNotFound:
|
|
195
|
-
return http_error(
|
|
196
|
-
HTTPStatus.NOT_FOUND,
|
|
197
|
-
'Model not found',
|
|
198
|
-
f'Model with name {model_name} not found')
|
|
176
|
+
return http_error(HTTPStatus.NOT_FOUND, "Model not found", f"Model with name {model_name} not found")
|
|
199
177
|
|
|
200
178
|
try:
|
|
201
179
|
session.model_controller.delete_model(name_no_version, project_name, version=version)
|
|
202
180
|
except Exception as e:
|
|
181
|
+
logger.exception(f"Something went wrong while deleting model '{model_name}'")
|
|
203
182
|
return http_error(
|
|
204
183
|
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
205
|
-
|
|
206
|
-
f
|
|
184
|
+
"Error deleting model",
|
|
185
|
+
f"Something went wrong while deleting {model_name}: {e}",
|
|
186
|
+
)
|
|
207
187
|
|
|
208
|
-
return
|
|
188
|
+
return "", HTTPStatus.NO_CONTENT
|
|
209
189
|
|
|
210
190
|
|
|
211
|
-
@ns_conf.route(
|
|
212
|
-
@ns_conf.param(
|
|
213
|
-
@ns_conf.param(
|
|
191
|
+
@ns_conf.route("/<project_name>/models/<model_name>/predict")
|
|
192
|
+
@ns_conf.param("project_name", "Name of the project")
|
|
193
|
+
@ns_conf.param("model_name", "Name of the model")
|
|
214
194
|
class ModelPredict(Resource):
|
|
215
|
-
@ns_conf.doc(
|
|
216
|
-
@api_endpoint_metrics(
|
|
195
|
+
@ns_conf.doc("post_model_predict")
|
|
196
|
+
@api_endpoint_metrics("POST", "/models/model/predict")
|
|
217
197
|
def post(self, project_name, model_name):
|
|
218
|
-
|
|
198
|
+
"""Call prediction"""
|
|
219
199
|
|
|
220
200
|
name_no_version, version = Predictor.get_name_and_version(model_name)
|
|
221
201
|
|
|
@@ -223,23 +203,19 @@ class ModelPredict(Resource):
|
|
|
223
203
|
project_datanode = session.datahub.get(project_name)
|
|
224
204
|
if project_datanode is None:
|
|
225
205
|
return http_error(
|
|
226
|
-
HTTPStatus.NOT_FOUND,
|
|
227
|
-
|
|
228
|
-
f'Project with name {project_name} does not exist')
|
|
206
|
+
HTTPStatus.NOT_FOUND, "Project not found", f"Project with name {project_name} does not exist"
|
|
207
|
+
)
|
|
229
208
|
|
|
230
209
|
try:
|
|
231
210
|
session.model_controller.get_model(name_no_version, version=version, project_name=project_name)
|
|
232
211
|
except PredictorRecordNotFound:
|
|
233
|
-
return http_error(
|
|
234
|
-
HTTPStatus.NOT_FOUND,
|
|
235
|
-
'Model not found',
|
|
236
|
-
f'Model with name {model_name} not found')
|
|
212
|
+
return http_error(HTTPStatus.NOT_FOUND, "Model not found", f"Model with name {model_name} not found")
|
|
237
213
|
|
|
238
|
-
data = request.json[
|
|
214
|
+
data = request.json["data"]
|
|
239
215
|
if isinstance(data, str):
|
|
240
216
|
# Support object or serialized object.
|
|
241
217
|
data = json.loads(data)
|
|
242
|
-
params = request.json.get(
|
|
218
|
+
params = request.json.get("params")
|
|
243
219
|
|
|
244
220
|
predictions = project_datanode.predict(
|
|
245
221
|
model_name=name_no_version,
|
|
@@ -248,50 +224,42 @@ class ModelPredict(Resource):
|
|
|
248
224
|
params=params,
|
|
249
225
|
)
|
|
250
226
|
|
|
251
|
-
return predictions.to_dict(
|
|
227
|
+
return predictions.to_dict("records")
|
|
252
228
|
|
|
253
229
|
|
|
254
|
-
@ns_conf.route(
|
|
255
|
-
@ns_conf.param(
|
|
256
|
-
@ns_conf.param(
|
|
230
|
+
@ns_conf.route("/<project_name>/models/<model_name>/describe")
|
|
231
|
+
@ns_conf.param("project_name", "Name of the project")
|
|
232
|
+
@ns_conf.param("model_name", "Name of the model")
|
|
257
233
|
class ModelDescribe(Resource):
|
|
258
|
-
@ns_conf.doc(
|
|
259
|
-
@api_endpoint_metrics(
|
|
234
|
+
@ns_conf.doc("describe_model")
|
|
235
|
+
@api_endpoint_metrics("GET", "/models/model/describe")
|
|
260
236
|
def get(self, project_name, model_name):
|
|
261
|
-
|
|
237
|
+
"""Describes a model"""
|
|
262
238
|
session = SessionController()
|
|
263
239
|
|
|
264
240
|
project_datanode = session.datahub.get(project_name)
|
|
265
241
|
if project_datanode is None:
|
|
266
|
-
return http_error(
|
|
267
|
-
HTTPStatus.NOT_FOUND,
|
|
268
|
-
'Project not found',
|
|
269
|
-
f'Project name {project_name} does not exist')
|
|
242
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not found", f"Project name {project_name} does not exist")
|
|
270
243
|
|
|
271
244
|
name_no_version, version = Predictor.get_name_and_version(model_name)
|
|
272
245
|
|
|
273
246
|
try:
|
|
274
247
|
session.model_controller.get_model(name_no_version, version=version, project_name=project_name)
|
|
275
248
|
except PredictorRecordNotFound:
|
|
276
|
-
return http_error(
|
|
277
|
-
HTTPStatus.NOT_FOUND,
|
|
278
|
-
'Model not found',
|
|
279
|
-
f'Model with name {model_name} not found')
|
|
249
|
+
return http_error(HTTPStatus.NOT_FOUND, "Model not found", f"Model with name {model_name} not found")
|
|
280
250
|
|
|
281
251
|
attribute = None
|
|
282
|
-
if
|
|
283
|
-
attribute = request.json[
|
|
252
|
+
if "attribute" in request.json:
|
|
253
|
+
attribute = request.json["attribute"]
|
|
284
254
|
|
|
285
255
|
try:
|
|
286
256
|
description_df = session.model_controller.describe_model(
|
|
287
|
-
session,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
attribute,
|
|
291
|
-
version=version)
|
|
292
|
-
return description_df.to_dict('records')
|
|
257
|
+
session, project_name, name_no_version, attribute, version=version
|
|
258
|
+
)
|
|
259
|
+
return description_df.to_dict("records")
|
|
293
260
|
except Exception:
|
|
294
261
|
return http_error(
|
|
295
262
|
HTTPStatus.BAD_REQUEST,
|
|
296
|
-
|
|
297
|
-
f
|
|
263
|
+
"ML handler unsupported",
|
|
264
|
+
f"ML handler for {model_name} does not support model description",
|
|
265
|
+
)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from http import HTTPStatus
|
|
2
2
|
|
|
3
3
|
from flask_restx import Resource
|
|
4
|
-
from sqlalchemy.exc import NoResultFound
|
|
5
4
|
|
|
6
5
|
from mindsdb.metrics.metrics import api_endpoint_metrics
|
|
7
6
|
from mindsdb.api.http.utils import http_error
|
|
@@ -10,37 +9,29 @@ from mindsdb.api.executor.controllers.session_controller import SessionControlle
|
|
|
10
9
|
from mindsdb.utilities.exception import EntityNotExistsError
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
@ns_conf.route(
|
|
12
|
+
@ns_conf.route("/")
|
|
14
13
|
class ProjectsList(Resource):
|
|
15
|
-
@ns_conf.doc(
|
|
16
|
-
@api_endpoint_metrics(
|
|
14
|
+
@ns_conf.doc("list_projects")
|
|
15
|
+
@api_endpoint_metrics("GET", "/projects")
|
|
17
16
|
def get(self):
|
|
18
|
-
|
|
17
|
+
"""List all projects"""
|
|
19
18
|
session = SessionController()
|
|
20
19
|
|
|
21
|
-
projects = [
|
|
22
|
-
{'name': i}
|
|
23
|
-
for i in session.datahub.get_projects_names()
|
|
24
|
-
]
|
|
20
|
+
projects = [{"name": i} for i in session.datahub.get_projects_names()]
|
|
25
21
|
return projects
|
|
26
22
|
|
|
27
23
|
|
|
28
|
-
@ns_conf.route(
|
|
24
|
+
@ns_conf.route("/<project_name>")
|
|
29
25
|
class ProjectsGet(Resource):
|
|
30
|
-
@ns_conf.doc(
|
|
31
|
-
@api_endpoint_metrics(
|
|
26
|
+
@ns_conf.doc("get_project")
|
|
27
|
+
@api_endpoint_metrics("GET", "/projects/project")
|
|
32
28
|
def get(self, project_name):
|
|
33
|
-
|
|
29
|
+
"""Gets a project by name"""
|
|
34
30
|
session = SessionController()
|
|
35
31
|
|
|
36
32
|
try:
|
|
37
33
|
project = session.database_controller.get_project(project_name)
|
|
38
|
-
except
|
|
39
|
-
return http_error(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
'name': project.name
|
|
46
|
-
}
|
|
34
|
+
except EntityNotExistsError:
|
|
35
|
+
return http_error(HTTPStatus.NOT_FOUND, "Project not exists", f"Project name {project_name} does not exist")
|
|
36
|
+
|
|
37
|
+
return {"name": project.name}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from http import HTTPStatus
|
|
2
|
-
import traceback
|
|
3
2
|
|
|
4
3
|
from flask import request
|
|
5
4
|
from flask_restx import Resource
|
|
@@ -18,6 +17,7 @@ from mindsdb.metrics.metrics import api_endpoint_metrics
|
|
|
18
17
|
from mindsdb.utilities import log
|
|
19
18
|
from mindsdb.utilities.config import Config
|
|
20
19
|
from mindsdb.utilities.context import context as ctx
|
|
20
|
+
from mindsdb.utilities.exception import QueryError
|
|
21
21
|
|
|
22
22
|
logger = log.getLogger(__name__)
|
|
23
23
|
|
|
@@ -29,18 +29,14 @@ class Query(Resource):
|
|
|
29
29
|
super().__init__(*args, **kwargs)
|
|
30
30
|
|
|
31
31
|
@ns_conf.doc("query")
|
|
32
|
-
@api_endpoint_metrics(
|
|
32
|
+
@api_endpoint_metrics("POST", "/sql/query")
|
|
33
33
|
def post(self):
|
|
34
34
|
query = request.json["query"]
|
|
35
35
|
context = request.json.get("context", {})
|
|
36
36
|
|
|
37
37
|
if isinstance(query, str) is False or isinstance(context, dict) is False:
|
|
38
|
-
return http_error(
|
|
39
|
-
|
|
40
|
-
'Wrong arguments',
|
|
41
|
-
'Please provide "query" with the request.'
|
|
42
|
-
)
|
|
43
|
-
logger.debug(f'Incoming query: {query}')
|
|
38
|
+
return http_error(HTTPStatus.BAD_REQUEST, "Wrong arguments", 'Please provide "query" with the request.')
|
|
39
|
+
logger.debug(f"Incoming query: {query}")
|
|
44
40
|
|
|
45
41
|
if context.get("profiling") is True:
|
|
46
42
|
profiler.enable()
|
|
@@ -50,9 +46,7 @@ class Query(Resource):
|
|
|
50
46
|
error_text = None
|
|
51
47
|
error_traceback = None
|
|
52
48
|
|
|
53
|
-
profiler.set_meta(
|
|
54
|
-
query=query, api="http", environment=Config().get("environment")
|
|
55
|
-
)
|
|
49
|
+
profiler.set_meta(query=query, api="http", environment=Config().get("environment"))
|
|
56
50
|
with profiler.Context("http_query_processing"):
|
|
57
51
|
mysql_proxy = FakeMysqlProxy()
|
|
58
52
|
mysql_proxy.set_context(context)
|
|
@@ -67,8 +61,18 @@ class Query(Resource):
|
|
|
67
61
|
"error_code": 0,
|
|
68
62
|
"error_message": str(e),
|
|
69
63
|
}
|
|
70
|
-
logger.
|
|
71
|
-
|
|
64
|
+
logger.warning(f"Error query processing: {e}")
|
|
65
|
+
except QueryError as e:
|
|
66
|
+
error_type = "expected" if e.is_acceptable else "unexpected"
|
|
67
|
+
query_response = {
|
|
68
|
+
"type": SQL_RESPONSE_TYPE.ERROR,
|
|
69
|
+
"error_code": 0,
|
|
70
|
+
"error_message": str(e),
|
|
71
|
+
}
|
|
72
|
+
if e.is_acceptable:
|
|
73
|
+
logger.warning(f"Query failed due to expected reason: {e}")
|
|
74
|
+
else:
|
|
75
|
+
logger.exception("Error query processing:")
|
|
72
76
|
except UnknownError as e:
|
|
73
77
|
# unclassified
|
|
74
78
|
error_type = "unexpected"
|
|
@@ -77,7 +81,7 @@ class Query(Resource):
|
|
|
77
81
|
"error_code": 0,
|
|
78
82
|
"error_message": str(e),
|
|
79
83
|
}
|
|
80
|
-
logger.
|
|
84
|
+
logger.exception("Error query processing:")
|
|
81
85
|
|
|
82
86
|
except Exception as e:
|
|
83
87
|
error_type = "unexpected"
|
|
@@ -86,7 +90,7 @@ class Query(Resource):
|
|
|
86
90
|
"error_code": 0,
|
|
87
91
|
"error_message": str(e),
|
|
88
92
|
}
|
|
89
|
-
logger.
|
|
93
|
+
logger.exception("Error query processing:")
|
|
90
94
|
|
|
91
95
|
if query_response.get("type") == SQL_RESPONSE_TYPE.ERROR:
|
|
92
96
|
error_type = "expected"
|
|
@@ -115,7 +119,7 @@ class Query(Resource):
|
|
|
115
119
|
@ns_conf.param("list_databases", "lists databases of mindsdb")
|
|
116
120
|
class ListDatabases(Resource):
|
|
117
121
|
@ns_conf.doc("list_databases")
|
|
118
|
-
@api_endpoint_metrics(
|
|
122
|
+
@api_endpoint_metrics("GET", "/sql/list_databases")
|
|
119
123
|
def get(self):
|
|
120
124
|
listing_query = "SHOW DATABASES"
|
|
121
125
|
mysql_proxy = FakeMysqlProxy()
|
|
@@ -133,17 +137,21 @@ class ListDatabases(Resource):
|
|
|
133
137
|
listing_query_response = {"type": "ok"}
|
|
134
138
|
elif result.type == SQL_RESPONSE_TYPE.TABLE:
|
|
135
139
|
listing_query_response = {
|
|
136
|
-
"data": [
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
"data": [
|
|
141
|
+
{
|
|
142
|
+
"name": db_row[0],
|
|
143
|
+
"tables": [
|
|
144
|
+
table_row[0]
|
|
145
|
+
for table_row in mysql_proxy.process_query(
|
|
146
|
+
"SHOW TABLES FROM `{}`".format(db_row[0])
|
|
147
|
+
).result_set.to_lists()
|
|
148
|
+
],
|
|
149
|
+
}
|
|
150
|
+
for db_row in result.result_set.to_lists()
|
|
151
|
+
]
|
|
145
152
|
}
|
|
146
153
|
except Exception as e:
|
|
154
|
+
logger.exception("Error while retrieving list of databases")
|
|
147
155
|
listing_query_response = {
|
|
148
156
|
"type": "error",
|
|
149
157
|
"error_code": 0,
|