MindsDB 25.6.2.0__py3-none-any.whl → 25.6.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 (30) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/api/a2a/agent.py +25 -4
  3. mindsdb/api/a2a/task_manager.py +68 -6
  4. mindsdb/api/executor/datahub/datanodes/mindsdb_tables.py +91 -84
  5. mindsdb/api/http/namespaces/knowledge_bases.py +132 -154
  6. mindsdb/integrations/handlers/bigquery_handler/bigquery_handler.py +219 -28
  7. mindsdb/integrations/handlers/llama_index_handler/requirements.txt +1 -1
  8. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +3 -0
  9. mindsdb/integrations/handlers/openai_handler/openai_handler.py +277 -356
  10. mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +94 -8
  11. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +19 -1
  12. mindsdb/integrations/libs/api_handler.py +19 -1
  13. mindsdb/integrations/libs/base.py +86 -2
  14. mindsdb/interfaces/agents/agents_controller.py +32 -6
  15. mindsdb/interfaces/agents/constants.py +1 -0
  16. mindsdb/interfaces/agents/mindsdb_database_agent.py +23 -18
  17. mindsdb/interfaces/data_catalog/data_catalog_loader.py +22 -6
  18. mindsdb/interfaces/data_catalog/data_catalog_reader.py +4 -0
  19. mindsdb/interfaces/database/integrations.py +4 -2
  20. mindsdb/interfaces/knowledge_base/controller.py +3 -15
  21. mindsdb/interfaces/knowledge_base/evaluate.py +0 -3
  22. mindsdb/interfaces/skills/skills_controller.py +0 -23
  23. mindsdb/interfaces/skills/sql_agent.py +8 -4
  24. mindsdb/interfaces/storage/db.py +20 -4
  25. mindsdb/utilities/config.py +5 -1
  26. {mindsdb-25.6.2.0.dist-info → mindsdb-25.6.3.0.dist-info}/METADATA +250 -250
  27. {mindsdb-25.6.2.0.dist-info → mindsdb-25.6.3.0.dist-info}/RECORD +30 -30
  28. {mindsdb-25.6.2.0.dist-info → mindsdb-25.6.3.0.dist-info}/WHEEL +0 -0
  29. {mindsdb-25.6.2.0.dist-info → mindsdb-25.6.3.0.dist-info}/licenses/LICENSE +0 -0
  30. {mindsdb-25.6.2.0.dist-info → mindsdb-25.6.3.0.dist-info}/top_level.txt +0 -0
mindsdb/__about__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  __title__ = "MindsDB"
2
2
  __package_name__ = "mindsdb"
3
- __version__ = "25.6.2.0"
3
+ __version__ = "25.6.3.0"
4
4
  __description__ = "MindsDB's AI SQL Server enables developers to build AI tools that need access to real-time data to perform their tasks"
5
5
  __email__ = "jorge@mindsdb.com"
6
6
  __author__ = "MindsDB Inc"
mindsdb/api/a2a/agent.py CHANGED
@@ -257,14 +257,35 @@ class MindsDBAgent:
257
257
  try:
258
258
  logger.info(f"Using streaming API for query: {query[:100]}...")
259
259
 
260
- # Start with history if provided, otherwise empty list
261
- messages = history or []
260
+ # Format history into the expected format
261
+ formatted_messages = []
262
+ if history:
263
+ for msg in history:
264
+ # Convert Message object to dict if needed
265
+ msg_dict = msg.dict() if hasattr(msg, "dict") else msg
266
+ role = msg_dict.get("role", "user")
267
+
268
+ # Extract text from parts
269
+ text = ""
270
+ for part in msg_dict.get("parts", []):
271
+ if part.get("type") == "text":
272
+ text = part.get("text", "")
273
+ break
274
+
275
+ if text:
276
+ if role == "user":
277
+ formatted_messages.append({"question": text, "answer": None})
278
+ elif role == "assistant" and formatted_messages:
279
+ # Add the answer to the last question
280
+ formatted_messages[-1]["answer"] = text
262
281
 
263
282
  # Add the current query to the messages
264
- messages.append({"question": query, "answer": None})
283
+ formatted_messages.append({"question": query, "answer": None})
284
+
285
+ logger.debug(f"Formatted messages for agent: {formatted_messages}")
265
286
 
266
287
  # Use the streaming_invoke method to get real streaming responses
267
- streaming_response = self.streaming_invoke(messages)
288
+ streaming_response = self.streaming_invoke(formatted_messages)
268
289
 
269
290
  # Yield all chunks directly from the streaming response
270
291
  for chunk in streaming_response:
@@ -63,7 +63,8 @@ class AgentTaskManager(InMemoryTaskManager):
63
63
 
64
64
  # Create and store the task first to ensure it exists
65
65
  try:
66
- await self.upsert_task(task_send_params)
66
+ task = await self.upsert_task(task_send_params)
67
+ logger.info(f"Task created/updated with history length: {len(task.history) if task.history else 0}")
67
68
  except Exception as e:
68
69
  logger.error(f"Error creating task: {str(e)}")
69
70
  yield SendTaskStreamingResponse(
@@ -74,10 +75,27 @@ class AgentTaskManager(InMemoryTaskManager):
74
75
 
75
76
  agent = self._create_agent(agent_name)
76
77
 
78
+ # Get the history from the task
79
+ history = task.history if task and task.history else []
80
+ logger.info(f"Using history with length {len(history)} for request")
81
+
82
+ # Log the history for debugging
83
+ logger.info(f"Conversation history for task {task_send_params.id}:")
84
+ for idx, msg in enumerate(history):
85
+ # Convert Message object to dict if needed
86
+ msg_dict = msg.dict() if hasattr(msg, "dict") else msg
87
+ role = msg_dict.get("role", "unknown")
88
+ text = ""
89
+ for part in msg_dict.get("parts", []):
90
+ if part.get("type") == "text":
91
+ text = part.get("text", "")
92
+ break
93
+ logger.info(f"Message {idx + 1} ({role}): {text[:100]}...")
94
+
77
95
  if not streaming:
78
96
  # If streaming is disabled, use invoke and return a single response
79
97
  try:
80
- result = agent.invoke(query, task_send_params.sessionId)
98
+ result = agent.invoke(query, task_send_params.sessionId, history=history)
81
99
 
82
100
  # Use the parts from the agent response if available, or create them
83
101
  if "parts" in result:
@@ -134,7 +152,7 @@ class AgentTaskManager(InMemoryTaskManager):
134
152
  # Track the chunks we've seen to avoid duplicates
135
153
  seen_chunks = set()
136
154
 
137
- async for item in agent.stream(query, task_send_params.sessionId):
155
+ async for item in agent.stream(query, task_send_params.sessionId, history=history):
138
156
  # Ensure item has the required fields or provide defaults
139
157
  is_task_complete = item.get("is_task_complete", False)
140
158
 
@@ -356,13 +374,26 @@ class AgentTaskManager(InMemoryTaskManager):
356
374
  message = task_send_params.message
357
375
  message_dict = message.dict() if hasattr(message, "dict") else message
358
376
 
377
+ # Get history from request if available
378
+ history = []
379
+ if hasattr(task_send_params, "history") and task_send_params.history:
380
+ # Convert each history item to dict if needed and ensure proper role
381
+ for item in task_send_params.history:
382
+ item_dict = item.dict() if hasattr(item, "dict") else item
383
+ # Ensure the role is properly set
384
+ if "role" not in item_dict:
385
+ item_dict["role"] = "assistant" if "answer" in item_dict else "user"
386
+ history.append(item_dict)
387
+
388
+ # Add current message to history
389
+ history.append(message_dict)
390
+
359
391
  # Create a new task
360
392
  task = Task(
361
393
  id=task_send_params.id,
362
394
  sessionId=task_send_params.sessionId,
363
- messages=[message_dict],
364
395
  status=TaskStatus(state=TaskState.SUBMITTED),
365
- history=[message_dict],
396
+ history=history,
366
397
  artifacts=[],
367
398
  )
368
399
  self.tasks[task_send_params.id] = task
@@ -372,6 +403,22 @@ class AgentTaskManager(InMemoryTaskManager):
372
403
  message_dict = message.dict() if hasattr(message, "dict") else message
373
404
 
374
405
  # Update the existing task
406
+ if task.history is None:
407
+ task.history = []
408
+
409
+ # If we have new history from the request, use it
410
+ if hasattr(task_send_params, "history") and task_send_params.history:
411
+ # Convert each history item to dict if needed and ensure proper role
412
+ history = []
413
+ for item in task_send_params.history:
414
+ item_dict = item.dict() if hasattr(item, "dict") else item
415
+ # Ensure the role is properly set
416
+ if "role" not in item_dict:
417
+ item_dict["role"] = "assistant" if "answer" in item_dict else "user"
418
+ history.append(item_dict)
419
+ task.history = history
420
+
421
+ # Add current message to history
375
422
  task.history.append(message_dict)
376
423
  return task
377
424
 
@@ -459,6 +506,17 @@ class AgentTaskManager(InMemoryTaskManager):
459
506
  self.tasks[task_id] = task
460
507
 
461
508
  task.status = status
509
+
510
+ # Store assistant's response in history if we have a message
511
+ if status.message and status.message.role == "agent":
512
+ if task.history is None:
513
+ task.history = []
514
+ # Convert message to dict if needed
515
+ message_dict = status.message.dict() if hasattr(status.message, "dict") else status.message
516
+ # Ensure role is set to assistant
517
+ message_dict["role"] = "assistant"
518
+ task.history.append(message_dict)
519
+
462
520
  if artifacts is not None:
463
521
  for artifact in artifacts:
464
522
  if artifact.append and len(task.artifacts) > 0:
@@ -505,12 +563,16 @@ class AgentTaskManager(InMemoryTaskManager):
505
563
  agent = self._create_agent(agent_name)
506
564
 
507
565
  try:
566
+ # Get the history from the task
567
+ task = self.tasks.get(task_send_params.id)
568
+ history = task.history if task and task.history else []
569
+
508
570
  # Always use streaming internally, but handle the response differently based on the streaming parameter
509
571
  all_parts = []
510
572
  final_metadata = {}
511
573
 
512
574
  # Create a streaming generator
513
- stream_gen = agent.stream(query, task_send_params.sessionId)
575
+ stream_gen = agent.stream(query, task_send_params.sessionId, history=history)
514
576
 
515
577
  if streaming:
516
578
  # For streaming mode, we'll use the streaming endpoint instead
@@ -26,18 +26,17 @@ def to_json(obj):
26
26
  def get_project_name(query: ASTNode = None):
27
27
  project_name = None
28
28
  if (
29
- isinstance(query, Select)
30
- and type(query.where) is BinaryOperation
31
- and query.where.op == '='
32
- and query.where.args[0].parts == ['project']
33
- and isinstance(query.where.args[1], Constant)
29
+ isinstance(query, Select)
30
+ and type(query.where) is BinaryOperation
31
+ and query.where.op == "="
32
+ and query.where.args[0].parts == ["project"]
33
+ and isinstance(query.where.args[1], Constant)
34
34
  ):
35
35
  project_name = query.where.args[1].value
36
36
  return project_name
37
37
 
38
38
 
39
39
  class MdbTable(Table):
40
-
41
40
  visible: bool = True
42
41
 
43
42
 
@@ -75,27 +74,29 @@ class ModelsTable(MdbTable):
75
74
  table_name = row["name"]
76
75
  table_meta = row["metadata"]
77
76
 
78
- data.append([
79
- table_name,
80
- table_meta["engine"],
81
- project_name,
82
- table_meta["active"],
83
- table_meta["version"],
84
- table_meta["status"],
85
- table_meta["accuracy"],
86
- table_meta["predict"],
87
- table_meta["update_status"],
88
- table_meta["mindsdb_version"],
89
- table_meta["error"],
90
- table_meta["select_data_query"],
91
- to_json(table_meta["training_options"]),
92
- table_meta["current_training_phase"],
93
- table_meta["total_training_phases"],
94
- table_meta["training_phase_name"],
95
- table_meta["label"],
96
- row["created_at"],
97
- table_meta["training_time"],
98
- ])
77
+ data.append(
78
+ [
79
+ table_name,
80
+ table_meta["engine"],
81
+ project_name,
82
+ table_meta["active"],
83
+ table_meta["version"],
84
+ table_meta["status"],
85
+ table_meta["accuracy"],
86
+ table_meta["predict"],
87
+ table_meta["update_status"],
88
+ table_meta["mindsdb_version"],
89
+ table_meta["error"],
90
+ table_meta["select_data_query"],
91
+ to_json(table_meta["training_options"]),
92
+ table_meta["current_training_phase"],
93
+ table_meta["total_training_phases"],
94
+ table_meta["training_phase_name"],
95
+ table_meta["label"],
96
+ row["created_at"],
97
+ table_meta["training_time"],
98
+ ]
99
+ )
99
100
  # TODO optimise here
100
101
  # if target_table is not None and target_table != project_name:
101
102
  # continue
@@ -110,12 +111,8 @@ class DatabasesTable(MdbTable):
110
111
 
111
112
  @classmethod
112
113
  def get_data(cls, session, inf_schema, **kwargs):
113
-
114
114
  project = inf_schema.database_controller.get_list(with_secrets=session.show_secrets)
115
- data = [
116
- [x["name"], x["type"], x["engine"], to_json(x.get("connection_data"))]
117
- for x in project
118
- ]
115
+ data = [[x["name"], x["type"], x["engine"], to_json(x.get("connection_data"))] for x in project]
119
116
 
120
117
  df = pd.DataFrame(data, columns=cls.columns)
121
118
  return df
@@ -123,17 +120,12 @@ class DatabasesTable(MdbTable):
123
120
 
124
121
  class MLEnginesTable(MdbTable):
125
122
  name = "ML_ENGINES"
126
- columns = [
127
- "NAME", "HANDLER", "CONNECTION_DATA"
128
- ]
123
+ columns = ["NAME", "HANDLER", "CONNECTION_DATA"]
129
124
 
130
125
  @classmethod
131
126
  def get_data(cls, session, inf_schema, **kwargs):
132
-
133
127
  integrations = inf_schema.integration_controller.get_all(show_secrets=session.show_secrets)
134
- ml_integrations = {
135
- key: val for key, val in integrations.items() if val["type"] == "ml"
136
- }
128
+ ml_integrations = {key: val for key, val in integrations.items() if val["type"] == "ml"}
137
129
 
138
130
  data = []
139
131
  for _key, val in ml_integrations.items():
@@ -158,7 +150,6 @@ class HandlersTable(MdbTable):
158
150
 
159
151
  @classmethod
160
152
  def get_data(cls, inf_schema, **kwargs):
161
-
162
153
  handlers = inf_schema.integration_controller.get_handlers_import_status()
163
154
 
164
155
  data = []
@@ -272,7 +263,7 @@ class TriggersTable(MdbTable):
272
263
  data = triggers_controller.get_list(project_name)
273
264
 
274
265
  columns = cls.mindsdb_columns
275
- if inf_schema.session.api_type == 'sql':
266
+ if inf_schema.session.api_type == "sql":
276
267
  columns = columns + cls.columns
277
268
  columns_lower = [col.lower() for col in columns]
278
269
 
@@ -283,7 +274,7 @@ class TriggersTable(MdbTable):
283
274
 
284
275
 
285
276
  class ChatbotsTable(MdbTable):
286
- name = 'CHATBOTS'
277
+ name = "CHATBOTS"
287
278
  columns = [
288
279
  "NAME",
289
280
  "PROJECT",
@@ -319,62 +310,78 @@ class ChatbotsTable(MdbTable):
319
310
  # to list of lists
320
311
  data = []
321
312
  for row in chatbot_data:
322
- row['params'] = to_json(row['params'])
313
+ row["params"] = to_json(row["params"])
323
314
  data.append([row[k] for k in columns_lower])
324
315
 
325
316
  return pd.DataFrame(data, columns=columns)
326
317
 
327
318
 
328
319
  class KBTable(MdbTable):
329
- name = 'KNOWLEDGE_BASES'
330
- columns = ["NAME", "PROJECT", "MODEL", "STORAGE", "PARAMS",
331
- "INSERT_STARTED_AT", "INSERT_FINISHED_AT", "PROCESSED_ROWS", "ERROR", "QUERY_ID"]
320
+ name = "KNOWLEDGE_BASES"
321
+ columns = [
322
+ "NAME",
323
+ "PROJECT",
324
+ "EMBEDDING_MODEL",
325
+ "RERANKING_MODEL",
326
+ "STORAGE",
327
+ "METADATA_COLUMNS",
328
+ "CONTENT_COLUMNS",
329
+ "ID_COLUMN",
330
+ "PARAMS",
331
+ "INSERT_STARTED_AT",
332
+ "INSERT_FINISHED_AT",
333
+ "PROCESSED_ROWS",
334
+ "ERROR",
335
+ "QUERY_ID",
336
+ ]
332
337
 
333
338
  @classmethod
334
339
  def get_data(cls, query: ASTNode = None, inf_schema=None, **kwargs):
335
340
  project_name = get_project_name(query)
336
341
 
337
342
  from mindsdb.interfaces.knowledge_base.controller import KnowledgeBaseController
343
+
338
344
  controller = KnowledgeBaseController(inf_schema.session)
339
345
  kb_list = controller.list(project_name)
340
346
 
341
347
  # shouldn't be a lot of queries, we can fetch them all
342
- queries_data = {
343
- item['id']: item
344
- for item in query_context_controller.list_queries()
345
- }
348
+ queries_data = {item["id"]: item for item in query_context_controller.list_queries()}
346
349
 
347
350
  data = []
348
351
 
349
352
  for kb in kb_list:
350
- vector_database_name = kb['vector_database'] or ''
351
-
352
353
  query_item = {}
353
- query_id = kb['query_id']
354
+ query_id = kb["query_id"]
354
355
  if query_id is not None:
355
356
  if query_id in queries_data:
356
357
  query_item = queries_data.get(query_id)
357
358
  else:
358
359
  query_id = None
359
360
 
360
- data.append((
361
- kb['name'],
362
- kb['project_name'],
363
- kb['embedding_model'],
364
- vector_database_name + '.' + kb['vector_database_table'],
365
- to_json(kb['params']),
366
- query_item.get('started_at'),
367
- query_item.get('finished_at'),
368
- query_item.get('processed_rows'),
369
- query_item.get('error'),
370
- query_id,
371
- ))
361
+ data.append(
362
+ (
363
+ kb["name"],
364
+ kb["project_name"],
365
+ to_json(kb["embedding_model"]),
366
+ to_json(kb["reranking_model"]),
367
+ kb["vector_database"] + "." + kb["vector_database_table"],
368
+ to_json(kb["metadata_columns"]),
369
+ to_json(kb["content_columns"]),
370
+ kb["id_column"],
371
+ to_json(kb["params"]),
372
+ query_item.get("started_at"),
373
+ query_item.get("finished_at"),
374
+ query_item.get("processed_rows"),
375
+ query_item.get("error"),
376
+ query_id,
377
+ )
378
+ )
372
379
 
373
380
  return pd.DataFrame(data, columns=cls.columns)
374
381
 
375
382
 
376
383
  class SkillsTable(MdbTable):
377
- name = 'SKILLS'
384
+ name = "SKILLS"
378
385
  columns = ["NAME", "PROJECT", "TYPE", "PARAMS"]
379
386
 
380
387
  @classmethod
@@ -394,14 +401,8 @@ class SkillsTable(MdbTable):
394
401
 
395
402
 
396
403
  class AgentsTable(MdbTable):
397
- name = 'AGENTS'
398
- columns = [
399
- "NAME",
400
- "PROJECT",
401
- "MODEL_NAME",
402
- "SKILLS",
403
- "PARAMS"
404
- ]
404
+ name = "AGENTS"
405
+ columns = ["NAME", "PROJECT", "MODEL_NAME", "SKILLS", "PARAMS"]
405
406
 
406
407
  @classmethod
407
408
  def get_data(cls, query: ASTNode = None, inf_schema=None, **kwargs):
@@ -411,10 +412,7 @@ class AgentsTable(MdbTable):
411
412
  all_agents = agents_controller.get_agents(project_name)
412
413
 
413
414
  project_controller = ProjectController()
414
- project_names = {
415
- i.id: i.name
416
- for i in project_controller.get_list()
417
- }
415
+ project_names = {i.id: i.name for i in project_controller.get_list()}
418
416
 
419
417
  # NAME, PROJECT, MODEL, SKILLS, PARAMS
420
418
  data = [
@@ -423,7 +421,7 @@ class AgentsTable(MdbTable):
423
421
  project_names[a.project_id],
424
422
  a.model_name,
425
423
  [rel.skill.name for rel in a.skills_relationships],
426
- to_json(a.params)
424
+ to_json(a.params),
427
425
  )
428
426
  for a in all_agents
429
427
  ]
@@ -431,12 +429,11 @@ class AgentsTable(MdbTable):
431
429
 
432
430
 
433
431
  class ViewsTable(MdbTable):
434
- name = 'VIEWS'
432
+ name = "VIEWS"
435
433
  columns = ["NAME", "PROJECT", "QUERY"]
436
434
 
437
435
  @classmethod
438
436
  def get_data(cls, query: ASTNode = None, **kwargs):
439
-
440
437
  project_name = get_project_name(query)
441
438
 
442
439
  data = ViewController().list(project_name)
@@ -450,9 +447,19 @@ class ViewsTable(MdbTable):
450
447
 
451
448
 
452
449
  class QueriesTable(MdbTable):
453
- name = 'QUERIES'
454
- columns = ["ID", "STARTED_AT", "FINISHED_AT", "PROCESSED_ROWS", "ERROR", "SQL", "DATABASE",
455
- "PARAMETERS", "CONTEXT", "UPDATED_AT"]
450
+ name = "QUERIES"
451
+ columns = [
452
+ "ID",
453
+ "STARTED_AT",
454
+ "FINISHED_AT",
455
+ "PROCESSED_ROWS",
456
+ "ERROR",
457
+ "SQL",
458
+ "DATABASE",
459
+ "PARAMETERS",
460
+ "CONTEXT",
461
+ "UPDATED_AT",
462
+ ]
456
463
 
457
464
  @classmethod
458
465
  def get_data(cls, **kwargs):