MindsDB 25.8.2.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 (101) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/__main__.py +5 -45
  3. mindsdb/api/a2a/__init__.py +52 -0
  4. mindsdb/api/a2a/agent.py +17 -28
  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/common/types.py +3 -4
  8. mindsdb/api/a2a/task_manager.py +43 -55
  9. mindsdb/api/a2a/utils.py +63 -0
  10. mindsdb/api/common/middleware.py +106 -0
  11. mindsdb/api/http/initialize.py +13 -15
  12. mindsdb/api/http/namespaces/agents.py +6 -7
  13. mindsdb/api/http/namespaces/auth.py +6 -14
  14. mindsdb/api/http/namespaces/config.py +0 -2
  15. mindsdb/api/http/namespaces/default.py +74 -106
  16. mindsdb/api/http/start.py +25 -44
  17. mindsdb/api/litellm/start.py +11 -10
  18. mindsdb/api/mcp/__init__.py +165 -0
  19. mindsdb/api/mysql/mysql_proxy/mysql_proxy.py +33 -64
  20. mindsdb/api/postgres/postgres_proxy/postgres_proxy.py +86 -85
  21. mindsdb/integrations/handlers/crate_handler/crate_handler.py +3 -7
  22. mindsdb/integrations/handlers/derby_handler/derby_handler.py +32 -34
  23. mindsdb/integrations/handlers/documentdb_handler/requirements.txt +1 -0
  24. mindsdb/integrations/handlers/dummy_data_handler/dummy_data_handler.py +12 -13
  25. mindsdb/integrations/handlers/google_books_handler/google_books_handler.py +45 -44
  26. mindsdb/integrations/handlers/google_calendar_handler/google_calendar_handler.py +101 -95
  27. mindsdb/integrations/handlers/google_content_shopping_handler/google_content_shopping_handler.py +129 -129
  28. mindsdb/integrations/handlers/google_fit_handler/google_fit_handler.py +59 -43
  29. mindsdb/integrations/handlers/google_search_handler/google_search_handler.py +38 -39
  30. mindsdb/integrations/handlers/informix_handler/informix_handler.py +5 -18
  31. mindsdb/integrations/handlers/maxdb_handler/maxdb_handler.py +22 -28
  32. mindsdb/integrations/handlers/monetdb_handler/monetdb_handler.py +3 -7
  33. mindsdb/integrations/handlers/mongodb_handler/mongodb_handler.py +53 -67
  34. mindsdb/integrations/handlers/mongodb_handler/requirements.txt +1 -0
  35. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_ast.py +43 -68
  36. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_parser.py +17 -25
  37. mindsdb/{api/mongo/utilities → integrations/handlers/mongodb_handler/utils}/mongodb_query.py +10 -16
  38. mindsdb/integrations/handlers/mongodb_handler/utils/mongodb_render.py +43 -69
  39. mindsdb/integrations/libs/base.py +1 -1
  40. mindsdb/interfaces/agents/constants.py +17 -2
  41. mindsdb/interfaces/agents/langchain_agent.py +83 -18
  42. mindsdb/interfaces/knowledge_base/controller.py +3 -1
  43. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +7 -1
  44. mindsdb/interfaces/skills/skill_tool.py +7 -1
  45. mindsdb/interfaces/skills/sql_agent.py +6 -2
  46. mindsdb/utilities/config.py +3 -155
  47. mindsdb/utilities/fs.py +10 -4
  48. mindsdb/utilities/log.py +0 -25
  49. mindsdb/utilities/starters.py +0 -39
  50. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/METADATA +265 -263
  51. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/RECORD +54 -98
  52. mindsdb/api/a2a/__main__.py +0 -144
  53. mindsdb/api/a2a/run_a2a.py +0 -86
  54. mindsdb/api/common/check_auth.py +0 -42
  55. mindsdb/api/http/gunicorn_wrapper.py +0 -17
  56. mindsdb/api/mcp/start.py +0 -205
  57. mindsdb/api/mongo/__init__.py +0 -0
  58. mindsdb/api/mongo/classes/__init__.py +0 -5
  59. mindsdb/api/mongo/classes/query_sql.py +0 -19
  60. mindsdb/api/mongo/classes/responder.py +0 -45
  61. mindsdb/api/mongo/classes/responder_collection.py +0 -34
  62. mindsdb/api/mongo/classes/scram.py +0 -86
  63. mindsdb/api/mongo/classes/session.py +0 -23
  64. mindsdb/api/mongo/functions/__init__.py +0 -19
  65. mindsdb/api/mongo/responders/__init__.py +0 -73
  66. mindsdb/api/mongo/responders/add_shard.py +0 -13
  67. mindsdb/api/mongo/responders/aggregate.py +0 -90
  68. mindsdb/api/mongo/responders/buildinfo.py +0 -17
  69. mindsdb/api/mongo/responders/coll_stats.py +0 -63
  70. mindsdb/api/mongo/responders/company_id.py +0 -25
  71. mindsdb/api/mongo/responders/connection_status.py +0 -22
  72. mindsdb/api/mongo/responders/count.py +0 -21
  73. mindsdb/api/mongo/responders/db_stats.py +0 -32
  74. mindsdb/api/mongo/responders/delete.py +0 -105
  75. mindsdb/api/mongo/responders/describe.py +0 -23
  76. mindsdb/api/mongo/responders/end_sessions.py +0 -13
  77. mindsdb/api/mongo/responders/find.py +0 -175
  78. mindsdb/api/mongo/responders/get_cmd_line_opts.py +0 -18
  79. mindsdb/api/mongo/responders/get_free_monitoring_status.py +0 -14
  80. mindsdb/api/mongo/responders/get_parameter.py +0 -23
  81. mindsdb/api/mongo/responders/getlog.py +0 -14
  82. mindsdb/api/mongo/responders/host_info.py +0 -28
  83. mindsdb/api/mongo/responders/insert.py +0 -270
  84. mindsdb/api/mongo/responders/is_master.py +0 -20
  85. mindsdb/api/mongo/responders/is_master_lower.py +0 -13
  86. mindsdb/api/mongo/responders/list_collections.py +0 -55
  87. mindsdb/api/mongo/responders/list_databases.py +0 -37
  88. mindsdb/api/mongo/responders/list_indexes.py +0 -22
  89. mindsdb/api/mongo/responders/ping.py +0 -13
  90. mindsdb/api/mongo/responders/recv_chunk_start.py +0 -13
  91. mindsdb/api/mongo/responders/replsetgetstatus.py +0 -13
  92. mindsdb/api/mongo/responders/sasl_continue.py +0 -34
  93. mindsdb/api/mongo/responders/sasl_start.py +0 -33
  94. mindsdb/api/mongo/responders/update_range_deletions.py +0 -12
  95. mindsdb/api/mongo/responders/whatsmyuri.py +0 -18
  96. mindsdb/api/mongo/server.py +0 -388
  97. mindsdb/api/mongo/start.py +0 -15
  98. mindsdb/api/mongo/utilities/__init__.py +0 -0
  99. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/WHEEL +0 -0
  100. {mindsdb-25.8.2.0.dist-info → mindsdb-25.9.1.0.dist-info}/licenses/LICENSE +0 -0
  101. {mindsdb-25.8.2.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.2.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
 
@@ -134,6 +127,9 @@ class TrunkProcessData:
134
127
 
135
128
  def close_api_gracefully(trunc_processes_struct):
136
129
  _stop_event.set()
130
+
131
+ delete_pid_file()
132
+
137
133
  try:
138
134
  for trunc_processes_data in trunc_processes_struct.values():
139
135
  process = trunc_processes_data.process
@@ -345,9 +341,6 @@ if __name__ == "__main__":
345
341
  config.raise_warnings(logger=logger)
346
342
  os.environ["MINDSDB_RUNTIME"] = "1"
347
343
 
348
- if os.environ.get("FLASK_SECRET_KEY") is None:
349
- os.environ["FLASK_SECRET_KEY"] = secrets.token_hex(32)
350
-
351
344
  if os.environ.get("ARROW_DEFAULT_MEMORY_POOL") is None:
352
345
  try:
353
346
  """It seems like snowflake handler have memory issue that related to pyarrow. Memory usage keep growing with
@@ -382,7 +375,7 @@ if __name__ == "__main__":
382
375
  apis = os.getenv("MINDSDB_APIS") or config.cmd_args.api
383
376
 
384
377
  if apis is None: # If "--api" option is not specified, start the default APIs
385
- api_arr = [TrunkProcessEnum.HTTP, TrunkProcessEnum.MYSQL, TrunkProcessEnum.MCP, TrunkProcessEnum.A2A]
378
+ api_arr = [TrunkProcessEnum.HTTP, TrunkProcessEnum.MYSQL]
386
379
  elif apis == "": # If "--api=" (blank) is specified, don't start any APIs
387
380
  api_arr = []
388
381
  else: # The user has provided a list of APIs to start
@@ -418,9 +411,7 @@ if __name__ == "__main__":
418
411
  # Get config values for APIs
419
412
  http_api_config = config.get("api", {}).get("http", {})
420
413
  mysql_api_config = config.get("api", {}).get("mysql", {})
421
- mcp_api_config = config.get("api", {}).get("mcp", {})
422
414
  litellm_api_config = config.get("api", {}).get("litellm", {})
423
- a2a_api_config = config.get("api", {}).get("a2a", {})
424
415
  trunc_processes_struct = {
425
416
  TrunkProcessEnum.HTTP: TrunkProcessData(
426
417
  name=TrunkProcessEnum.HTTP.value,
@@ -444,12 +435,6 @@ if __name__ == "__main__":
444
435
  "max_restart_interval_seconds", TrunkProcessData.max_restart_interval_seconds
445
436
  ),
446
437
  ),
447
- TrunkProcessEnum.MONGODB: TrunkProcessData(
448
- name=TrunkProcessEnum.MONGODB.value,
449
- entrypoint=start_mongo,
450
- port=config["api"]["mongodb"]["port"],
451
- args=(config.cmd_args.verbose,),
452
- ),
453
438
  TrunkProcessEnum.POSTGRES: TrunkProcessData(
454
439
  name=TrunkProcessEnum.POSTGRES.value,
455
440
  entrypoint=start_postgres,
@@ -465,18 +450,6 @@ if __name__ == "__main__":
465
450
  TrunkProcessEnum.ML_TASK_QUEUE: TrunkProcessData(
466
451
  name=TrunkProcessEnum.ML_TASK_QUEUE.value, entrypoint=start_ml_task_queue, args=(config.cmd_args.verbose,)
467
452
  ),
468
- TrunkProcessEnum.MCP: TrunkProcessData(
469
- name=TrunkProcessEnum.MCP.value,
470
- entrypoint=start_mcp,
471
- port=mcp_api_config.get("port", 47337),
472
- args=(config.cmd_args.verbose,),
473
- need_to_run=mcp_api_config.get("need_to_run", False),
474
- restart_on_failure=mcp_api_config.get("restart_on_failure", False),
475
- max_restart_count=mcp_api_config.get("max_restart_count", TrunkProcessData.max_restart_count),
476
- max_restart_interval_seconds=mcp_api_config.get(
477
- "max_restart_interval_seconds", TrunkProcessData.max_restart_interval_seconds
478
- ),
479
- ),
480
453
  TrunkProcessEnum.LITELLM: TrunkProcessData(
481
454
  name=TrunkProcessEnum.LITELLM.value,
482
455
  entrypoint=start_litellm,
@@ -488,18 +461,6 @@ if __name__ == "__main__":
488
461
  "max_restart_interval_seconds", TrunkProcessData.max_restart_interval_seconds
489
462
  ),
490
463
  ),
491
- TrunkProcessEnum.A2A: TrunkProcessData(
492
- name=TrunkProcessEnum.A2A.value,
493
- entrypoint=start_a2a,
494
- port=a2a_api_config.get("port", 8001),
495
- args=(config.cmd_args.verbose,),
496
- need_to_run=a2a_api_config.get("enabled", False),
497
- restart_on_failure=a2a_api_config.get("restart_on_failure", True),
498
- max_restart_count=a2a_api_config.get("max_restart_count", TrunkProcessData.max_restart_count),
499
- max_restart_interval_seconds=a2a_api_config.get(
500
- "max_restart_interval_seconds", TrunkProcessData.max_restart_interval_seconds
501
- ),
502
- ),
503
464
  }
504
465
 
505
466
  for api_enum in api_arr:
@@ -600,7 +561,6 @@ if __name__ == "__main__":
600
561
  ],
601
562
  return_exceptions=False,
602
563
  )
603
- delete_pid_file()
604
564
 
605
565
  ioloop = asyncio.new_event_loop()
606
566
  ioloop.run_until_complete(wait_apis_start())
@@ -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
- from mindsdb.api.a2a.utils import to_serializable
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,23 +116,12 @@ 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
- formatted_messages = []
119
+ # Create A2A message structure with history and current query
120
+ a2a_message = {"role": "user", "parts": [{"text": query}]}
121
121
  if history:
122
- for msg in history:
123
- msg_dict = msg.dict() if hasattr(msg, "dict") else msg
124
- role = msg_dict.get("role", "user")
125
- text = ""
126
- for part in msg_dict.get("parts", []):
127
- if part.get("type") == "text":
128
- text = part.get("text", "")
129
- break
130
- if text:
131
- if role == "user":
132
- formatted_messages.append({"question": text, "answer": None})
133
- elif role == "assistant" and formatted_messages:
134
- formatted_messages[-1]["answer"] = text
135
- formatted_messages.append({"question": query, "answer": None})
122
+ a2a_message["history"] = history
123
+ # Convert to Q&A format using centralized utility
124
+ formatted_messages = convert_a2a_message_to_qa_format(a2a_message)
136
125
  logger.debug(f"Formatted messages for agent: {formatted_messages}")
137
126
  streaming_response = self.streaming_invoke(formatted_messages, timeout=timeout)
138
127
  async for chunk in streaming_response:
@@ -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]
@@ -35,9 +35,7 @@ class FileContent(BaseModel):
35
35
  if not (self.bytes or self.uri):
36
36
  raise ValueError("Either 'bytes' or 'uri' must be present in the file data")
37
37
  if self.bytes and self.uri:
38
- raise ValueError(
39
- "Only one of 'bytes' or 'uri' can be present in the file data"
40
- )
38
+ raise ValueError("Only one of 'bytes' or 'uri' can be present in the file data")
41
39
  return self
42
40
 
43
41
 
@@ -57,9 +55,10 @@ Part = Annotated[Union[TextPart, FilePart, DataPart], Field(discriminator="type"
57
55
 
58
56
 
59
57
  class Message(BaseModel):
60
- role: Literal["user", "agent"]
58
+ role: Literal["user", "agent", "assistant"]
61
59
  parts: List[Part]
62
60
  metadata: dict[str, Any] | None = None
61
+ history: Optional[List["Message"]] = None
63
62
 
64
63
 
65
64
  class TaskStatus(BaseModel):