MindsDB 25.7.2.0__py3-none-any.whl → 25.7.4.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.
- mindsdb/__about__.py +1 -1
- mindsdb/__main__.py +1 -1
- mindsdb/api/a2a/common/server/server.py +16 -6
- mindsdb/api/executor/command_executor.py +213 -137
- mindsdb/api/executor/datahub/datanodes/integration_datanode.py +5 -1
- mindsdb/api/executor/datahub/datanodes/project_datanode.py +14 -3
- mindsdb/api/executor/planner/plan_join.py +3 -0
- mindsdb/api/executor/planner/plan_join_ts.py +117 -100
- mindsdb/api/executor/planner/query_planner.py +1 -0
- mindsdb/api/executor/sql_query/steps/apply_predictor_step.py +54 -85
- mindsdb/api/http/initialize.py +16 -43
- mindsdb/api/http/namespaces/agents.py +24 -21
- mindsdb/api/http/namespaces/chatbots.py +83 -120
- mindsdb/api/http/namespaces/file.py +1 -1
- mindsdb/api/http/namespaces/jobs.py +38 -60
- mindsdb/api/http/namespaces/tree.py +69 -61
- mindsdb/api/mcp/start.py +2 -0
- mindsdb/api/mysql/mysql_proxy/utilities/dump.py +3 -2
- mindsdb/integrations/handlers/autogluon_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/autosklearn_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/bigquery_handler/bigquery_handler.py +25 -5
- mindsdb/integrations/handlers/chromadb_handler/chromadb_handler.py +3 -3
- mindsdb/integrations/handlers/flaml_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/google_calendar_handler/google_calendar_tables.py +82 -73
- mindsdb/integrations/handlers/hubspot_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/langchain_handler/langchain_handler.py +83 -76
- mindsdb/integrations/handlers/lightwood_handler/requirements.txt +4 -4
- mindsdb/integrations/handlers/litellm_handler/litellm_handler.py +16 -3
- mindsdb/integrations/handlers/litellm_handler/settings.py +2 -1
- mindsdb/integrations/handlers/llama_index_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/pgvector_handler/pgvector_handler.py +106 -90
- mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +41 -39
- mindsdb/integrations/handlers/s3_handler/s3_handler.py +72 -70
- mindsdb/integrations/handlers/salesforce_handler/constants.py +208 -0
- mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +142 -81
- mindsdb/integrations/handlers/salesforce_handler/salesforce_tables.py +12 -4
- mindsdb/integrations/handlers/slack_handler/slack_tables.py +141 -161
- mindsdb/integrations/handlers/tpot_handler/requirements.txt +1 -1
- mindsdb/integrations/handlers/web_handler/urlcrawl_helpers.py +32 -17
- mindsdb/integrations/handlers/web_handler/web_handler.py +19 -22
- mindsdb/integrations/handlers/youtube_handler/youtube_tables.py +183 -55
- mindsdb/integrations/libs/vectordatabase_handler.py +10 -1
- mindsdb/integrations/utilities/handler_utils.py +32 -12
- mindsdb/interfaces/agents/agents_controller.py +169 -110
- mindsdb/interfaces/agents/langchain_agent.py +10 -3
- mindsdb/interfaces/data_catalog/data_catalog_loader.py +22 -8
- mindsdb/interfaces/database/database.py +38 -13
- mindsdb/interfaces/database/integrations.py +20 -5
- mindsdb/interfaces/database/projects.py +63 -16
- mindsdb/interfaces/database/views.py +86 -60
- mindsdb/interfaces/jobs/jobs_controller.py +103 -110
- mindsdb/interfaces/knowledge_base/controller.py +33 -5
- mindsdb/interfaces/knowledge_base/evaluate.py +53 -9
- mindsdb/interfaces/knowledge_base/executor.py +24 -0
- mindsdb/interfaces/knowledge_base/llm_client.py +3 -3
- mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +21 -13
- mindsdb/interfaces/query_context/context_controller.py +100 -133
- mindsdb/interfaces/skills/skills_controller.py +18 -6
- mindsdb/interfaces/storage/db.py +40 -6
- mindsdb/interfaces/variables/variables_controller.py +8 -15
- mindsdb/utilities/config.py +3 -3
- mindsdb/utilities/functions.py +72 -60
- mindsdb/utilities/log.py +38 -6
- mindsdb/utilities/ps.py +7 -7
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/METADATA +262 -263
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/RECORD +69 -68
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/WHEEL +0 -0
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/licenses/LICENSE +0 -0
- {mindsdb-25.7.2.0.dist-info → mindsdb-25.7.4.0.dist-info}/top_level.txt +0 -0
|
@@ -23,13 +23,16 @@ class SkillsController:
|
|
|
23
23
|
project_controller = ProjectController()
|
|
24
24
|
self.project_controller = project_controller
|
|
25
25
|
|
|
26
|
-
def get_skill(
|
|
26
|
+
def get_skill(
|
|
27
|
+
self, skill_name: str, project_name: str = default_project, strict_case: bool = False
|
|
28
|
+
) -> Optional[db.Skills]:
|
|
27
29
|
"""
|
|
28
30
|
Gets a skill by name. Skills are expected to have unique names.
|
|
29
31
|
|
|
30
32
|
Parameters:
|
|
31
33
|
skill_name (str): The name of the skill
|
|
32
34
|
project_name (str): The name of the containing project
|
|
35
|
+
strict_case (bool): If True, the skill name is case-sensitive. Defaults to False.
|
|
33
36
|
|
|
34
37
|
Returns:
|
|
35
38
|
skill (Optional[db.Skills]): The database skill object
|
|
@@ -39,11 +42,16 @@ class SkillsController:
|
|
|
39
42
|
"""
|
|
40
43
|
|
|
41
44
|
project = self.project_controller.get(name=project_name)
|
|
42
|
-
|
|
43
|
-
func.lower(db.Skills.name) == func.lower(skill_name),
|
|
45
|
+
query = db.Skills.query.filter(
|
|
44
46
|
db.Skills.project_id == project.id,
|
|
45
47
|
db.Skills.deleted_at == null(),
|
|
46
|
-
)
|
|
48
|
+
)
|
|
49
|
+
if strict_case:
|
|
50
|
+
query = query.filter(db.Skills.name == skill_name)
|
|
51
|
+
else:
|
|
52
|
+
query = query.filter(func.lower(db.Skills.name) == func.lower(skill_name))
|
|
53
|
+
|
|
54
|
+
return query.first()
|
|
47
55
|
|
|
48
56
|
def get_skills(self, project_name: Optional[str]) -> List[dict]:
|
|
49
57
|
"""
|
|
@@ -92,6 +100,9 @@ class SkillsController:
|
|
|
92
100
|
project_name = default_project
|
|
93
101
|
project = self.project_controller.get(name=project_name)
|
|
94
102
|
|
|
103
|
+
if not name.islower():
|
|
104
|
+
raise ValueError(f"The name must be in lower case: {name}")
|
|
105
|
+
|
|
95
106
|
skill = self.get_skill(name, project_name)
|
|
96
107
|
|
|
97
108
|
if skill is not None:
|
|
@@ -158,19 +169,20 @@ class SkillsController:
|
|
|
158
169
|
|
|
159
170
|
return existing_skill
|
|
160
171
|
|
|
161
|
-
def delete_skill(self, skill_name: str, project_name: str = default_project):
|
|
172
|
+
def delete_skill(self, skill_name: str, project_name: str = default_project, strict_case: bool = False):
|
|
162
173
|
"""
|
|
163
174
|
Deletes a skill by name.
|
|
164
175
|
|
|
165
176
|
Parameters:
|
|
166
177
|
skill_name (str): The name of the skill to delete
|
|
167
178
|
project_name (str): The name of the containing project
|
|
179
|
+
strict_case (bool): If true, then skill_name is case sensitive
|
|
168
180
|
|
|
169
181
|
Raises:
|
|
170
182
|
ValueError: If `project_name` does not exist or skill doesn't exist
|
|
171
183
|
"""
|
|
172
184
|
|
|
173
|
-
skill = self.get_skill(skill_name, project_name)
|
|
185
|
+
skill = self.get_skill(skill_name, project_name, strict_case)
|
|
174
186
|
if skill is None:
|
|
175
187
|
raise ValueError(f"Skill with name doesn't exist: {skill_name}")
|
|
176
188
|
if isinstance(skill.params, dict) and skill.params.get("is_demo") is True:
|
mindsdb/interfaces/storage/db.py
CHANGED
|
@@ -448,19 +448,53 @@ class Agents(Base):
|
|
|
448
448
|
deleted_at = Column(DateTime)
|
|
449
449
|
|
|
450
450
|
def as_dict(self) -> Dict:
|
|
451
|
-
|
|
451
|
+
skills = []
|
|
452
|
+
skills_extra_parameters = {}
|
|
453
|
+
for rel in self.skills_relationships:
|
|
454
|
+
skill = rel.skill
|
|
455
|
+
# Skip auto-generated SQL skills
|
|
456
|
+
if skill.params.get("description", "").startswith("Auto-generated SQL skill for agent"):
|
|
457
|
+
continue
|
|
458
|
+
skills.append(skill.as_dict())
|
|
459
|
+
skills_extra_parameters[skill.name] = rel.parameters or {}
|
|
460
|
+
|
|
461
|
+
params = self.params.copy()
|
|
462
|
+
|
|
463
|
+
agent_dict = {
|
|
452
464
|
"id": self.id,
|
|
453
465
|
"name": self.name,
|
|
454
466
|
"project_id": self.project_id,
|
|
455
|
-
"model_name": self.model_name,
|
|
456
|
-
"skills": [rel.skill.as_dict() for rel in self.skills_relationships],
|
|
457
|
-
"skills_extra_parameters": {rel.skill.name: (rel.parameters or {}) for rel in self.skills_relationships},
|
|
458
|
-
"provider": self.provider,
|
|
459
|
-
"params": self.params,
|
|
460
467
|
"updated_at": self.updated_at,
|
|
461
468
|
"created_at": self.created_at,
|
|
462
469
|
}
|
|
463
470
|
|
|
471
|
+
if self.model_name:
|
|
472
|
+
agent_dict["model_name"] = self.model_name
|
|
473
|
+
|
|
474
|
+
if self.provider:
|
|
475
|
+
agent_dict["provider"] = self.provider
|
|
476
|
+
|
|
477
|
+
# Since skills were depreciated, they are only used with Minds
|
|
478
|
+
# Minds expects the parameters to be provided as is without breaking them down
|
|
479
|
+
if skills:
|
|
480
|
+
agent_dict["skills"] = skills
|
|
481
|
+
agent_dict["skills_extra_parameters"] = skills_extra_parameters
|
|
482
|
+
agent_dict["params"] = params
|
|
483
|
+
else:
|
|
484
|
+
data = params.pop("data", {})
|
|
485
|
+
model = params.pop("model", {})
|
|
486
|
+
prompt_template = params.pop("prompt_template", None)
|
|
487
|
+
if data:
|
|
488
|
+
agent_dict["data"] = data
|
|
489
|
+
if model:
|
|
490
|
+
agent_dict["model"] = model
|
|
491
|
+
if prompt_template:
|
|
492
|
+
agent_dict["prompt_template"] = prompt_template
|
|
493
|
+
if params:
|
|
494
|
+
agent_dict["params"] = params
|
|
495
|
+
|
|
496
|
+
return agent_dict
|
|
497
|
+
|
|
464
498
|
|
|
465
499
|
class KnowledgeBase(Base):
|
|
466
500
|
__tablename__ = "knowledge_base"
|
|
@@ -15,13 +15,9 @@ ENV_VAR_PREFIX = "MDB_"
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class VariablesController:
|
|
18
|
-
|
|
19
18
|
def __init__(self) -> None:
|
|
20
|
-
self._storage = get_json_storage(
|
|
21
|
-
|
|
22
|
-
resource_group=RESOURCE_GROUP.SYSTEM
|
|
23
|
-
)
|
|
24
|
-
self._store_key = 'variables'
|
|
19
|
+
self._storage = get_json_storage(resource_id=0, resource_group=RESOURCE_GROUP.SYSTEM)
|
|
20
|
+
self._store_key = "variables"
|
|
25
21
|
self._data = None
|
|
26
22
|
|
|
27
23
|
def _get_data(self) -> dict:
|
|
@@ -54,7 +50,7 @@ class VariablesController:
|
|
|
54
50
|
return os.environ[var_name]
|
|
55
51
|
|
|
56
52
|
def _get_function(self, name: str) -> Callable:
|
|
57
|
-
if name ==
|
|
53
|
+
if name == "from_env":
|
|
58
54
|
return self._from_env
|
|
59
55
|
raise ValueError(f"Function {name} is not found")
|
|
60
56
|
|
|
@@ -81,16 +77,13 @@ class VariablesController:
|
|
|
81
77
|
|
|
82
78
|
if isinstance(var, Variable):
|
|
83
79
|
return self.get_value(var.value.lower())
|
|
80
|
+
if isinstance(var, Function):
|
|
81
|
+
fnc = self._get_function(var.op)
|
|
82
|
+
return fnc(*var.args)
|
|
84
83
|
elif isinstance(var, dict):
|
|
85
|
-
return {
|
|
86
|
-
key: self.fill_parameters(value)
|
|
87
|
-
for key, value in var.items()
|
|
88
|
-
}
|
|
84
|
+
return {key: self.fill_parameters(value) for key, value in var.items()}
|
|
89
85
|
elif isinstance(var, list):
|
|
90
|
-
return [
|
|
91
|
-
self.fill_parameters(value)
|
|
92
|
-
for value in var
|
|
93
|
-
]
|
|
86
|
+
return [self.fill_parameters(value) for value in var]
|
|
94
87
|
return var
|
|
95
88
|
|
|
96
89
|
|
mindsdb/utilities/config.py
CHANGED
|
@@ -318,7 +318,7 @@ class Config:
|
|
|
318
318
|
self._env_config["logging"]["handlers"]["console"]["level"] = os.environ["MINDSDB_LOG_LEVEL"]
|
|
319
319
|
self._env_config["logging"]["handlers"]["console"]["enabled"] = True
|
|
320
320
|
if os.environ.get("MINDSDB_CONSOLE_LOG_LEVEL", "") != "":
|
|
321
|
-
self._env_config["logging"]["handlers"]["console"]["level"] = os.environ["
|
|
321
|
+
self._env_config["logging"]["handlers"]["console"]["level"] = os.environ["MINDSDB_CONSOLE_LOG_LEVEL"]
|
|
322
322
|
self._env_config["logging"]["handlers"]["console"]["enabled"] = True
|
|
323
323
|
if os.environ.get("MINDSDB_FILE_LOG_LEVEL", "") != "":
|
|
324
324
|
self._env_config["logging"]["handlers"]["file"]["level"] = os.environ["MINDSDB_FILE_LOG_LEVEL"]
|
|
@@ -459,8 +459,8 @@ class Config:
|
|
|
459
459
|
"""Merge multiple configs to one."""
|
|
460
460
|
new_config = deepcopy(self._default_config)
|
|
461
461
|
_merge_configs(new_config, self._user_config)
|
|
462
|
-
_merge_configs(new_config, self._auto_config)
|
|
463
|
-
_merge_configs(new_config, self._env_config)
|
|
462
|
+
_merge_configs(new_config, self._auto_config or {})
|
|
463
|
+
_merge_configs(new_config, self._env_config or {})
|
|
464
464
|
|
|
465
465
|
# Apply command-line arguments for A2A
|
|
466
466
|
a2a_config = {}
|
mindsdb/utilities/functions.py
CHANGED
|
@@ -35,20 +35,19 @@ def get_handler_install_message(handler_name):
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def cast_row_types(row, field_types):
|
|
38
|
-
|
|
39
|
-
'''
|
|
38
|
+
""" """
|
|
40
39
|
keys = [x for x in row.keys() if x in field_types]
|
|
41
40
|
for key in keys:
|
|
42
41
|
t = field_types[key]
|
|
43
|
-
if t ==
|
|
44
|
-
timestamp = datetime.datetime.
|
|
45
|
-
row[key] = timestamp.strftime(
|
|
46
|
-
elif t ==
|
|
47
|
-
timestamp = datetime.datetime.
|
|
48
|
-
row[key] = timestamp.strftime(
|
|
49
|
-
elif t ==
|
|
42
|
+
if t == "Timestamp" and isinstance(row[key], (int, float)):
|
|
43
|
+
timestamp = datetime.datetime.fromtimestamp(row[key], datetime.timezone.utc)
|
|
44
|
+
row[key] = timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
|
45
|
+
elif t == "Date" and isinstance(row[key], (int, float)):
|
|
46
|
+
timestamp = datetime.datetime.fromtimestamp(row[key], datetime.timezone.utc)
|
|
47
|
+
row[key] = timestamp.strftime("%Y-%m-%d")
|
|
48
|
+
elif t == "Int" and isinstance(row[key], (int, float, str)):
|
|
50
49
|
try:
|
|
51
|
-
logger.debug(f
|
|
50
|
+
logger.debug(f"cast {row[key]} to {int(row[key])}")
|
|
52
51
|
row[key] = int(row[key])
|
|
53
52
|
except Exception:
|
|
54
53
|
pass
|
|
@@ -67,13 +66,16 @@ def mark_process(name: str, custom_mark: str = None) -> Callable:
|
|
|
67
66
|
return func(*args, **kwargs)
|
|
68
67
|
finally:
|
|
69
68
|
delete_process_mark(name, mark)
|
|
69
|
+
|
|
70
70
|
return wrapper
|
|
71
|
+
|
|
71
72
|
return mark_process_wrapper
|
|
72
73
|
|
|
73
74
|
|
|
74
75
|
def init_lexer_parsers():
|
|
75
76
|
from mindsdb_sql_parser.lexer import MindsDBLexer
|
|
76
77
|
from mindsdb_sql_parser.parser import MindsDBParser
|
|
78
|
+
|
|
77
79
|
return MindsDBLexer(), MindsDBParser()
|
|
78
80
|
|
|
79
81
|
|
|
@@ -86,62 +88,72 @@ def resolve_table_identifier(identifier: Identifier, default_database: str = Non
|
|
|
86
88
|
elif parts_count == 2:
|
|
87
89
|
return (parts[0], parts[1])
|
|
88
90
|
else:
|
|
89
|
-
raise Exception(f
|
|
91
|
+
raise Exception(f"Table identifier must contain max 2 parts: {parts}")
|
|
90
92
|
|
|
91
93
|
|
|
92
94
|
def resolve_model_identifier(identifier: Identifier) -> tuple:
|
|
93
|
-
""" split model name to parts
|
|
94
|
-
|
|
95
|
-
Identifier may be:
|
|
96
|
-
|
|
97
|
-
Examples:
|
|
98
|
-
>>> resolve_model_identifier(['a', 'b'])
|
|
99
|
-
('a', 'b', None)
|
|
100
|
-
|
|
101
|
-
>>> resolve_model_identifier(['a', '1'])
|
|
102
|
-
(None, 'a', 1)
|
|
103
|
-
|
|
104
|
-
>>> resolve_model_identifier(['a'])
|
|
105
|
-
(None, 'a', None)
|
|
106
|
-
|
|
107
|
-
>>> resolve_model_identifier(['a', 'b', 'c'])
|
|
108
|
-
(None, None, None) # not found
|
|
109
|
-
|
|
110
|
-
Args:
|
|
111
|
-
name (Identifier): Identifier parts
|
|
112
|
-
|
|
113
|
-
Returns:
|
|
114
|
-
tuple: (database_name, model_name, model_version)
|
|
115
95
|
"""
|
|
116
|
-
|
|
117
|
-
|
|
96
|
+
Splits a model identifier into its database, model name, and version components.
|
|
97
|
+
|
|
98
|
+
The identifier may contain one, two, or three parts.
|
|
99
|
+
The function supports both quoted and unquoted identifiers, and normalizes names to lowercase if unquoted.
|
|
100
|
+
|
|
101
|
+
Examples:
|
|
102
|
+
>>> resolve_model_identifier(Identifier(parts=['a', 'b']))
|
|
103
|
+
('a', 'b', None)
|
|
104
|
+
>>> resolve_model_identifier(Identifier(parts=['a', '1']))
|
|
105
|
+
(None, 'a', 1)
|
|
106
|
+
>>> resolve_model_identifier(Identifier(parts=['a']))
|
|
107
|
+
(None, 'a', None)
|
|
108
|
+
>>> resolve_model_identifier(Identifier(parts=['a', 'b', 'c']))
|
|
109
|
+
(None, None, None) # not found
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
identifier (Identifier): The identifier object containing parts and is_quoted attributes.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
tuple: (database_name, model_name, model_version)
|
|
116
|
+
- database_name (str or None): The name of the database/project, or None if not specified.
|
|
117
|
+
- model_name (str or None): The name of the model, or None if not found.
|
|
118
|
+
- model_version (int or None): The model version as an integer, or None if not specified.
|
|
119
|
+
"""
|
|
118
120
|
model_name = None
|
|
119
|
-
|
|
121
|
+
db_name = None
|
|
122
|
+
version = None
|
|
123
|
+
model_name_quoted = None
|
|
124
|
+
db_name_quoted = None
|
|
125
|
+
|
|
126
|
+
match identifier.parts, identifier.is_quoted:
|
|
127
|
+
case [model_name], [model_name_quoted]:
|
|
128
|
+
...
|
|
129
|
+
case [model_name, str(version)], [model_name_quoted, _] if version.isdigit():
|
|
130
|
+
...
|
|
131
|
+
case [model_name, int(version)], [model_name_quoted, _]:
|
|
132
|
+
...
|
|
133
|
+
case [db_name, model_name], [db_name_quoted, model_name_quoted]:
|
|
134
|
+
...
|
|
135
|
+
case [db_name, model_name, str(version)], [db_name_quoted, model_name_quoted, _] if version.isdigit():
|
|
136
|
+
...
|
|
137
|
+
case [db_name, model_name, int(version)], [db_name_quoted, model_name_quoted, _]:
|
|
138
|
+
...
|
|
139
|
+
case [db_name, model_name, str(version)], [db_name_quoted, model_name_quoted, _]:
|
|
140
|
+
# for back compatibility. May be delete?
|
|
141
|
+
return (None, None, None)
|
|
142
|
+
case _:
|
|
143
|
+
... # may be raise ValueError?
|
|
144
|
+
|
|
145
|
+
if model_name_quoted is False:
|
|
146
|
+
model_name = model_name.lower()
|
|
147
|
+
|
|
148
|
+
if db_name_quoted is False:
|
|
149
|
+
db_name = db_name.lower()
|
|
150
|
+
|
|
151
|
+
if isinstance(version, int) or isinstance(version, str) and version.isdigit():
|
|
152
|
+
version = int(version)
|
|
153
|
+
else:
|
|
154
|
+
version = None
|
|
120
155
|
|
|
121
|
-
|
|
122
|
-
if parts_count == 1:
|
|
123
|
-
database_name = None
|
|
124
|
-
model_name = parts[0]
|
|
125
|
-
model_version = None
|
|
126
|
-
elif parts_count == 2:
|
|
127
|
-
if parts[-1].isdigit():
|
|
128
|
-
database_name = None
|
|
129
|
-
model_name = parts[0]
|
|
130
|
-
model_version = int(parts[-1])
|
|
131
|
-
else:
|
|
132
|
-
database_name = parts[0]
|
|
133
|
-
model_name = parts[1]
|
|
134
|
-
model_version = None
|
|
135
|
-
elif parts_count == 3:
|
|
136
|
-
database_name = parts[0]
|
|
137
|
-
model_name = parts[1]
|
|
138
|
-
if parts[2].isdigit():
|
|
139
|
-
model_version = int(parts[2])
|
|
140
|
-
else:
|
|
141
|
-
# not found
|
|
142
|
-
return None, None, None
|
|
143
|
-
|
|
144
|
-
return database_name, model_name, model_version
|
|
156
|
+
return db_name, model_name, version
|
|
145
157
|
|
|
146
158
|
|
|
147
159
|
def encrypt(string: bytes, key: str) -> bytes:
|
mindsdb/utilities/log.py
CHANGED
|
@@ -43,6 +43,13 @@ class ColorFormatter(logging.Formatter):
|
|
|
43
43
|
return log_fmt.format(record)
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
FORMATTERS = {
|
|
47
|
+
"default": {"()": ColorFormatter},
|
|
48
|
+
"json": {"()": JsonFormatter},
|
|
49
|
+
"file": {"format": "%(asctime)s %(processName)15s %(levelname)-8s %(name)s: %(message)s"},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
46
53
|
def get_console_handler_config_level() -> int:
|
|
47
54
|
console_handler_config = app_config["logging"]["handlers"]["console"]
|
|
48
55
|
return getattr(logging, console_handler_config["level"])
|
|
@@ -60,7 +67,7 @@ def get_mindsdb_log_level() -> int:
|
|
|
60
67
|
return min(console_handler_config_level, file_handler_config_level)
|
|
61
68
|
|
|
62
69
|
|
|
63
|
-
def
|
|
70
|
+
def get_handlers_config(process_name: str) -> dict:
|
|
64
71
|
handlers_config = {}
|
|
65
72
|
console_handler_config = app_config["logging"]["handlers"]["console"]
|
|
66
73
|
console_handler_config_level = getattr(logging, console_handler_config["level"])
|
|
@@ -89,16 +96,41 @@ def configure_logging(process_name: str = None):
|
|
|
89
96
|
"maxBytes": file_handler_config["maxBytes"], # 0.5 Mb
|
|
90
97
|
"backupCount": file_handler_config["backupCount"],
|
|
91
98
|
}
|
|
99
|
+
return handlers_config
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_uvicorn_logging_config(process_name: str) -> dict:
|
|
103
|
+
"""Generate a logging configuration dictionary for Uvicorn using MindsDB's logging settings.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
process_name (str): The name of the process to include in log file names and handlers.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
dict: A dictionary suitable for use with logging.config.dictConfig, configured for Uvicorn logging.
|
|
110
|
+
"""
|
|
111
|
+
handlers_config = get_handlers_config(process_name)
|
|
112
|
+
mindsdb_log_level = get_mindsdb_log_level()
|
|
113
|
+
return {
|
|
114
|
+
"version": 1,
|
|
115
|
+
"formatters": FORMATTERS,
|
|
116
|
+
"handlers": handlers_config,
|
|
117
|
+
"loggers": {
|
|
118
|
+
"uvicorn": {
|
|
119
|
+
"handlers": list(handlers_config.keys()),
|
|
120
|
+
"level": mindsdb_log_level,
|
|
121
|
+
"propagate": False,
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
|
|
92
126
|
|
|
127
|
+
def configure_logging(process_name: str = None):
|
|
128
|
+
handlers_config = get_handlers_config(process_name)
|
|
93
129
|
mindsdb_log_level = get_mindsdb_log_level()
|
|
94
130
|
|
|
95
131
|
logging_config = dict(
|
|
96
132
|
version=1,
|
|
97
|
-
formatters=
|
|
98
|
-
"default": {"()": ColorFormatter},
|
|
99
|
-
"json": {"()": JsonFormatter},
|
|
100
|
-
"file": {"format": "%(asctime)s %(processName)15s %(levelname)-8s %(name)s: %(message)s"},
|
|
101
|
-
},
|
|
133
|
+
formatters=FORMATTERS,
|
|
102
134
|
handlers=handlers_config,
|
|
103
135
|
loggers={
|
|
104
136
|
"": { # root logger
|
mindsdb/utilities/ps.py
CHANGED
|
@@ -11,23 +11,23 @@ def get_child_pids(pid):
|
|
|
11
11
|
|
|
12
12
|
def net_connections():
|
|
13
13
|
"""Cross-platform psutil.net_connections like interface"""
|
|
14
|
-
if sys.platform.lower().startswith(
|
|
14
|
+
if sys.platform.lower().startswith("linux"):
|
|
15
15
|
return psutil.net_connections()
|
|
16
16
|
|
|
17
17
|
all_connections = []
|
|
18
18
|
Pconn = None
|
|
19
|
-
for p in psutil.process_iter([
|
|
19
|
+
for p in psutil.process_iter(["pid"]):
|
|
20
20
|
try:
|
|
21
21
|
process = psutil.Process(p.pid)
|
|
22
|
-
connections = process.
|
|
22
|
+
connections = process.net_connections()
|
|
23
23
|
if connections:
|
|
24
24
|
for conn in connections:
|
|
25
25
|
# Adding pid to the returned instance
|
|
26
26
|
# for consistency with psutil.net_connections()
|
|
27
27
|
if Pconn is None:
|
|
28
28
|
fields = list(conn._fields)
|
|
29
|
-
fields.append(
|
|
30
|
-
_conn = namedtuple(
|
|
29
|
+
fields.append("pid")
|
|
30
|
+
_conn = namedtuple("Pconn", fields)
|
|
31
31
|
for attr in conn._fields:
|
|
32
32
|
setattr(_conn, attr, getattr(conn, attr))
|
|
33
33
|
_conn.pid = p.pid
|
|
@@ -43,7 +43,7 @@ def is_port_in_use(port_num):
|
|
|
43
43
|
parent_process = psutil.Process()
|
|
44
44
|
child_pids = [x.pid for x in parent_process.children(recursive=True)]
|
|
45
45
|
conns = net_connections()
|
|
46
|
-
portsinuse = [x.laddr[1] for x in conns if x.pid in child_pids and x.status ==
|
|
46
|
+
portsinuse = [x.laddr[1] for x in conns if x.pid in child_pids and x.status == "LISTEN"]
|
|
47
47
|
portsinuse.sort()
|
|
48
48
|
return int(port_num) in portsinuse
|
|
49
49
|
|
|
@@ -66,7 +66,7 @@ def wait_port(port_num, timeout):
|
|
|
66
66
|
def get_listen_ports(pid):
|
|
67
67
|
try:
|
|
68
68
|
p = psutil.Process(pid)
|
|
69
|
-
cons = p.
|
|
69
|
+
cons = p.net_connections()
|
|
70
70
|
cons = [x.laddr.port for x in cons]
|
|
71
71
|
except Exception:
|
|
72
72
|
return []
|