MindsDB 25.4.4.0__py3-none-any.whl → 25.5.3.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 (86) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +107 -125
  3. mindsdb/api/executor/command_executor.py +14 -3
  4. mindsdb/api/executor/datahub/datanodes/information_schema_datanode.py +8 -0
  5. mindsdb/api/executor/datahub/datanodes/mindsdb_tables.py +2 -1
  6. mindsdb/api/executor/datahub/datanodes/system_tables.py +10 -13
  7. mindsdb/api/executor/planner/query_plan.py +1 -0
  8. mindsdb/api/executor/planner/query_planner.py +9 -1
  9. mindsdb/api/executor/sql_query/sql_query.py +24 -8
  10. mindsdb/api/executor/sql_query/steps/apply_predictor_step.py +21 -3
  11. mindsdb/api/executor/sql_query/steps/fetch_dataframe_partition.py +3 -1
  12. mindsdb/api/http/initialize.py +20 -3
  13. mindsdb/api/http/namespaces/analysis.py +14 -1
  14. mindsdb/api/http/namespaces/config.py +19 -11
  15. mindsdb/api/http/namespaces/tree.py +1 -1
  16. mindsdb/api/http/start.py +7 -2
  17. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +4 -8
  18. mindsdb/api/mysql/mysql_proxy/utilities/exceptions.py +0 -4
  19. mindsdb/api/postgres/postgres_proxy/postgres_packets/postgres_message_formats.py +2 -2
  20. mindsdb/integrations/handlers/bigquery_handler/requirements.txt +1 -0
  21. mindsdb/integrations/handlers/chromadb_handler/requirements.txt +1 -0
  22. mindsdb/integrations/handlers/gmail_handler/requirements.txt +1 -0
  23. mindsdb/integrations/handlers/google_analytics_handler/requirements.txt +2 -1
  24. mindsdb/integrations/handlers/google_books_handler/requirements.txt +1 -1
  25. mindsdb/integrations/handlers/google_calendar_handler/requirements.txt +1 -0
  26. mindsdb/integrations/handlers/google_content_shopping_handler/requirements.txt +1 -1
  27. mindsdb/integrations/handlers/google_fit_handler/requirements.txt +2 -0
  28. mindsdb/integrations/handlers/google_search_handler/requirements.txt +1 -1
  29. mindsdb/integrations/handlers/jira_handler/jira_handler.archived.py +75 -0
  30. mindsdb/integrations/handlers/jira_handler/jira_handler.py +113 -38
  31. mindsdb/integrations/handlers/jira_handler/jira_tables.py +229 -0
  32. mindsdb/integrations/handlers/jira_handler/requirements.txt +1 -0
  33. mindsdb/integrations/handlers/lightfm_handler/requirements.txt +1 -0
  34. mindsdb/integrations/handlers/lightwood_handler/lightwood_handler.py +0 -2
  35. mindsdb/integrations/handlers/lightwood_handler/requirements.txt +4 -4
  36. mindsdb/integrations/handlers/lindorm_handler/requirements.txt +1 -0
  37. mindsdb/integrations/handlers/ms_one_drive_handler/requirements.txt +2 -0
  38. mindsdb/integrations/handlers/ms_teams_handler/requirements.txt +3 -1
  39. mindsdb/integrations/handlers/openai_handler/helpers.py +3 -5
  40. mindsdb/integrations/handlers/openai_handler/openai_handler.py +25 -12
  41. mindsdb/integrations/handlers/snowflake_handler/requirements.txt +1 -1
  42. mindsdb/integrations/handlers/togetherai_handler/__about__.py +9 -0
  43. mindsdb/integrations/handlers/togetherai_handler/__init__.py +20 -0
  44. mindsdb/integrations/handlers/togetherai_handler/creation_args.py +14 -0
  45. mindsdb/integrations/handlers/togetherai_handler/icon.svg +15 -0
  46. mindsdb/integrations/handlers/togetherai_handler/model_using_args.py +5 -0
  47. mindsdb/integrations/handlers/togetherai_handler/requirements.txt +2 -0
  48. mindsdb/integrations/handlers/togetherai_handler/settings.py +33 -0
  49. mindsdb/integrations/handlers/togetherai_handler/togetherai_handler.py +234 -0
  50. mindsdb/integrations/handlers/vertex_handler/requirements.txt +1 -0
  51. mindsdb/integrations/handlers/youtube_handler/requirements.txt +1 -0
  52. mindsdb/integrations/utilities/files/file_reader.py +5 -2
  53. mindsdb/integrations/utilities/handler_utils.py +4 -0
  54. mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +360 -0
  55. mindsdb/integrations/utilities/rag/rerankers/reranker_compressor.py +6 -346
  56. mindsdb/interfaces/agents/constants.py +14 -2
  57. mindsdb/interfaces/agents/langchain_agent.py +2 -4
  58. mindsdb/interfaces/database/projects.py +1 -7
  59. mindsdb/interfaces/functions/controller.py +14 -16
  60. mindsdb/interfaces/functions/to_markdown.py +9 -124
  61. mindsdb/interfaces/knowledge_base/controller.py +109 -92
  62. mindsdb/interfaces/knowledge_base/preprocessing/document_preprocessor.py +28 -5
  63. mindsdb/interfaces/knowledge_base/utils.py +10 -15
  64. mindsdb/interfaces/model/model_controller.py +0 -2
  65. mindsdb/interfaces/query_context/context_controller.py +55 -15
  66. mindsdb/interfaces/query_context/query_task.py +19 -0
  67. mindsdb/interfaces/skills/sql_agent.py +33 -11
  68. mindsdb/interfaces/storage/db.py +2 -2
  69. mindsdb/interfaces/tasks/task_monitor.py +5 -1
  70. mindsdb/interfaces/tasks/task_thread.py +6 -0
  71. mindsdb/migrations/migrate.py +0 -2
  72. mindsdb/migrations/versions/2025-04-22_53502b6d63bf_query_database.py +27 -0
  73. mindsdb/utilities/config.py +15 -3
  74. mindsdb/utilities/context.py +2 -1
  75. mindsdb/utilities/functions.py +0 -36
  76. mindsdb/utilities/langfuse.py +19 -10
  77. mindsdb/utilities/otel/__init__.py +9 -193
  78. mindsdb/utilities/otel/metric_handlers/__init__.py +5 -1
  79. mindsdb/utilities/otel/prepare.py +198 -0
  80. mindsdb/utilities/sql.py +83 -0
  81. {mindsdb-25.4.4.0.dist-info → mindsdb-25.5.3.0.dist-info}/METADATA +662 -592
  82. {mindsdb-25.4.4.0.dist-info → mindsdb-25.5.3.0.dist-info}/RECORD +85 -69
  83. {mindsdb-25.4.4.0.dist-info → mindsdb-25.5.3.0.dist-info}/WHEEL +1 -1
  84. mindsdb/api/mysql/mysql_proxy/classes/sql_statement_parser.py +0 -151
  85. {mindsdb-25.4.4.0.dist-info → mindsdb-25.5.3.0.dist-info}/licenses/LICENSE +0 -0
  86. {mindsdb-25.4.4.0.dist-info → mindsdb-25.5.3.0.dist-info}/top_level.txt +0 -0
@@ -15,6 +15,7 @@ from mindsdb.utilities.cache import get_cache
15
15
 
16
16
  from mindsdb.interfaces.storage import db
17
17
  from mindsdb.utilities.context import context as ctx
18
+ from mindsdb.utilities.config import config
18
19
 
19
20
  from .last_query import LastQuery
20
21
 
@@ -24,9 +25,12 @@ class RunningQuery:
24
25
  Query in progres
25
26
  """
26
27
 
28
+ OBJECT_TYPE = 'query'
29
+
27
30
  def __init__(self, record: db.Queries):
28
31
  self.record = record
29
32
  self.sql = record.sql
33
+ self.database = record.database or config.get('default_project')
30
34
 
31
35
  def get_partition_query(self, step_num: int, query: Select) -> Select:
32
36
  """
@@ -67,6 +71,44 @@ class RunningQuery:
67
71
 
68
72
  return query
69
73
 
74
+ def get_info(self):
75
+ record = self.record
76
+ return {
77
+ 'id': record.id,
78
+ 'sql': record.sql,
79
+ 'database': record.database,
80
+ 'started_at': record.started_at,
81
+ 'finished_at': record.finished_at,
82
+ 'parameters': record.parameters,
83
+ 'context': record.context,
84
+ 'processed_rows': record.processed_rows,
85
+ 'error': record.error,
86
+ 'updated_at': record.updated_at,
87
+ }
88
+
89
+ def add_to_task(self):
90
+
91
+ task_record = db.Tasks(
92
+ company_id=ctx.company_id,
93
+ user_class=ctx.user_class,
94
+
95
+ object_type=self.OBJECT_TYPE,
96
+ object_id=self.record.id,
97
+ )
98
+ db.session.add(task_record)
99
+ db.session.commit()
100
+
101
+ def remove_from_task(self):
102
+ task = db.Tasks.query.filter(
103
+ db.Tasks.object_type == self.OBJECT_TYPE,
104
+ db.Tasks.object_id == self.record.id,
105
+ db.Tasks.company_id == ctx.company_id,
106
+ ).first()
107
+
108
+ if task is not None:
109
+ db.session.delete(task)
110
+ db.session.commit()
111
+
70
112
  def set_params(self, params: dict):
71
113
  """
72
114
  Store parameters of the step which is about to be split into partitions
@@ -126,14 +168,21 @@ class RunningQuery:
126
168
 
127
169
  db.session.commit()
128
170
 
129
- def clear_error(self):
171
+ def mark_as_run(self):
130
172
  """
131
- Reset error of the query in database
173
+ Mark query as running and reset error of the query
132
174
  """
175
+ if self.record.finished_at is not None:
176
+ raise RuntimeError('The query already finished')
133
177
 
134
- if self.record.error is not None:
178
+ if self.record.started_at is None:
179
+ self.record.started_at = dt.datetime.now()
180
+ db.session.commit()
181
+ elif self.record.error is not None:
135
182
  self.record.error = None
136
183
  db.session.commit()
184
+ else:
185
+ raise RuntimeError('The query might be running already')
137
186
 
138
187
  def get_state(self) -> dict:
139
188
  """
@@ -448,7 +497,7 @@ class QueryContextController:
448
497
  raise RuntimeError(f'Query not found: {query_id}')
449
498
  return RunningQuery(rec)
450
499
 
451
- def create_query(self, query: ASTNode) -> RunningQuery:
500
+ def create_query(self, query: ASTNode, database: str = None) -> RunningQuery:
452
501
  """
453
502
  Create a new running query from AST query
454
503
  """
@@ -463,6 +512,7 @@ class QueryContextController:
463
512
 
464
513
  rec = db.Queries(
465
514
  sql=str(query),
515
+ database=database,
466
516
  company_id=ctx.company_id,
467
517
  )
468
518
 
@@ -479,17 +529,7 @@ class QueryContextController:
479
529
  db.Queries.company_id == ctx.company_id
480
530
  )
481
531
  return [
482
- {
483
- 'id': record.id,
484
- 'sql': record.sql,
485
- 'started_at': record.started_at,
486
- 'finished_at': record.finished_at,
487
- 'parameters': record.parameters,
488
- 'context': record.context,
489
- 'processed_rows': record.processed_rows,
490
- 'error': record.error,
491
- 'updated_at': record.updated_at,
492
- }
532
+ RunningQuery(record).get_info()
493
533
  for record in query
494
534
  ]
495
535
 
@@ -0,0 +1,19 @@
1
+ from mindsdb.api.executor.sql_query import SQLQuery
2
+ from mindsdb.interfaces.query_context.context_controller import query_context_controller
3
+ from mindsdb.api.executor.controllers.session_controller import SessionController
4
+ from mindsdb.interfaces.tasks.task import BaseTask
5
+
6
+
7
+ class QueryTask(BaseTask):
8
+ def __init__(self, *args, **kwargs):
9
+ super().__init__(*args, **kwargs)
10
+ self.query_id = self.object_id
11
+
12
+ def run(self, stop_event):
13
+
14
+ try:
15
+ session = SessionController()
16
+ SQLQuery(None, query_id=self.query_id, session=session, stop_event=stop_event)
17
+ finally:
18
+ # clear task
19
+ query_context_controller.get_query(self.query_id).remove_from_task()
@@ -1,4 +1,3 @@
1
-
2
1
  import re
3
2
  import csv
4
3
  import inspect
@@ -13,6 +12,7 @@ from mindsdb.utilities import log
13
12
  from mindsdb.utilities.context import context as ctx
14
13
  from mindsdb.integrations.utilities.query_traversal import query_traversal
15
14
  from mindsdb.integrations.libs.response import INF_SCHEMA_COLUMNS_NAMES
15
+ from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import MYSQL_DATA_TYPE
16
16
 
17
17
  logger = log.getLogger(__name__)
18
18
 
@@ -253,7 +253,7 @@ class SQLAgent:
253
253
  for table in all_tables:
254
254
  key = f"{ctx.company_id}_{table}_info"
255
255
  table_info = self._cache.get(key) if self._cache else None
256
- if table_info is None:
256
+ if True or table_info is None:
257
257
  table_info = self._get_single_table_info(table)
258
258
  if self._cache:
259
259
  self._cache.set(key, table_info)
@@ -276,19 +276,41 @@ class SQLAgent:
276
276
  dn = self._command_executor.session.datahub.get(integration)
277
277
 
278
278
  fields, dtypes = [], []
279
- for df in dn.get_table_columns_df(table_name, schema_name):
280
- df_records = df.to_dict(orient='records')
281
- fields.append(df_records[INF_SCHEMA_COLUMNS_NAMES.COLUMN_NAME])
282
- if df_records[INF_SCHEMA_COLUMNS_NAMES.MYSQL_DATA_TYPE] is not None:
283
- dtypes.append(df_records[INF_SCHEMA_COLUMNS_NAMES.MYSQL_DATA_TYPE].value)
284
- else:
285
- dtypes.append(df_records[INF_SCHEMA_COLUMNS_NAMES.DATA_TYPE])
279
+ try:
280
+ df = dn.get_table_columns_df(table_name, schema_name)
281
+ if not isinstance(df, pd.DataFrame) or df.empty:
282
+ logger.warning(f"Received empty or invalid DataFrame for table columns of {table_str}")
283
+ return f"Table named `{table_str}`:\n [No column information available]"
284
+
285
+ fields = df[INF_SCHEMA_COLUMNS_NAMES.COLUMN_NAME].to_list()
286
+ dtypes = [
287
+ mysql_data_type.value if isinstance(mysql_data_type, MYSQL_DATA_TYPE) else (data_type or 'UNKNOWN')
288
+ for mysql_data_type, data_type
289
+ in zip(
290
+ df[INF_SCHEMA_COLUMNS_NAMES.MYSQL_DATA_TYPE],
291
+ df[INF_SCHEMA_COLUMNS_NAMES.DATA_TYPE]
292
+ )
293
+ ]
294
+ except Exception as e:
295
+ logger.error(f"Failed processing column info for {table_str}: {e}", exc_info=True)
296
+ raise ValueError(f"Failed to process column info for {table_str}") from e
297
+
298
+ if not fields:
299
+ logger.error(f"Could not extract column fields for {table_str}.")
300
+ return f"Table named `{table_str}`:\n [Could not extract column information]"
301
+
302
+ try:
303
+ sample_rows_info = self._get_sample_rows(table_str, fields)
304
+ except Exception as e:
305
+ logger.warning(f"Could not get sample rows for {table_str}: {e}")
306
+ sample_rows_info = "\n\t [error] Couldn't retrieve sample rows!"
286
307
 
287
308
  info = f'Table named `{table_str}`:\n'
288
309
  info += f"\nSample with first {self._sample_rows_in_table_info} rows from table {table_str} in CSV format (dialect is 'excel'):\n"
289
- info += self._get_sample_rows(table_str, fields) + "\n"
310
+ info += sample_rows_info + "\n"
290
311
  info += '\nColumn data types: ' + ",\t".join(
291
- [f'\n`{field}` : `{dtype}`' for field, dtype in zip(fields, dtypes)]) + '\n' # noqa
312
+ [f'\n`{field}` : `{dtype}`' for field, dtype in zip(fields, dtypes)]
313
+ ) + '\n'
292
314
  return info
293
315
 
294
316
  def _get_sample_rows(self, table: str, fields: List[str]) -> str:
@@ -571,9 +571,9 @@ class Queries(Base):
571
571
  company_id: int = Column(Integer, nullable=True)
572
572
 
573
573
  sql: str = Column(String, nullable=False)
574
- # step_data: JSON = Column(JSON, nullable=True)
574
+ database: str = Column(String, nullable=True)
575
575
 
576
- started_at: datetime.datetime = Column(DateTime, default=datetime.datetime.now)
576
+ started_at: datetime.datetime = Column(DateTime)
577
577
  finished_at: datetime.datetime = Column(DateTime)
578
578
 
579
579
  parameters = Column(JSON, default={})
@@ -2,6 +2,7 @@ import datetime as dt
2
2
  import os
3
3
  import socket
4
4
  import time
5
+ from threading import Event
5
6
 
6
7
  import sqlalchemy as sa
7
8
 
@@ -22,7 +23,7 @@ class TaskMonitor:
22
23
  def __init__(self):
23
24
  self._active_tasks = {}
24
25
 
25
- def start(self):
26
+ def start(self, stop_event: Event = None):
26
27
  config = Config()
27
28
  db.init()
28
29
  self.config = config
@@ -42,6 +43,9 @@ class TaskMonitor:
42
43
  logger.error(e)
43
44
  db.session.rollback()
44
45
 
46
+ if stop_event is not None and stop_event.is_set():
47
+ return
48
+
45
49
  def stop_all_tasks(self):
46
50
 
47
51
  active_tasks = list(self._active_tasks.keys())
@@ -6,6 +6,7 @@ from mindsdb.utilities import log
6
6
 
7
7
  from mindsdb.interfaces.triggers.trigger_task import TriggerTask
8
8
  from mindsdb.interfaces.chatbot.chatbot_task import ChatBotTask
9
+ from mindsdb.interfaces.query_context.query_task import QueryTask
9
10
 
10
11
  logger = log.getLogger(__name__)
11
12
 
@@ -28,6 +29,7 @@ class TaskThread(threading.Thread):
28
29
  ctx.company_id = task_record.company_id
29
30
  if task_record.user_class is not None:
30
31
  ctx.user_class = task_record.user_class
32
+ ctx.task_id = task_record.id
31
33
 
32
34
  self.object_type = task_record.object_type
33
35
  self.object_id = task_record.object_id
@@ -43,6 +45,10 @@ class TaskThread(threading.Thread):
43
45
  bot = ChatBotTask(self.task_id, self.object_id)
44
46
  bot.run(self._stop_event)
45
47
 
48
+ elif self.object_type == 'query':
49
+ query = QueryTask(self.task_id, self.object_id)
50
+ query.run(self._stop_event)
51
+
46
52
  except Exception:
47
53
  logger.error(traceback.format_exc())
48
54
  task_record.last_error = str(traceback.format_exc())
@@ -47,7 +47,5 @@ def migrate_to_head():
47
47
  if __name__ == "__main__":
48
48
  # have to import this because
49
49
  # all env initialization happens here
50
- from mindsdb.utilities.config import Config as MDBConfig
51
- MDBConfig()
52
50
  db.init()
53
51
  migrate_to_head()
@@ -0,0 +1,27 @@
1
+ """query_database
2
+
3
+ Revision ID: 53502b6d63bf
4
+ Revises: fda503400e43
5
+ Create Date: 2025-04-22 16:30:15.139978
6
+
7
+ """
8
+ from alembic import op
9
+ import sqlalchemy as sa
10
+ import mindsdb.interfaces.storage.db # noqa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = '53502b6d63bf'
15
+ down_revision = 'fda503400e43'
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ with op.batch_alter_table('queries', schema=None) as batch_op:
22
+ batch_op.add_column(sa.Column('database', sa.String(), nullable=True))
23
+
24
+
25
+ def downgrade():
26
+ with op.batch_alter_table('queries', schema=None) as batch_op:
27
+ batch_op.drop_column('database')
@@ -143,7 +143,8 @@ class Config:
143
143
  'auth': {
144
144
  'http_auth_enabled': False,
145
145
  "http_permanent_session_lifetime": datetime.timedelta(days=31),
146
- "username": "mindsdb"
146
+ "username": "mindsdb",
147
+ "password": ""
147
148
  },
148
149
  "logging": {
149
150
  "handlers": {
@@ -230,7 +231,9 @@ class Config:
230
231
  "tasks": {
231
232
  "disable": False
232
233
  },
233
- "default_project": "mindsdb"
234
+ "default_project": "mindsdb",
235
+ "default_llm": {},
236
+ "default_embedding_model": {}
234
237
  }
235
238
  # endregion
236
239
 
@@ -369,6 +372,15 @@ class Config:
369
372
  if os.environ.get('MINDSDB_DEFAULT_PROJECT', '') != '':
370
373
  self._env_config['default_project'] = os.environ['MINDSDB_DEFAULT_PROJECT'].lower()
371
374
 
375
+ if os.environ.get('MINDSDB_DEFAULT_LLM_API_KEY', '') != '':
376
+ self._env_config['default_llm'] = {
377
+ 'api_key': os.environ['MINDSDB_DEFAULT_LLM_API_KEY']
378
+ }
379
+ if os.environ.get('MINDSDB_DEFAULT_EMBEDDING_MODEL_API_KEY', '') != '':
380
+ self._env_config['default_embedding_model'] = {
381
+ 'api_key': os.environ['MINDSDB_DEFAULT_EMBEDDING_MODEL_API_KEY']
382
+ }
383
+
372
384
  def parse_cmd_args(self) -> None:
373
385
  """Collect cmd args to self._cmd_args (accessable as self.cmd_args)
374
386
  """
@@ -448,7 +460,7 @@ class Config:
448
460
  """
449
461
  updated = self.fetch_auto_config()
450
462
  if updated:
451
- self.init_config()
463
+ self.merge_configs()
452
464
 
453
465
  def merge_configs(self) -> None:
454
466
  """Merge multiple configs to one.
@@ -18,6 +18,7 @@ class Context:
18
18
  'user_id': None,
19
19
  'company_id': None,
20
20
  'session_id': "",
21
+ 'task_id': None,
21
22
  'user_class': 0,
22
23
  'profiling': {
23
24
  'level': 0,
@@ -53,7 +54,7 @@ class Context:
53
54
  def load(self, storage: dict) -> None:
54
55
  self._storage.set(storage)
55
56
 
56
- def metadata(self, **kwargs) -> dict:
57
+ def get_metadata(self, **kwargs) -> dict:
57
58
  return {
58
59
  'user_id': self.user_id or "",
59
60
  'company_id': self.company_id or "",
@@ -7,7 +7,6 @@ import textwrap
7
7
  from functools import wraps
8
8
  from collections.abc import Callable
9
9
 
10
- import requests
11
10
  from cryptography.fernet import Fernet
12
11
  from mindsdb_sql_parser.ast import Identifier
13
12
 
@@ -72,41 +71,6 @@ def mark_process(name: str, custom_mark: str = None) -> Callable:
72
71
  return mark_process_wrapper
73
72
 
74
73
 
75
- def get_versions_where_predictors_become_obsolete():
76
- """ Get list of MindsDB versions in which predictors should be retrained
77
- Returns:
78
- list of str or False
79
- """
80
- versions_for_updating_predictors = []
81
- try:
82
- try:
83
- res = requests.get(
84
- 'https://mindsdb-cloud-public-service-files.s3.us-east-2.amazonaws.com/version_for_updating_predictors.txt',
85
- timeout=0.5
86
- )
87
- except (ConnectionError, requests.exceptions.ConnectionError) as e:
88
- logger.error(f'Is no connection. {e}')
89
- raise
90
- except Exception as e:
91
- logger.error(f'Is something wrong with getting version_for_updating_predictors.txt: {e}')
92
- raise
93
-
94
- if res.status_code != 200:
95
- logger.error(f'Cant get version_for_updating_predictors.txt: returned status code = {res.status_code}')
96
- raise
97
-
98
- try:
99
- versions_for_updating_predictors = res.text.replace(' \t\r', '').split('\n')
100
- except Exception as e:
101
- logger.error(f'Cant decode version_for_updating_predictors.txt: {e}')
102
- raise
103
- except Exception:
104
- return False, versions_for_updating_predictors
105
-
106
- versions_for_updating_predictors = [x for x in versions_for_updating_predictors if len(x) > 0]
107
- return True, versions_for_updating_predictors
108
-
109
-
110
74
  def init_lexer_parsers():
111
75
  from mindsdb_sql_parser.lexer import MindsDBLexer
112
76
  from mindsdb_sql_parser.parser import MindsDBParser
@@ -1,11 +1,12 @@
1
1
  import os
2
2
  import typing
3
+ from typing import TYPE_CHECKING
3
4
 
4
5
  from mindsdb.utilities import log
5
- from langfuse import Langfuse
6
- from langfuse.client import StatefulSpanClient
7
- from langfuse.callback import CallbackHandler
8
- from langfuse.api.resources.commons.errors.not_found_error import NotFoundError as TraceNotFoundError
6
+
7
+ if TYPE_CHECKING:
8
+ from langfuse.callback import CallbackHandler
9
+ from langfuse.client import StatefulSpanClient
9
10
 
10
11
  logger = log.getLogger(__name__)
11
12
 
@@ -98,6 +99,12 @@ class LangfuseClientWrapper:
98
99
  logger.debug(f"LANGFUSE_TIMEOUT: {LANGFUSE_TIMEOUT}")
99
100
  logger.debug(f"LANGFUSE_SAMPLE_RATE: {LANGFUSE_SAMPLE_RATE * 100}%")
100
101
 
102
+ try:
103
+ from langfuse import Langfuse
104
+ except ImportError:
105
+ logger.error("Langfuse is not installed. Please install it with `pip install langfuse`.")
106
+ return
107
+
101
108
  self.client = Langfuse(
102
109
  public_key=public_key,
103
110
  secret_key=secret_key,
@@ -164,7 +171,7 @@ class LangfuseClientWrapper:
164
171
 
165
172
  def start_span(self,
166
173
  name: str,
167
- input: typing.Optional[typing.Any] = None) -> typing.Optional[StatefulSpanClient]:
174
+ input: typing.Optional[typing.Any] = None) -> typing.Optional['StatefulSpanClient']:
168
175
  """
169
176
  Create span. If Langfuse is disabled, nothing will be done.
170
177
 
@@ -180,7 +187,7 @@ class LangfuseClientWrapper:
180
187
  return self.trace.span(name=name, input=input)
181
188
 
182
189
  def end_span_stream(self,
183
- span: typing.Optional[StatefulSpanClient] = None) -> None:
190
+ span: typing.Optional['StatefulSpanClient'] = None) -> None:
184
191
  """
185
192
  End span. If Langfuse is disabled, nothing will happen.
186
193
  Args:
@@ -195,7 +202,7 @@ class LangfuseClientWrapper:
195
202
  self.trace.update()
196
203
 
197
204
  def end_span(self,
198
- span: typing.Optional[StatefulSpanClient] = None,
205
+ span: typing.Optional['StatefulSpanClient'] = None,
199
206
  output: typing.Optional[typing.Any] = None) -> None:
200
207
  """
201
208
  End trace. If Langfuse is disabled, nothing will be done.
@@ -227,7 +234,7 @@ class LangfuseClientWrapper:
227
234
  except Exception as e:
228
235
  logger.error(f'Something went wrong while processing Langfuse trace {self.trace.id}: {str(e)}')
229
236
 
230
- def get_langchain_handler(self) -> typing.Optional[CallbackHandler]:
237
+ def get_langchain_handler(self) -> typing.Optional['CallbackHandler']:
231
238
  """
232
239
  Get Langchain handler. If Langfuse is disabled, returns None.
233
240
  """
@@ -257,8 +264,10 @@ class LangfuseClientWrapper:
257
264
  self.tags.append(self.release)
258
265
 
259
266
  def _get_tool_usage(self) -> typing.Dict:
260
- """ Retrieves tool usage information from a langfuse trace.
261
- Note: assumes trace marks an action with string `AgentAction` """
267
+ """Retrieves tool usage information from a langfuse trace.
268
+ Note: assumes trace marks an action with string `AgentAction`
269
+ """
270
+ from langfuse.api.resources.commons.errors.not_found_error import NotFoundError as TraceNotFoundError
262
271
 
263
272
  tool_usage = {}
264
273