MindsDB 25.8.3.0__py3-none-any.whl → 25.9.1.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 (94) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +2 -44
  3. mindsdb/api/a2a/__init__.py +52 -0
  4. mindsdb/api/a2a/agent.py +11 -12
  5. mindsdb/api/a2a/common/server/server.py +17 -36
  6. mindsdb/api/a2a/common/server/task_manager.py +14 -28
  7. mindsdb/api/a2a/task_manager.py +20 -21
  8. mindsdb/api/a2a/utils.py +1 -1
  9. mindsdb/api/common/middleware.py +106 -0
  10. mindsdb/api/http/initialize.py +13 -15
  11. mindsdb/api/http/namespaces/auth.py +6 -14
  12. mindsdb/api/http/namespaces/config.py +0 -2
  13. mindsdb/api/http/namespaces/default.py +74 -106
  14. mindsdb/api/http/start.py +25 -44
  15. mindsdb/api/litellm/start.py +11 -10
  16. mindsdb/api/mcp/__init__.py +165 -0
  17. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +33 -64
  18. mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +86 -85
  19. mindsdb/integrations/handlers/crate_handler/crate_handler.py +3 -7
  20. mindsdb/integrations/handlers/derby_handler/derby_handler.py +32 -34
  21. mindsdb/integrations/handlers/documentdb_handler/requirements.txt +1 -0
  22. mindsdb/integrations/handlers/dummy_data_handler/dummy_data_handler.py +12 -13
  23. mindsdb/integrations/handlers/google_books_handler/google_books_handler.py +45 -44
  24. mindsdb/integrations/handlers/google_calendar_handler/google_calendar_handler.py +101 -95
  25. mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_handler.py +129 -129
  26. mindsdb/integrations/handlers/google_fit_handler/google_fit_handler.py +59 -43
  27. mindsdb/integrations/handlers/google_search_handler/google_search_handler.py +38 -39
  28. mindsdb/integrations/handlers/informix_handler/informix_handler.py +5 -18
  29. mindsdb/integrations/handlers/maxdb_handler/maxdb_handler.py +22 -28
  30. mindsdb/integrations/handlers/monetdb_handler/monetdb_handler.py +3 -7
  31. mindsdb/integrations/handlers/mongodb_handler/mongodb_handler.py +53 -67
  32. mindsdb/integrations/handlers/mongodb_handler/requirements.txt +1 -0
  33. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_ast.py +43 -68
  34. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_parser.py +17 -25
  35. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_query.py +10 -16
  36. mindsdb/integrations/handlers/mongodb_handler/utils/mongodb_render.py +43 -69
  37. mindsdb/integrations/libs/base.py +1 -1
  38. mindsdb/interfaces/agents/constants.py +1 -0
  39. mindsdb/interfaces/knowledge_base/controller.py +3 -1
  40. mindsdb/utilities/config.py +3 -155
  41. mindsdb/utilities/log.py +0 -25
  42. mindsdb/utilities/starters.py +0 -39
  43. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/METADATA +263 -261
  44. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/RECORD +47 -91
  45. mindsdb/api/a2a/__main__.py +0 -144
  46. mindsdb/api/a2a/run_a2a.py +0 -86
  47. mindsdb/api/common/check_auth.py +0 -42
  48. mindsdb/api/http/gunicorn_wrapper.py +0 -17
  49. mindsdb/api/mcp/start.py +0 -205
  50. mindsdb/api/mongo/__init__.py +0 -0
  51. mindsdb/api/mongo/classes/__init__.py +0 -5
  52. mindsdb/api/mongo/classes/query_sql.py +0 -19
  53. mindsdb/api/mongo/classes/responder.py +0 -45
  54. mindsdb/api/mongo/classes/responder_collection.py +0 -34
  55. mindsdb/api/mongo/classes/scram.py +0 -86
  56. mindsdb/api/mongo/classes/session.py +0 -23
  57. mindsdb/api/mongo/functions/__init__.py +0 -19
  58. mindsdb/api/mongo/responders/__init__.py +0 -73
  59. mindsdb/api/mongo/responders/add_shard.py +0 -13
  60. mindsdb/api/mongo/responders/aggregate.py +0 -90
  61. mindsdb/api/mongo/responders/buildinfo.py +0 -17
  62. mindsdb/api/mongo/responders/coll_stats.py +0 -63
  63. mindsdb/api/mongo/responders/company_id.py +0 -25
  64. mindsdb/api/mongo/responders/connection_status.py +0 -22
  65. mindsdb/api/mongo/responders/count.py +0 -21
  66. mindsdb/api/mongo/responders/db_stats.py +0 -32
  67. mindsdb/api/mongo/responders/delete.py +0 -105
  68. mindsdb/api/mongo/responders/describe.py +0 -23
  69. mindsdb/api/mongo/responders/end_sessions.py +0 -13
  70. mindsdb/api/mongo/responders/find.py +0 -175
  71. mindsdb/api/mongo/responders/get_cmd_line_opts.py +0 -18
  72. mindsdb/api/mongo/responders/get_free_monitoring_status.py +0 -14
  73. mindsdb/api/mongo/responders/get_parameter.py +0 -23
  74. mindsdb/api/mongo/responders/getlog.py +0 -14
  75. mindsdb/api/mongo/responders/host_info.py +0 -28
  76. mindsdb/api/mongo/responders/insert.py +0 -270
  77. mindsdb/api/mongo/responders/is_master.py +0 -20
  78. mindsdb/api/mongo/responders/is_master_lower.py +0 -13
  79. mindsdb/api/mongo/responders/list_collections.py +0 -55
  80. mindsdb/api/mongo/responders/list_databases.py +0 -37
  81. mindsdb/api/mongo/responders/list_indexes.py +0 -22
  82. mindsdb/api/mongo/responders/ping.py +0 -13
  83. mindsdb/api/mongo/responders/recv_chunk_start.py +0 -13
  84. mindsdb/api/mongo/responders/replsetgetstatus.py +0 -13
  85. mindsdb/api/mongo/responders/sasl_continue.py +0 -34
  86. mindsdb/api/mongo/responders/sasl_start.py +0 -33
  87. mindsdb/api/mongo/responders/update_range_deletions.py +0 -12
  88. mindsdb/api/mongo/responders/whatsmyuri.py +0 -18
  89. mindsdb/api/mongo/server.py +0 -388
  90. mindsdb/api/mongo/start.py +0 -15
  91. mindsdb/api/mongo/utilities/__init__.py +0 -0
  92. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/WHEEL +0 -0
  93. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.0.dist-info}/licenses/LICENSE +0 -0
  94. {mindsdb-25.8.3.0.dist-info → mindsdb-25.9.1.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.8.3.0"
3
+ __version__ = "25.9.1.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/__main__.py CHANGED
@@ -8,7 +8,6 @@ import atexit
8
8
  import signal
9
9
  import psutil
10
10
  import asyncio
11
- import secrets
12
11
  import traceback
13
12
  import threading
14
13
  from enum import Enum
@@ -28,14 +27,11 @@ from mindsdb.utilities.config import config
28
27
  from mindsdb.utilities.starters import (
29
28
  start_http,
30
29
  start_mysql,
31
- start_mongo,
32
30
  start_postgres,
33
31
  start_ml_task_queue,
34
32
  start_scheduler,
35
33
  start_tasks,
36
- start_mcp,
37
34
  start_litellm,
38
- start_a2a,
39
35
  )
40
36
  from mindsdb.utilities.ps import is_pid_listen_port, get_child_pids
41
37
  import mindsdb.interfaces.storage.db as db
@@ -61,18 +57,15 @@ _stop_event = threading.Event()
61
57
  class TrunkProcessEnum(Enum):
62
58
  HTTP = "http"
63
59
  MYSQL = "mysql"
64
- MONGODB = "mongodb"
65
60
  POSTGRES = "postgres"
66
61
  JOBS = "jobs"
67
62
  TASKS = "tasks"
68
63
  ML_TASK_QUEUE = "ml_task_queue"
69
- MCP = "mcp"
70
64
  LITELLM = "litellm"
71
- A2A = "a2a"
72
65
 
73
66
  @classmethod
74
67
  def _missing_(cls, value):
75
- print(f'"{value}" is not a valid name of subprocess')
68
+ logger.error(f'"{value}" is not a valid name of subprocess')
76
69
  sys.exit(1)
77
70
 
78
71
 
@@ -348,9 +341,6 @@ if __name__ == "__main__":
348
341
  config.raise_warnings(logger=logger)
349
342
  os.environ["MINDSDB_RUNTIME"] = "1"
350
343
 
351
- if os.environ.get("FLASK_SECRET_KEY") is None:
352
- os.environ["FLASK_SECRET_KEY"] = secrets.token_hex(32)
353
-
354
344
  if os.environ.get("ARROW_DEFAULT_MEMORY_POOL") is None:
355
345
  try:
356
346
  """It seems like snowflake handler have memory issue that related to pyarrow. Memory usage keep growing with
@@ -385,7 +375,7 @@ if __name__ == "__main__":
385
375
  apis = os.getenv("MINDSDB_APIS") or config.cmd_args.api
386
376
 
387
377
  if apis is None: # If "--api" option is not specified, start the default APIs
388
- api_arr = [TrunkProcessEnum.HTTP, TrunkProcessEnum.MYSQL, TrunkProcessEnum.MCP, TrunkProcessEnum.A2A]
378
+ api_arr = [TrunkProcessEnum.HTTP, TrunkProcessEnum.MYSQL]
389
379
  elif apis == "": # If "--api=" (blank) is specified, don't start any APIs
390
380
  api_arr = []
391
381
  else: # The user has provided a list of APIs to start
@@ -421,9 +411,7 @@ if __name__ == "__main__":
421
411
  # Get config values for APIs
422
412
  http_api_config = config.get("api", {}).get("http", {})
423
413
  mysql_api_config = config.get("api", {}).get("mysql", {})
424
- mcp_api_config = config.get("api", {}).get("mcp", {})
425
414
  litellm_api_config = config.get("api", {}).get("litellm", {})
426
- a2a_api_config = config.get("api", {}).get("a2a", {})
427
415
  trunc_processes_struct = {
428
416
  TrunkProcessEnum.HTTP: TrunkProcessData(
429
417
  name=TrunkProcessEnum.HTTP.value,
@@ -447,12 +435,6 @@ if __name__ == "__main__":
447
435
  "max_restart_interval_seconds", TrunkProcessData.max_restart_interval_seconds
448
436
  ),
449
437
  ),
450
- TrunkProcessEnum.MONGODB: TrunkProcessData(
451
- name=TrunkProcessEnum.MONGODB.value,
452
- entrypoint=start_mongo,
453
- port=config["api"]["mongodb"]["port"],
454
- args=(config.cmd_args.verbose,),
455
- ),
456
438
  TrunkProcessEnum.POSTGRES: TrunkProcessData(
457
439
  name=TrunkProcessEnum.POSTGRES.value,
458
440
  entrypoint=start_postgres,
@@ -468,18 +450,6 @@ if __name__ == "__main__":
468
450
  TrunkProcessEnum.ML_TASK_QUEUE: TrunkProcessData(
469
451
  name=TrunkProcessEnum.ML_TASK_QUEUE.value, entrypoint=start_ml_task_queue, args=(config.cmd_args.verbose,)
470
452
  ),
471
- TrunkProcessEnum.MCP: TrunkProcessData(
472
- name=TrunkProcessEnum.MCP.value,
473
- entrypoint=start_mcp,
474
- port=mcp_api_config.get("port", 47337),
475
- args=(config.cmd_args.verbose,),
476
- need_to_run=mcp_api_config.get("need_to_run", False),
477
- restart_on_failure=mcp_api_config.get("restart_on_failure", False),
478
- max_restart_count=mcp_api_config.get("max_restart_count", TrunkProcessData.max_restart_count),
479
- max_restart_interval_seconds=mcp_api_config.get(
480
- "max_restart_interval_seconds", TrunkProcessData.max_restart_interval_seconds
481
- ),
482
- ),
483
453
  TrunkProcessEnum.LITELLM: TrunkProcessData(
484
454
  name=TrunkProcessEnum.LITELLM.value,
485
455
  entrypoint=start_litellm,
@@ -491,18 +461,6 @@ if __name__ == "__main__":
491
461
  "max_restart_interval_seconds", TrunkProcessData.max_restart_interval_seconds
492
462
  ),
493
463
  ),
494
- TrunkProcessEnum.A2A: TrunkProcessData(
495
- name=TrunkProcessEnum.A2A.value,
496
- entrypoint=start_a2a,
497
- port=a2a_api_config.get("port", 8001),
498
- args=(config.cmd_args.verbose,),
499
- need_to_run=a2a_api_config.get("enabled", False),
500
- restart_on_failure=a2a_api_config.get("restart_on_failure", True),
501
- max_restart_count=a2a_api_config.get("max_restart_count", TrunkProcessData.max_restart_count),
502
- max_restart_interval_seconds=a2a_api_config.get(
503
- "max_restart_interval_seconds", TrunkProcessData.max_restart_interval_seconds
504
- ),
505
- ),
506
464
  }
507
465
 
508
466
  for api_enum in api_arr:
@@ -0,0 +1,52 @@
1
+ # A2A specific imports
2
+ from mindsdb.api.a2a.common.types import (
3
+ AgentCard,
4
+ AgentCapabilities,
5
+ AgentSkill,
6
+ )
7
+ from mindsdb.api.a2a.common.server.server import A2AServer
8
+ from mindsdb.api.a2a.task_manager import AgentTaskManager
9
+ from mindsdb.api.a2a.agent import MindsDBAgent
10
+ from mindsdb.utilities.config import config
11
+
12
+
13
+ def get_a2a_app(
14
+ project_name: str = "mindsdb",
15
+ ):
16
+ mindsdb_port = config.get("api", {}).get("http", {}).get("port", 47334)
17
+
18
+ # Prepare A2A artefacts (agent card & task-manager)
19
+ capabilities = AgentCapabilities(streaming=True)
20
+ skill = AgentSkill(
21
+ id="mindsdb_query",
22
+ name="MindsDB Query",
23
+ description="Executes natural-language queries via MindsDB agents.",
24
+ tags=["database", "mindsdb", "query", "analytics"],
25
+ examples=[
26
+ "What trends exist in my sales data?",
27
+ "Generate insights from the support tickets dataset.",
28
+ ],
29
+ inputModes=MindsDBAgent.SUPPORTED_CONTENT_TYPES,
30
+ outputModes=MindsDBAgent.SUPPORTED_CONTENT_TYPES,
31
+ )
32
+
33
+ agent_card = AgentCard(
34
+ name="MindsDB Agent Connector",
35
+ description=(f"A2A connector that proxies requests to MindsDB agents in project '{project_name}'."),
36
+ url=f"http://127.0.0.1:{mindsdb_port}",
37
+ version="1.0.0",
38
+ defaultInputModes=MindsDBAgent.SUPPORTED_CONTENT_TYPES,
39
+ defaultOutputModes=MindsDBAgent.SUPPORTED_CONTENT_TYPES,
40
+ capabilities=capabilities,
41
+ skills=[skill],
42
+ )
43
+
44
+ task_manager = AgentTaskManager(
45
+ project_name=project_name,
46
+ )
47
+
48
+ server = A2AServer(
49
+ agent_card=agent_card,
50
+ task_manager=task_manager,
51
+ )
52
+ return server.app
mindsdb/api/a2a/agent.py CHANGED
@@ -1,12 +1,13 @@
1
1
  import json
2
2
  from typing import Any, AsyncIterable, Dict, List
3
3
  import requests
4
- import logging
5
4
  import httpx
6
5
  from mindsdb.api.a2a.utils import to_serializable, convert_a2a_message_to_qa_format
7
6
  from mindsdb.api.a2a.constants import DEFAULT_STREAM_TIMEOUT
7
+ from mindsdb.utilities import log
8
+ from mindsdb.utilities.config import config
8
9
 
9
- logger = logging.getLogger(__name__)
10
+ logger = log.getLogger(__name__)
10
11
 
11
12
 
12
13
  class MindsDBAgent:
@@ -18,16 +19,15 @@ class MindsDBAgent:
18
19
  self,
19
20
  agent_name="my_agent",
20
21
  project_name="mindsdb",
21
- host="localhost",
22
- port=47334,
22
+ user_info: Dict[str, Any] = None,
23
23
  ):
24
24
  self.agent_name = agent_name
25
25
  self.project_name = project_name
26
- self.host = host
27
- self.port = port
28
- self.base_url = f"http://{host}:{port}"
26
+ port = config.get("api", {}).get("http", {}).get("port", 47334)
27
+ self.base_url = f"http://localhost:{port}"
29
28
  self.agent_url = f"{self.base_url}/api/projects/{project_name}/agents/{agent_name}"
30
29
  self.sql_url = f"{self.base_url}/api/sql/query"
30
+ self.headers = {k: v for k, v in user_info.items() if v is not None} or {}
31
31
  logger.info(f"Initialized MindsDB agent connector to {self.base_url}")
32
32
 
33
33
  def invoke(self, query, session_id) -> Dict[str, Any]:
@@ -35,8 +35,8 @@ class MindsDBAgent:
35
35
  try:
36
36
  escaped_query = query.replace("'", "''")
37
37
  sql_query = f"SELECT * FROM {self.project_name}.{self.agent_name} WHERE question = '{escaped_query}'"
38
- logger.info(f"Sending SQL query to MindsDB: {sql_query[:100]}...")
39
- response = requests.post(self.sql_url, json={"query": sql_query})
38
+ logger.debug(f"Sending SQL query to MindsDB: {sql_query[:100]}...")
39
+ response = requests.post(self.sql_url, json={"query": sql_query}, headers=self.headers)
40
40
  response.raise_for_status()
41
41
  data = response.json()
42
42
  logger.debug(f"Received response from MindsDB: {json.dumps(data)[:200]}...")
@@ -89,8 +89,8 @@ class MindsDBAgent:
89
89
 
90
90
  async def streaming_invoke(self, messages, timeout=DEFAULT_STREAM_TIMEOUT):
91
91
  url = f"{self.base_url}/api/projects/{self.project_name}/agents/{self.agent_name}/completions/stream"
92
- logger.info(f"Sending streaming request to MindsDB agent: {self.agent_name}")
93
- async with httpx.AsyncClient(timeout=timeout) as client:
92
+ logger.debug(f"Sending streaming request to MindsDB agent: {self.agent_name}")
93
+ async with httpx.AsyncClient(timeout=timeout, headers=self.headers) as client:
94
94
  async with client.stream("POST", url, json={"messages": to_serializable(messages)}) as response:
95
95
  response.raise_for_status()
96
96
  async for line in response.aiter_lines():
@@ -116,7 +116,6 @@ class MindsDBAgent:
116
116
  ) -> AsyncIterable[Dict[str, Any]]:
117
117
  """Stream responses from the MindsDB agent (uses streaming API endpoint)."""
118
118
  try:
119
- logger.info(f"Using streaming API for query: {query[:100]}...")
120
119
  # Create A2A message structure with history and current query
121
120
  a2a_message = {"role": "user", "parts": [{"text": query}]}
122
121
  if history:
@@ -7,6 +7,7 @@ from starlette.middleware.cors import CORSMiddleware
7
7
  from starlette.responses import JSONResponse
8
8
  from sse_starlette.sse import EventSourceResponse
9
9
  from starlette.requests import Request
10
+ from starlette.routing import Route
10
11
  from ...common.types import (
11
12
  A2ARequest,
12
13
  JSONRPCResponse,
@@ -26,7 +27,6 @@ from pydantic import ValidationError
26
27
  from ...common.server.task_manager import TaskManager
27
28
 
28
29
  from mindsdb.utilities import log
29
- from mindsdb.utilities.log import get_uvicorn_logging_config, get_mindsdb_log_level
30
30
 
31
31
  logger = log.getLogger(__name__)
32
32
 
@@ -34,22 +34,18 @@ logger = log.getLogger(__name__)
34
34
  class A2AServer:
35
35
  def __init__(
36
36
  self,
37
- host="0.0.0.0",
38
- port=5000,
39
- endpoint="/",
40
37
  agent_card: AgentCard = None,
41
38
  task_manager: TaskManager = None,
42
39
  ):
43
- self.host = host
44
- self.port = port
45
- self.endpoint = endpoint
46
40
  self.task_manager = task_manager
47
41
  self.agent_card = agent_card
48
- self.app = Starlette()
49
- self.app.add_route(self.endpoint, self._process_request, methods=["POST"])
50
- self.app.add_route("/.well-known/agent.json", self._get_agent_card, methods=["GET"])
51
- # Add status endpoint
52
- self.app.add_route("/status", self._get_status, methods=["GET"])
42
+ self.app = Starlette(
43
+ routes=[
44
+ Route("/", self._process_request, methods=["POST"]),
45
+ Route("/.well-known/agent.json", self._get_agent_card, methods=["GET"]),
46
+ Route("/status", self._get_status, methods=["GET"]),
47
+ ]
48
+ )
53
49
  # TODO: Remove this when we have a proper CORS policy
54
50
  self.app.add_middleware(
55
51
  CORSMiddleware,
@@ -60,26 +56,6 @@ class A2AServer:
60
56
  )
61
57
  self.start_time = time.time()
62
58
 
63
- def start(self):
64
- if self.agent_card is None:
65
- raise ValueError("agent_card is not defined")
66
-
67
- if self.task_manager is None:
68
- raise ValueError("request_handler is not defined")
69
-
70
- import uvicorn
71
-
72
- # Configure uvicorn with optimized settings for streaming
73
- uvicorn.run(
74
- self.app,
75
- host=self.host,
76
- port=self.port,
77
- http="h11",
78
- timeout_keep_alive=65,
79
- log_level=get_mindsdb_log_level(),
80
- log_config=get_uvicorn_logging_config("uvicorn_a2a"),
81
- )
82
-
83
59
  def _get_agent_card(self, request: Request) -> JSONResponse:
84
60
  return JSONResponse(self.agent_card.model_dump(exclude_none=True))
85
61
 
@@ -94,8 +70,6 @@ class A2AServer:
94
70
  "status": "ok",
95
71
  "service": "mindsdb-a2a",
96
72
  "uptime_seconds": round(uptime_seconds, 2),
97
- "host": self.host,
98
- "port": self.port,
99
73
  "agent_name": self.agent_card.name if self.agent_card else None,
100
74
  "version": self.agent_card.version if self.agent_card else "unknown",
101
75
  }
@@ -107,13 +81,20 @@ class A2AServer:
107
81
  body = await request.json()
108
82
  json_rpc_request = A2ARequest.validate_python(body)
109
83
 
84
+ user_info = {
85
+ "user-id": request.headers.get("user-id", None),
86
+ "company-id": request.headers.get("company-id", None),
87
+ "user-class": request.headers.get("user-class", None),
88
+ "authorization": request.headers.get("Authorization", None),
89
+ }
90
+
110
91
  if isinstance(json_rpc_request, GetTaskRequest):
111
92
  result = await self.task_manager.on_get_task(json_rpc_request)
112
93
  elif isinstance(json_rpc_request, SendTaskRequest):
113
- result = await self.task_manager.on_send_task(json_rpc_request)
94
+ result = await self.task_manager.on_send_task(json_rpc_request, user_info)
114
95
  elif isinstance(json_rpc_request, SendTaskStreamingRequest):
115
96
  # Don't await the async generator, just pass it to _create_response
116
- result = self.task_manager.on_send_task_subscribe(json_rpc_request)
97
+ result = self.task_manager.on_send_task_subscribe(json_rpc_request, user_info)
117
98
  elif isinstance(json_rpc_request, CancelTaskRequest):
118
99
  result = await self.task_manager.on_cancel_task(json_rpc_request)
119
100
  elif isinstance(json_rpc_request, SetTaskPushNotificationRequest):
@@ -1,5 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Union, AsyncIterable, List
2
+ from typing import Union, AsyncIterable, List, Dict
3
3
  from ...common.types import Task
4
4
  from ...common.types import (
5
5
  JSONRPCResponse,
@@ -31,10 +31,10 @@ from ...common.types import (
31
31
  InternalError,
32
32
  )
33
33
  from ...common.server.utils import new_not_implemented_error
34
+ from mindsdb.utilities import log
34
35
  import asyncio
35
- import logging
36
36
 
37
- logger = logging.getLogger(__name__)
37
+ logger = log.getLogger(__name__)
38
38
 
39
39
 
40
40
  class TaskManager(ABC):
@@ -47,12 +47,12 @@ class TaskManager(ABC):
47
47
  pass
48
48
 
49
49
  @abstractmethod
50
- async def on_send_task(self, request: SendTaskRequest) -> SendTaskResponse:
50
+ async def on_send_task(self, request: SendTaskRequest, user_info: Dict) -> SendTaskResponse:
51
51
  pass
52
52
 
53
53
  @abstractmethod
54
54
  async def on_send_task_subscribe(
55
- self, request: SendTaskStreamingRequest
55
+ self, request: SendTaskStreamingRequest, user_info: Dict
56
56
  ) -> Union[AsyncIterable[SendTaskStreamingResponse], JSONRPCResponse]:
57
57
  pass
58
58
 
@@ -92,9 +92,7 @@ class InMemoryTaskManager(TaskManager):
92
92
  if task is None:
93
93
  return GetTaskResponse(id=request.id, error=TaskNotFoundError())
94
94
 
95
- task_result = self.append_task_history(
96
- task, task_query_params.historyLength
97
- )
95
+ task_result = self.append_task_history(task, task_query_params.historyLength)
98
96
 
99
97
  return GetTaskResponse(id=request.id, result=task_result)
100
98
 
@@ -110,18 +108,16 @@ class InMemoryTaskManager(TaskManager):
110
108
  return CancelTaskResponse(id=request.id, error=TaskNotCancelableError())
111
109
 
112
110
  @abstractmethod
113
- async def on_send_task(self, request: SendTaskRequest) -> SendTaskResponse:
111
+ async def on_send_task(self, request: SendTaskRequest, user_info: Dict) -> SendTaskResponse:
114
112
  pass
115
113
 
116
114
  @abstractmethod
117
115
  async def on_send_task_subscribe(
118
- self, request: SendTaskStreamingRequest
116
+ self, request: SendTaskStreamingRequest, user_info: Dict
119
117
  ) -> Union[AsyncIterable[SendTaskStreamingResponse], JSONRPCResponse]:
120
118
  pass
121
119
 
122
- async def set_push_notification_info(
123
- self, task_id: str, notification_config: PushNotificationConfig
124
- ):
120
+ async def set_push_notification_info(self, task_id: str, notification_config: PushNotificationConfig):
125
121
  async with self.lock:
126
122
  task = self.tasks.get(task_id)
127
123
  if task is None:
@@ -160,14 +156,10 @@ class InMemoryTaskManager(TaskManager):
160
156
  logger.error(f"Error while setting push notification info: {e}")
161
157
  return JSONRPCResponse(
162
158
  id=request.id,
163
- error=InternalError(
164
- message="An error occurred while setting push notification info"
165
- ),
159
+ error=InternalError(message="An error occurred while setting push notification info"),
166
160
  )
167
161
 
168
- return SetTaskPushNotificationResponse(
169
- id=request.id, result=task_notification_params
170
- )
162
+ return SetTaskPushNotificationResponse(id=request.id, result=task_notification_params)
171
163
 
172
164
  async def on_get_task_push_notification(
173
165
  self, request: GetTaskPushNotificationRequest
@@ -181,16 +173,12 @@ class InMemoryTaskManager(TaskManager):
181
173
  logger.error(f"Error while getting push notification info: {e}")
182
174
  return GetTaskPushNotificationResponse(
183
175
  id=request.id,
184
- error=InternalError(
185
- message="An error occurred while getting push notification info"
186
- ),
176
+ error=InternalError(message="An error occurred while getting push notification info"),
187
177
  )
188
178
 
189
179
  return GetTaskPushNotificationResponse(
190
180
  id=request.id,
191
- result=TaskPushNotificationConfig(
192
- id=task_params.id, pushNotificationConfig=notification_info
193
- ),
181
+ result=TaskPushNotificationConfig(id=task_params.id, pushNotificationConfig=notification_info),
194
182
  )
195
183
 
196
184
  async def upsert_task(self, task_send_params: TaskSendParams) -> Task:
@@ -216,9 +204,7 @@ class InMemoryTaskManager(TaskManager):
216
204
  ) -> Union[AsyncIterable[SendTaskStreamingResponse], JSONRPCResponse]:
217
205
  return new_not_implemented_error(request.id)
218
206
 
219
- async def update_store(
220
- self, task_id: str, status: TaskStatus, artifacts: list[Artifact]
221
- ) -> Task:
207
+ async def update_store(self, task_id: str, status: TaskStatus, artifacts: list[Artifact]) -> Task:
222
208
  async with self.lock:
223
209
  try:
224
210
  task = self.tasks[task_id]
@@ -1,4 +1,4 @@
1
- from typing import AsyncIterable
1
+ from typing import AsyncIterable, Dict
2
2
  from mindsdb.api.a2a.common.types import (
3
3
  SendTaskRequest,
4
4
  TaskSendParams,
@@ -24,6 +24,7 @@ from typing import Union
24
24
  import logging
25
25
  import asyncio
26
26
  import time
27
+ import traceback
27
28
 
28
29
  logger = logging.getLogger(__name__)
29
30
 
@@ -46,19 +47,15 @@ class AgentTaskManager(InMemoryTaskManager):
46
47
  def __init__(
47
48
  self,
48
49
  project_name: str,
49
- mindsdb_host: str,
50
- mindsdb_port: int,
51
50
  agent_name: str = None,
52
51
  ):
53
52
  super().__init__()
54
53
  self.project_name = project_name
55
- self.mindsdb_host = mindsdb_host
56
- self.mindsdb_port = mindsdb_port
57
54
  self.agent_name = agent_name
58
55
  self.tasks = {} # Task storage
59
56
  self.lock = asyncio.Lock() # Lock for task operations
60
57
 
61
- def _create_agent(self, agent_name: str = None) -> MindsDBAgent:
58
+ def _create_agent(self, user_info: Dict, agent_name: str = None) -> MindsDBAgent:
62
59
  """Create a new MindsDBAgent instance for the given agent name."""
63
60
  if not agent_name:
64
61
  raise ValueError("Agent name is required but was not provided in the request")
@@ -66,11 +63,12 @@ class AgentTaskManager(InMemoryTaskManager):
66
63
  return MindsDBAgent(
67
64
  agent_name=agent_name,
68
65
  project_name=self.project_name,
69
- host=self.mindsdb_host,
70
- port=self.mindsdb_port,
66
+ user_info=user_info,
71
67
  )
72
68
 
73
- async def _stream_generator(self, request: SendTaskStreamingRequest) -> AsyncIterable[SendTaskStreamingResponse]:
69
+ async def _stream_generator(
70
+ self, request: SendTaskStreamingRequest, user_info: Dict
71
+ ) -> AsyncIterable[SendTaskStreamingResponse]:
74
72
  task_send_params: TaskSendParams = request.params
75
73
  query = self._get_user_query(task_send_params)
76
74
  params = self._get_task_params(task_send_params)
@@ -92,7 +90,7 @@ class AgentTaskManager(InMemoryTaskManager):
92
90
  yield error_result
93
91
  return # Early return from generator
94
92
 
95
- agent = self._create_agent(agent_name)
93
+ agent = self._create_agent(user_info, agent_name)
96
94
 
97
95
  # Get the history from the task object (where it was properly extracted and stored)
98
96
  history = task.history if task and task.history else []
@@ -168,16 +166,16 @@ class AgentTaskManager(InMemoryTaskManager):
168
166
 
169
167
  # If streaming is enabled (default), use the streaming implementation
170
168
  try:
171
- logger.debug(f"[TaskManager] Entering agent.stream() at {time.time()}")
169
+ logger.debug(f"Entering agent.stream() at {time.time()}")
172
170
  # Create A2A message structure and convert using centralized utility
173
171
  a2a_message = task_send_params.message.model_dump()
172
+ logger.debug(f"History: {history}")
174
173
  if history:
175
174
  a2a_message["history"] = [msg.model_dump() if hasattr(msg, "model_dump") else msg for msg in history]
176
175
 
177
176
  # Convert to Q&A format using centralized utility function
178
177
  all_messages = convert_a2a_message_to_qa_format(a2a_message)
179
178
 
180
- logger.debug(f"Sending {len(all_messages)} total messages to streaming agent")
181
179
  async for item in agent.streaming_invoke(all_messages, timeout=60):
182
180
  # Clean up: Remove verbose debug logs, keep only errors and essential info
183
181
  if isinstance(item, dict) and "artifact" in item and "parts" in item["artifact"]:
@@ -185,6 +183,7 @@ class AgentTaskManager(InMemoryTaskManager):
185
183
  yield to_serializable(item)
186
184
  except Exception as e:
187
185
  logger.error(f"An error occurred while streaming the response: {e}")
186
+ logger.error(traceback.format_exc())
188
187
  error_text = f"An error occurred while streaming the response: {str(e)}"
189
188
  # Ensure all parts are plain dicts
190
189
  parts = [{"type": "text", "text": error_text}]
@@ -310,27 +309,27 @@ class AgentTaskManager(InMemoryTaskManager):
310
309
 
311
310
  return None
312
311
 
313
- async def on_send_task(self, request: SendTaskRequest) -> SendTaskResponse:
312
+ async def on_send_task(self, request: SendTaskRequest, user_info: Dict) -> SendTaskResponse:
314
313
  error = self._validate_request(request)
315
314
  if error:
316
315
  return error
317
316
 
318
- return await self._invoke(request)
317
+ return await self._invoke(request, user_info=user_info)
319
318
 
320
319
  async def on_send_task_subscribe(
321
- self, request: SendTaskStreamingRequest
320
+ self, request: SendTaskStreamingRequest, user_info: Dict
322
321
  ) -> AsyncIterable[SendTaskStreamingResponse]:
323
322
  error = self._validate_request(request)
324
323
  if error:
325
- logger.info(f"[TaskManager] Yielding error at {time.time()} for invalid request: {error}")
324
+ logger.info(f"Yielding error at {time.time()} for invalid request: {error}")
326
325
  yield to_serializable(SendTaskStreamingResponse(id=request.id, error=to_serializable(error.error)))
327
326
  return
328
327
 
329
328
  # We can't await an async generator directly, so we need to use it as is
330
329
  try:
331
- logger.debug(f"[TaskManager] Entering streaming path at {time.time()}")
332
- async for response in self._stream_generator(request):
333
- logger.debug(f"[TaskManager] Yielding streaming response at {time.time()} with: {str(response)[:120]}")
330
+ logger.debug(f"Entering streaming path at {time.time()}")
331
+ async for response in self._stream_generator(request, user_info):
332
+ logger.debug(f"Yielding streaming response at {time.time()} with: {str(response)[:120]}")
334
333
  yield response
335
334
  except Exception as e:
336
335
  # If an error occurs, yield an error response
@@ -409,13 +408,13 @@ class AgentTaskManager(InMemoryTaskManager):
409
408
  "session_id": task_send_params.sessionId,
410
409
  }
411
410
 
412
- async def _invoke(self, request: SendTaskRequest) -> SendTaskResponse:
411
+ async def _invoke(self, request: SendTaskRequest, user_info: Dict) -> SendTaskResponse:
413
412
  task_send_params: TaskSendParams = request.params
414
413
  query = self._get_user_query(task_send_params)
415
414
  params = self._get_task_params(task_send_params)
416
415
  agent_name = params["agent_name"]
417
416
  streaming = params["streaming"]
418
- agent = self._create_agent(agent_name)
417
+ agent = self._create_agent(user_info, agent_name)
419
418
 
420
419
  try:
421
420
  # Get the history from the task
mindsdb/api/a2a/utils.py CHANGED
@@ -42,7 +42,7 @@ def convert_a2a_message_to_qa_format(a2a_message: Dict) -> List[Dict[str, str]]:
42
42
  converted_messages = []
43
43
 
44
44
  # Process conversation history first
45
- if "history" in a2a_message:
45
+ if "history" in a2a_message and a2a_message["history"] is not None:
46
46
  for hist_msg in a2a_message["history"]:
47
47
  if hist_msg.get("role") == "user":
48
48
  # Extract text from parts