agno 2.2.13__py3-none-any.whl → 2.4.3__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.
Files changed (383) hide show
  1. agno/agent/__init__.py +6 -0
  2. agno/agent/agent.py +5252 -3145
  3. agno/agent/remote.py +525 -0
  4. agno/api/api.py +2 -0
  5. agno/client/__init__.py +3 -0
  6. agno/client/a2a/__init__.py +10 -0
  7. agno/client/a2a/client.py +554 -0
  8. agno/client/a2a/schemas.py +112 -0
  9. agno/client/a2a/utils.py +369 -0
  10. agno/client/os.py +2669 -0
  11. agno/compression/__init__.py +3 -0
  12. agno/compression/manager.py +247 -0
  13. agno/culture/manager.py +2 -2
  14. agno/db/base.py +927 -6
  15. agno/db/dynamo/dynamo.py +788 -2
  16. agno/db/dynamo/schemas.py +128 -0
  17. agno/db/dynamo/utils.py +26 -3
  18. agno/db/firestore/firestore.py +674 -50
  19. agno/db/firestore/schemas.py +41 -0
  20. agno/db/firestore/utils.py +25 -10
  21. agno/db/gcs_json/gcs_json_db.py +506 -3
  22. agno/db/gcs_json/utils.py +14 -2
  23. agno/db/in_memory/in_memory_db.py +203 -4
  24. agno/db/in_memory/utils.py +14 -2
  25. agno/db/json/json_db.py +498 -2
  26. agno/db/json/utils.py +14 -2
  27. agno/db/migrations/manager.py +199 -0
  28. agno/db/migrations/utils.py +19 -0
  29. agno/db/migrations/v1_to_v2.py +54 -16
  30. agno/db/migrations/versions/__init__.py +0 -0
  31. agno/db/migrations/versions/v2_3_0.py +977 -0
  32. agno/db/mongo/async_mongo.py +1013 -39
  33. agno/db/mongo/mongo.py +684 -4
  34. agno/db/mongo/schemas.py +48 -0
  35. agno/db/mongo/utils.py +17 -0
  36. agno/db/mysql/__init__.py +2 -1
  37. agno/db/mysql/async_mysql.py +2958 -0
  38. agno/db/mysql/mysql.py +722 -53
  39. agno/db/mysql/schemas.py +77 -11
  40. agno/db/mysql/utils.py +151 -8
  41. agno/db/postgres/async_postgres.py +1254 -137
  42. agno/db/postgres/postgres.py +2316 -93
  43. agno/db/postgres/schemas.py +153 -21
  44. agno/db/postgres/utils.py +22 -7
  45. agno/db/redis/redis.py +531 -3
  46. agno/db/redis/schemas.py +36 -0
  47. agno/db/redis/utils.py +31 -15
  48. agno/db/schemas/evals.py +1 -0
  49. agno/db/schemas/memory.py +20 -9
  50. agno/db/singlestore/schemas.py +70 -1
  51. agno/db/singlestore/singlestore.py +737 -74
  52. agno/db/singlestore/utils.py +13 -3
  53. agno/db/sqlite/async_sqlite.py +1069 -89
  54. agno/db/sqlite/schemas.py +133 -1
  55. agno/db/sqlite/sqlite.py +2203 -165
  56. agno/db/sqlite/utils.py +21 -11
  57. agno/db/surrealdb/models.py +25 -0
  58. agno/db/surrealdb/surrealdb.py +603 -1
  59. agno/db/utils.py +60 -0
  60. agno/eval/__init__.py +26 -3
  61. agno/eval/accuracy.py +25 -12
  62. agno/eval/agent_as_judge.py +871 -0
  63. agno/eval/base.py +29 -0
  64. agno/eval/performance.py +10 -4
  65. agno/eval/reliability.py +22 -13
  66. agno/eval/utils.py +2 -1
  67. agno/exceptions.py +42 -0
  68. agno/hooks/__init__.py +3 -0
  69. agno/hooks/decorator.py +164 -0
  70. agno/integrations/discord/client.py +13 -2
  71. agno/knowledge/__init__.py +4 -0
  72. agno/knowledge/chunking/code.py +90 -0
  73. agno/knowledge/chunking/document.py +65 -4
  74. agno/knowledge/chunking/fixed.py +4 -1
  75. agno/knowledge/chunking/markdown.py +102 -11
  76. agno/knowledge/chunking/recursive.py +2 -2
  77. agno/knowledge/chunking/semantic.py +130 -48
  78. agno/knowledge/chunking/strategy.py +18 -0
  79. agno/knowledge/embedder/azure_openai.py +0 -1
  80. agno/knowledge/embedder/google.py +1 -1
  81. agno/knowledge/embedder/mistral.py +1 -1
  82. agno/knowledge/embedder/nebius.py +1 -1
  83. agno/knowledge/embedder/openai.py +16 -12
  84. agno/knowledge/filesystem.py +412 -0
  85. agno/knowledge/knowledge.py +4261 -1199
  86. agno/knowledge/protocol.py +134 -0
  87. agno/knowledge/reader/arxiv_reader.py +3 -2
  88. agno/knowledge/reader/base.py +9 -7
  89. agno/knowledge/reader/csv_reader.py +91 -42
  90. agno/knowledge/reader/docx_reader.py +9 -10
  91. agno/knowledge/reader/excel_reader.py +225 -0
  92. agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
  93. agno/knowledge/reader/firecrawl_reader.py +3 -2
  94. agno/knowledge/reader/json_reader.py +16 -22
  95. agno/knowledge/reader/markdown_reader.py +15 -14
  96. agno/knowledge/reader/pdf_reader.py +33 -28
  97. agno/knowledge/reader/pptx_reader.py +9 -10
  98. agno/knowledge/reader/reader_factory.py +135 -1
  99. agno/knowledge/reader/s3_reader.py +8 -16
  100. agno/knowledge/reader/tavily_reader.py +3 -3
  101. agno/knowledge/reader/text_reader.py +15 -14
  102. agno/knowledge/reader/utils/__init__.py +17 -0
  103. agno/knowledge/reader/utils/spreadsheet.py +114 -0
  104. agno/knowledge/reader/web_search_reader.py +8 -65
  105. agno/knowledge/reader/website_reader.py +16 -13
  106. agno/knowledge/reader/wikipedia_reader.py +36 -3
  107. agno/knowledge/reader/youtube_reader.py +3 -2
  108. agno/knowledge/remote_content/__init__.py +33 -0
  109. agno/knowledge/remote_content/config.py +266 -0
  110. agno/knowledge/remote_content/remote_content.py +105 -17
  111. agno/knowledge/utils.py +76 -22
  112. agno/learn/__init__.py +71 -0
  113. agno/learn/config.py +463 -0
  114. agno/learn/curate.py +185 -0
  115. agno/learn/machine.py +725 -0
  116. agno/learn/schemas.py +1114 -0
  117. agno/learn/stores/__init__.py +38 -0
  118. agno/learn/stores/decision_log.py +1156 -0
  119. agno/learn/stores/entity_memory.py +3275 -0
  120. agno/learn/stores/learned_knowledge.py +1583 -0
  121. agno/learn/stores/protocol.py +117 -0
  122. agno/learn/stores/session_context.py +1217 -0
  123. agno/learn/stores/user_memory.py +1495 -0
  124. agno/learn/stores/user_profile.py +1220 -0
  125. agno/learn/utils.py +209 -0
  126. agno/media.py +22 -6
  127. agno/memory/__init__.py +14 -1
  128. agno/memory/manager.py +223 -8
  129. agno/memory/strategies/__init__.py +15 -0
  130. agno/memory/strategies/base.py +66 -0
  131. agno/memory/strategies/summarize.py +196 -0
  132. agno/memory/strategies/types.py +37 -0
  133. agno/models/aimlapi/aimlapi.py +17 -0
  134. agno/models/anthropic/claude.py +434 -59
  135. agno/models/aws/bedrock.py +121 -20
  136. agno/models/aws/claude.py +131 -274
  137. agno/models/azure/ai_foundry.py +10 -6
  138. agno/models/azure/openai_chat.py +33 -10
  139. agno/models/base.py +1162 -561
  140. agno/models/cerebras/cerebras.py +120 -24
  141. agno/models/cerebras/cerebras_openai.py +21 -2
  142. agno/models/cohere/chat.py +65 -6
  143. agno/models/cometapi/cometapi.py +18 -1
  144. agno/models/dashscope/dashscope.py +2 -3
  145. agno/models/deepinfra/deepinfra.py +18 -1
  146. agno/models/deepseek/deepseek.py +69 -3
  147. agno/models/fireworks/fireworks.py +18 -1
  148. agno/models/google/gemini.py +959 -89
  149. agno/models/google/utils.py +22 -0
  150. agno/models/groq/groq.py +48 -18
  151. agno/models/huggingface/huggingface.py +17 -6
  152. agno/models/ibm/watsonx.py +16 -6
  153. agno/models/internlm/internlm.py +18 -1
  154. agno/models/langdb/langdb.py +13 -1
  155. agno/models/litellm/chat.py +88 -9
  156. agno/models/litellm/litellm_openai.py +18 -1
  157. agno/models/message.py +24 -5
  158. agno/models/meta/llama.py +40 -13
  159. agno/models/meta/llama_openai.py +22 -21
  160. agno/models/metrics.py +12 -0
  161. agno/models/mistral/mistral.py +8 -4
  162. agno/models/n1n/__init__.py +3 -0
  163. agno/models/n1n/n1n.py +57 -0
  164. agno/models/nebius/nebius.py +6 -7
  165. agno/models/nvidia/nvidia.py +20 -3
  166. agno/models/ollama/__init__.py +2 -0
  167. agno/models/ollama/chat.py +17 -6
  168. agno/models/ollama/responses.py +100 -0
  169. agno/models/openai/__init__.py +2 -0
  170. agno/models/openai/chat.py +117 -26
  171. agno/models/openai/open_responses.py +46 -0
  172. agno/models/openai/responses.py +110 -32
  173. agno/models/openrouter/__init__.py +2 -0
  174. agno/models/openrouter/openrouter.py +67 -2
  175. agno/models/openrouter/responses.py +146 -0
  176. agno/models/perplexity/perplexity.py +19 -1
  177. agno/models/portkey/portkey.py +7 -6
  178. agno/models/requesty/requesty.py +19 -2
  179. agno/models/response.py +20 -2
  180. agno/models/sambanova/sambanova.py +20 -3
  181. agno/models/siliconflow/siliconflow.py +19 -2
  182. agno/models/together/together.py +20 -3
  183. agno/models/vercel/v0.py +20 -3
  184. agno/models/vertexai/claude.py +124 -4
  185. agno/models/vllm/vllm.py +19 -14
  186. agno/models/xai/xai.py +19 -2
  187. agno/os/app.py +467 -137
  188. agno/os/auth.py +253 -5
  189. agno/os/config.py +22 -0
  190. agno/os/interfaces/a2a/a2a.py +7 -6
  191. agno/os/interfaces/a2a/router.py +635 -26
  192. agno/os/interfaces/a2a/utils.py +32 -33
  193. agno/os/interfaces/agui/agui.py +5 -3
  194. agno/os/interfaces/agui/router.py +26 -16
  195. agno/os/interfaces/agui/utils.py +97 -57
  196. agno/os/interfaces/base.py +7 -7
  197. agno/os/interfaces/slack/router.py +16 -7
  198. agno/os/interfaces/slack/slack.py +7 -7
  199. agno/os/interfaces/whatsapp/router.py +35 -7
  200. agno/os/interfaces/whatsapp/security.py +3 -1
  201. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  202. agno/os/managers.py +326 -0
  203. agno/os/mcp.py +652 -79
  204. agno/os/middleware/__init__.py +4 -0
  205. agno/os/middleware/jwt.py +718 -115
  206. agno/os/middleware/trailing_slash.py +27 -0
  207. agno/os/router.py +105 -1558
  208. agno/os/routers/agents/__init__.py +3 -0
  209. agno/os/routers/agents/router.py +655 -0
  210. agno/os/routers/agents/schema.py +288 -0
  211. agno/os/routers/components/__init__.py +3 -0
  212. agno/os/routers/components/components.py +475 -0
  213. agno/os/routers/database.py +155 -0
  214. agno/os/routers/evals/evals.py +111 -18
  215. agno/os/routers/evals/schemas.py +38 -5
  216. agno/os/routers/evals/utils.py +80 -11
  217. agno/os/routers/health.py +3 -3
  218. agno/os/routers/knowledge/knowledge.py +284 -35
  219. agno/os/routers/knowledge/schemas.py +14 -2
  220. agno/os/routers/memory/memory.py +274 -11
  221. agno/os/routers/memory/schemas.py +44 -3
  222. agno/os/routers/metrics/metrics.py +30 -15
  223. agno/os/routers/metrics/schemas.py +10 -6
  224. agno/os/routers/registry/__init__.py +3 -0
  225. agno/os/routers/registry/registry.py +337 -0
  226. agno/os/routers/session/session.py +143 -14
  227. agno/os/routers/teams/__init__.py +3 -0
  228. agno/os/routers/teams/router.py +550 -0
  229. agno/os/routers/teams/schema.py +280 -0
  230. agno/os/routers/traces/__init__.py +3 -0
  231. agno/os/routers/traces/schemas.py +414 -0
  232. agno/os/routers/traces/traces.py +549 -0
  233. agno/os/routers/workflows/__init__.py +3 -0
  234. agno/os/routers/workflows/router.py +757 -0
  235. agno/os/routers/workflows/schema.py +139 -0
  236. agno/os/schema.py +157 -584
  237. agno/os/scopes.py +469 -0
  238. agno/os/settings.py +3 -0
  239. agno/os/utils.py +574 -185
  240. agno/reasoning/anthropic.py +85 -1
  241. agno/reasoning/azure_ai_foundry.py +93 -1
  242. agno/reasoning/deepseek.py +102 -2
  243. agno/reasoning/default.py +6 -7
  244. agno/reasoning/gemini.py +87 -3
  245. agno/reasoning/groq.py +109 -2
  246. agno/reasoning/helpers.py +6 -7
  247. agno/reasoning/manager.py +1238 -0
  248. agno/reasoning/ollama.py +93 -1
  249. agno/reasoning/openai.py +115 -1
  250. agno/reasoning/vertexai.py +85 -1
  251. agno/registry/__init__.py +3 -0
  252. agno/registry/registry.py +68 -0
  253. agno/remote/__init__.py +3 -0
  254. agno/remote/base.py +581 -0
  255. agno/run/__init__.py +2 -4
  256. agno/run/agent.py +134 -19
  257. agno/run/base.py +49 -1
  258. agno/run/cancel.py +65 -52
  259. agno/run/cancellation_management/__init__.py +9 -0
  260. agno/run/cancellation_management/base.py +78 -0
  261. agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
  262. agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
  263. agno/run/requirement.py +181 -0
  264. agno/run/team.py +111 -19
  265. agno/run/workflow.py +2 -1
  266. agno/session/agent.py +57 -92
  267. agno/session/summary.py +1 -1
  268. agno/session/team.py +62 -115
  269. agno/session/workflow.py +353 -57
  270. agno/skills/__init__.py +17 -0
  271. agno/skills/agent_skills.py +377 -0
  272. agno/skills/errors.py +32 -0
  273. agno/skills/loaders/__init__.py +4 -0
  274. agno/skills/loaders/base.py +27 -0
  275. agno/skills/loaders/local.py +216 -0
  276. agno/skills/skill.py +65 -0
  277. agno/skills/utils.py +107 -0
  278. agno/skills/validator.py +277 -0
  279. agno/table.py +10 -0
  280. agno/team/__init__.py +5 -1
  281. agno/team/remote.py +447 -0
  282. agno/team/team.py +3769 -2202
  283. agno/tools/brandfetch.py +27 -18
  284. agno/tools/browserbase.py +225 -16
  285. agno/tools/crawl4ai.py +3 -0
  286. agno/tools/duckduckgo.py +25 -71
  287. agno/tools/exa.py +0 -21
  288. agno/tools/file.py +14 -13
  289. agno/tools/file_generation.py +12 -6
  290. agno/tools/firecrawl.py +15 -7
  291. agno/tools/function.py +94 -113
  292. agno/tools/google_bigquery.py +11 -2
  293. agno/tools/google_drive.py +4 -3
  294. agno/tools/knowledge.py +9 -4
  295. agno/tools/mcp/mcp.py +301 -18
  296. agno/tools/mcp/multi_mcp.py +269 -14
  297. agno/tools/mem0.py +11 -10
  298. agno/tools/memory.py +47 -46
  299. agno/tools/mlx_transcribe.py +10 -7
  300. agno/tools/models/nebius.py +5 -5
  301. agno/tools/models_labs.py +20 -10
  302. agno/tools/nano_banana.py +151 -0
  303. agno/tools/parallel.py +0 -7
  304. agno/tools/postgres.py +76 -36
  305. agno/tools/python.py +14 -6
  306. agno/tools/reasoning.py +30 -23
  307. agno/tools/redshift.py +406 -0
  308. agno/tools/shopify.py +1519 -0
  309. agno/tools/spotify.py +919 -0
  310. agno/tools/tavily.py +4 -1
  311. agno/tools/toolkit.py +253 -18
  312. agno/tools/websearch.py +93 -0
  313. agno/tools/website.py +1 -1
  314. agno/tools/wikipedia.py +1 -1
  315. agno/tools/workflow.py +56 -48
  316. agno/tools/yfinance.py +12 -11
  317. agno/tracing/__init__.py +12 -0
  318. agno/tracing/exporter.py +161 -0
  319. agno/tracing/schemas.py +276 -0
  320. agno/tracing/setup.py +112 -0
  321. agno/utils/agent.py +251 -10
  322. agno/utils/cryptography.py +22 -0
  323. agno/utils/dttm.py +33 -0
  324. agno/utils/events.py +264 -7
  325. agno/utils/hooks.py +111 -3
  326. agno/utils/http.py +161 -2
  327. agno/utils/mcp.py +49 -8
  328. agno/utils/media.py +22 -1
  329. agno/utils/models/ai_foundry.py +9 -2
  330. agno/utils/models/claude.py +20 -5
  331. agno/utils/models/cohere.py +9 -2
  332. agno/utils/models/llama.py +9 -2
  333. agno/utils/models/mistral.py +4 -2
  334. agno/utils/os.py +0 -0
  335. agno/utils/print_response/agent.py +99 -16
  336. agno/utils/print_response/team.py +223 -24
  337. agno/utils/print_response/workflow.py +0 -2
  338. agno/utils/prompts.py +8 -6
  339. agno/utils/remote.py +23 -0
  340. agno/utils/response.py +1 -13
  341. agno/utils/string.py +91 -2
  342. agno/utils/team.py +62 -12
  343. agno/utils/tokens.py +657 -0
  344. agno/vectordb/base.py +15 -2
  345. agno/vectordb/cassandra/cassandra.py +1 -1
  346. agno/vectordb/chroma/__init__.py +2 -1
  347. agno/vectordb/chroma/chromadb.py +468 -23
  348. agno/vectordb/clickhouse/clickhousedb.py +1 -1
  349. agno/vectordb/couchbase/couchbase.py +6 -2
  350. agno/vectordb/lancedb/lance_db.py +7 -38
  351. agno/vectordb/lightrag/lightrag.py +7 -6
  352. agno/vectordb/milvus/milvus.py +118 -84
  353. agno/vectordb/mongodb/__init__.py +2 -1
  354. agno/vectordb/mongodb/mongodb.py +14 -31
  355. agno/vectordb/pgvector/pgvector.py +120 -66
  356. agno/vectordb/pineconedb/pineconedb.py +2 -19
  357. agno/vectordb/qdrant/__init__.py +2 -1
  358. agno/vectordb/qdrant/qdrant.py +33 -56
  359. agno/vectordb/redis/__init__.py +2 -1
  360. agno/vectordb/redis/redisdb.py +19 -31
  361. agno/vectordb/singlestore/singlestore.py +17 -9
  362. agno/vectordb/surrealdb/surrealdb.py +2 -38
  363. agno/vectordb/weaviate/__init__.py +2 -1
  364. agno/vectordb/weaviate/weaviate.py +7 -3
  365. agno/workflow/__init__.py +5 -1
  366. agno/workflow/agent.py +2 -2
  367. agno/workflow/condition.py +12 -10
  368. agno/workflow/loop.py +28 -9
  369. agno/workflow/parallel.py +21 -13
  370. agno/workflow/remote.py +362 -0
  371. agno/workflow/router.py +12 -9
  372. agno/workflow/step.py +261 -36
  373. agno/workflow/steps.py +12 -8
  374. agno/workflow/types.py +40 -77
  375. agno/workflow/workflow.py +939 -213
  376. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
  377. agno-2.4.3.dist-info/RECORD +677 -0
  378. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
  379. agno/tools/googlesearch.py +0 -98
  380. agno/tools/memori.py +0 -339
  381. agno-2.2.13.dist-info/RECORD +0 -575
  382. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
  383. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
@@ -9,36 +9,633 @@ from fastapi.routing import APIRouter
9
9
  from typing_extensions import List
10
10
 
11
11
  try:
12
- from a2a.types import SendMessageSuccessResponse, Task, TaskState, TaskStatus
12
+ from a2a.types import (
13
+ AgentCapabilities,
14
+ AgentCard,
15
+ AgentSkill,
16
+ SendMessageSuccessResponse,
17
+ Task,
18
+ TaskState,
19
+ TaskStatus,
20
+ )
13
21
  except ImportError as e:
14
22
  raise ImportError("`a2a` not installed. Please install it with `pip install -U a2a-sdk`") from e
15
23
 
16
- from agno.agent import Agent
24
+ import warnings
25
+
26
+ from agno.agent import Agent, RemoteAgent
17
27
  from agno.os.interfaces.a2a.utils import (
18
28
  map_a2a_request_to_run_input,
19
29
  map_run_output_to_a2a_task,
20
30
  stream_a2a_response_with_error_handling,
21
31
  )
22
- from agno.os.router import _get_request_kwargs
23
- from agno.os.utils import get_agent_by_id, get_team_by_id, get_workflow_by_id
24
- from agno.team import Team
25
- from agno.workflow import Workflow
32
+ from agno.os.utils import get_agent_by_id, get_request_kwargs, get_team_by_id, get_workflow_by_id
33
+ from agno.team import RemoteTeam, Team
34
+ from agno.workflow import RemoteWorkflow, Workflow
26
35
 
27
36
 
28
37
  def attach_routes(
29
38
  router: APIRouter,
30
- agents: Optional[List[Agent]] = None,
31
- teams: Optional[List[Team]] = None,
32
- workflows: Optional[List[Workflow]] = None,
39
+ agents: Optional[List[Union[Agent, RemoteAgent]]] = None,
40
+ teams: Optional[List[Union[Team, RemoteTeam]]] = None,
41
+ workflows: Optional[List[Union[Workflow, RemoteWorkflow]]] = None,
33
42
  ) -> APIRouter:
34
43
  if agents is None and teams is None and workflows is None:
35
44
  raise ValueError("Agents, Teams, or Workflows are required to setup the A2A interface.")
36
45
 
46
+ # ============= AGENTS =============
47
+ @router.get("/agents/{id}/.well-known/agent-card.json")
48
+ async def get_agent_card(request: Request, id: str):
49
+ agent = get_agent_by_id(id, agents)
50
+ if not agent:
51
+ raise HTTPException(status_code=404, detail="Agent not found")
52
+
53
+ base_url = str(request.base_url).rstrip("/")
54
+ skill = AgentSkill(
55
+ id=agent.id or "",
56
+ name=agent.name or "",
57
+ description=agent.description or "",
58
+ tags=["agno"],
59
+ examples=["search", "ok"],
60
+ output_modes=["application/json"],
61
+ )
62
+
63
+ return AgentCard(
64
+ name=agent.name or "",
65
+ version="1.0.0",
66
+ description=agent.description or "",
67
+ url=f"{base_url}/a2a/agents/{agent.id}/v1/message:stream",
68
+ default_input_modes=["text"],
69
+ default_output_modes=["text"],
70
+ capabilities=AgentCapabilities(streaming=True, push_notifications=False, state_transition_history=False),
71
+ skills=[skill],
72
+ supports_authenticated_extended_card=False,
73
+ )
74
+
75
+ @router.post(
76
+ "/agents/{id}/v1/message:send",
77
+ operation_id="run_message_agent",
78
+ name="run_message_agent",
79
+ description="Send a message to an Agno Agent (non-streaming). The Agent is identified via the path parameter '{id}'. "
80
+ "Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata.",
81
+ response_model_exclude_none=True,
82
+ responses={
83
+ 200: {
84
+ "description": "Message sent successfully",
85
+ "content": {
86
+ "application/json": {
87
+ "example": {
88
+ "jsonrpc": "2.0",
89
+ "id": "request-123",
90
+ "result": {
91
+ "task": {
92
+ "id": "task-456",
93
+ "context_id": "context-789",
94
+ "status": "completed",
95
+ "history": [
96
+ {
97
+ "message_id": "msg-1",
98
+ "role": "agent",
99
+ "parts": [{"kind": "text", "text": "Response from agent"}],
100
+ }
101
+ ],
102
+ }
103
+ },
104
+ }
105
+ }
106
+ },
107
+ },
108
+ 400: {"description": "Invalid request"},
109
+ 404: {"description": "Agent not found"},
110
+ },
111
+ response_model=SendMessageSuccessResponse,
112
+ )
113
+ async def a2a_run_agent(request: Request, id: str):
114
+ if not agents:
115
+ raise HTTPException(status_code=404, detail="Agent not found")
116
+
117
+ # Load the request body. Unknown args are passed down as kwargs.
118
+ request_body = await request.json()
119
+ kwargs = await get_request_kwargs(request, a2a_run_agent)
120
+
121
+ # 1. Get the Agent to run
122
+ agent = get_agent_by_id(id, agents)
123
+ if not agent:
124
+ raise HTTPException(status_code=404, detail="Agent not found")
125
+
126
+ # 2. Map the request to our run_input and run variables
127
+ run_input = await map_a2a_request_to_run_input(request_body, stream=False)
128
+ context_id = request_body.get("params", {}).get("message", {}).get("contextId")
129
+ user_id = request.headers.get("X-User-ID")
130
+ if not user_id:
131
+ user_id = request_body.get("params", {}).get("message", {}).get("metadata", {}).get("userId")
132
+
133
+ # 3. Run the Agent
134
+ try:
135
+ response = await agent.arun(
136
+ input=run_input.input_content,
137
+ images=run_input.images,
138
+ videos=run_input.videos,
139
+ audio=run_input.audios,
140
+ files=run_input.files,
141
+ session_id=context_id,
142
+ user_id=user_id,
143
+ **kwargs,
144
+ )
145
+
146
+ # 4. Send the response
147
+ a2a_task = map_run_output_to_a2a_task(response)
148
+ return SendMessageSuccessResponse(
149
+ id=request_body.get("id", "unknown"),
150
+ result=a2a_task,
151
+ )
152
+
153
+ # Handle any critical error
154
+ except Exception as e:
155
+ from a2a.types import Message as A2AMessage
156
+ from a2a.types import Part, Role, TextPart
157
+
158
+ error_message = A2AMessage(
159
+ message_id=str(uuid4()),
160
+ role=Role.agent,
161
+ parts=[Part(root=TextPart(text=f"Error: {str(e)}"))],
162
+ context_id=context_id or str(uuid4()),
163
+ )
164
+ failed_task = Task(
165
+ id=str(uuid4()),
166
+ context_id=context_id or str(uuid4()),
167
+ status=TaskStatus(state=TaskState.failed),
168
+ history=[error_message],
169
+ )
170
+
171
+ return SendMessageSuccessResponse(
172
+ id=request_body.get("id", "unknown"),
173
+ result=failed_task,
174
+ )
175
+
176
+ @router.post(
177
+ "/agents/{id}/v1/message:stream",
178
+ operation_id="stream_message_agent",
179
+ name="stream_message_agent",
180
+ description="Stream a message to an Agno Agent (streaming). The Agent is identified via the path parameter '{id}'. "
181
+ "Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata. "
182
+ "Returns real-time updates as newline-delimited JSON (NDJSON).",
183
+ response_model_exclude_none=True,
184
+ responses={
185
+ 200: {
186
+ "description": "Streaming response with task updates",
187
+ "content": {
188
+ "text/event-stream": {
189
+ "example": 'event: TaskStatusUpdateEvent\ndata: {"jsonrpc":"2.0","id":"request-123","result":{"taskId":"task-456","status":"working"}}\n\n'
190
+ 'event: Message\ndata: {"jsonrpc":"2.0","id":"request-123","result":{"messageId":"msg-1","role":"agent","parts":[{"kind":"text","text":"Response"}]}}\n\n'
191
+ }
192
+ },
193
+ },
194
+ 400: {"description": "Invalid request"},
195
+ 404: {"description": "Agent not found"},
196
+ },
197
+ )
198
+ async def a2a_stream_agent(request: Request, id: str):
199
+ if not agents:
200
+ raise HTTPException(status_code=404, detail="Agent not found")
201
+
202
+ # Load the request body. Unknown args are passed down as kwargs.
203
+ request_body = await request.json()
204
+ kwargs = await get_request_kwargs(request, a2a_stream_agent)
205
+
206
+ # 1. Get the Agent to run
207
+ agent = get_agent_by_id(id, agents)
208
+ if not agent:
209
+ raise HTTPException(status_code=404, detail="Agent not found")
210
+
211
+ # 2. Map the request to our run_input and run variables
212
+ run_input = await map_a2a_request_to_run_input(request_body, stream=True)
213
+ context_id = request_body.get("params", {}).get("message", {}).get("contextId")
214
+ user_id = request.headers.get("X-User-ID")
215
+ if not user_id:
216
+ user_id = request_body.get("params", {}).get("message", {}).get("metadata", {}).get("userId")
217
+
218
+ # 3. Run the Agent and stream the response
219
+ try:
220
+ event_stream = agent.arun(
221
+ input=run_input.input_content,
222
+ images=run_input.images,
223
+ videos=run_input.videos,
224
+ audio=run_input.audios,
225
+ files=run_input.files,
226
+ session_id=context_id,
227
+ user_id=user_id,
228
+ stream=True,
229
+ stream_events=True,
230
+ **kwargs,
231
+ )
232
+
233
+ # 4. Stream the response
234
+ return StreamingResponse(
235
+ stream_a2a_response_with_error_handling(event_stream=event_stream, request_id=request_body["id"]), # type: ignore[arg-type]
236
+ media_type="text/event-stream",
237
+ )
238
+
239
+ except Exception as e:
240
+ raise HTTPException(status_code=500, detail=f"Failed to start run: {str(e)}")
241
+
242
+ # ============= TEAMS =============
243
+ @router.get("/teams/{id}/.well-known/agent-card.json")
244
+ async def get_team_card(request: Request, id: str):
245
+ team = get_team_by_id(id, teams)
246
+ if not team:
247
+ raise HTTPException(status_code=404, detail="Team not found")
248
+
249
+ base_url = str(request.base_url).rstrip("/")
250
+ skill = AgentSkill(
251
+ id=team.id or "",
252
+ name=team.name or "",
253
+ description=team.description or "",
254
+ tags=["agno"],
255
+ examples=["search", "ok"],
256
+ output_modes=["application/json"],
257
+ )
258
+ return AgentCard(
259
+ name=team.name or "",
260
+ version="1.0.0",
261
+ description=team.description or "",
262
+ url=f"{base_url}/a2a/teams/{team.id}/v1/message:stream",
263
+ default_input_modes=["text"],
264
+ default_output_modes=["text"],
265
+ capabilities=AgentCapabilities(streaming=True, push_notifications=False, state_transition_history=False),
266
+ skills=[skill],
267
+ supports_authenticated_extended_card=False,
268
+ )
269
+
270
+ @router.post(
271
+ "/teams/{id}/v1/message:send",
272
+ operation_id="run_message_team",
273
+ name="run_message_team",
274
+ description="Send a message to an Agno Team (non-streaming). The Team is identified via the path parameter '{id}'. "
275
+ "Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata.",
276
+ response_model_exclude_none=True,
277
+ responses={
278
+ 200: {
279
+ "description": "Message sent successfully",
280
+ "content": {
281
+ "application/json": {
282
+ "example": {
283
+ "jsonrpc": "2.0",
284
+ "id": "request-123",
285
+ "result": {
286
+ "task": {
287
+ "id": "task-456",
288
+ "context_id": "context-789",
289
+ "status": "completed",
290
+ "history": [
291
+ {
292
+ "message_id": "msg-1",
293
+ "role": "agent",
294
+ "parts": [{"kind": "text", "text": "Response from agent"}],
295
+ }
296
+ ],
297
+ }
298
+ },
299
+ }
300
+ }
301
+ },
302
+ },
303
+ 400: {"description": "Invalid request"},
304
+ 404: {"description": "Team not found"},
305
+ },
306
+ response_model=SendMessageSuccessResponse,
307
+ )
308
+ async def a2a_run_team(request: Request, id: str):
309
+ if not teams:
310
+ raise HTTPException(status_code=404, detail="Team not found")
311
+
312
+ # Load the request body. Unknown args are passed down as kwargs.
313
+ request_body = await request.json()
314
+ kwargs = await get_request_kwargs(request, a2a_run_team)
315
+
316
+ # 1. Get the Team to run
317
+ team = get_team_by_id(id, teams)
318
+ if not team:
319
+ raise HTTPException(status_code=404, detail="Team not found")
320
+
321
+ # 2. Map the request to our run_input and run variables
322
+ run_input = await map_a2a_request_to_run_input(request_body, stream=False)
323
+ context_id = request_body.get("params", {}).get("message", {}).get("contextId")
324
+ user_id = request.headers.get("X-User-ID")
325
+ if not user_id:
326
+ user_id = request_body.get("params", {}).get("message", {}).get("metadata", {}).get("userId")
327
+
328
+ # 3. Run the Team
329
+ try:
330
+ response = await team.arun(
331
+ input=run_input.input_content,
332
+ images=run_input.images,
333
+ videos=run_input.videos,
334
+ audio=run_input.audios,
335
+ files=run_input.files,
336
+ session_id=context_id,
337
+ user_id=user_id,
338
+ **kwargs,
339
+ )
340
+
341
+ # 4. Send the response
342
+ a2a_task = map_run_output_to_a2a_task(response)
343
+ return SendMessageSuccessResponse(
344
+ id=request_body.get("id", "unknown"),
345
+ result=a2a_task,
346
+ )
347
+
348
+ # Handle all critical errors
349
+ except Exception as e:
350
+ from a2a.types import Message as A2AMessage
351
+ from a2a.types import Part, Role, TextPart
352
+
353
+ error_message = A2AMessage(
354
+ message_id=str(uuid4()),
355
+ role=Role.agent,
356
+ parts=[Part(root=TextPart(text=f"Error: {str(e)}"))],
357
+ context_id=context_id or str(uuid4()),
358
+ )
359
+ failed_task = Task(
360
+ id=str(uuid4()),
361
+ context_id=context_id or str(uuid4()),
362
+ status=TaskStatus(state=TaskState.failed),
363
+ history=[error_message],
364
+ )
365
+
366
+ return SendMessageSuccessResponse(
367
+ id=request_body.get("id", "unknown"),
368
+ result=failed_task,
369
+ )
370
+
371
+ @router.post(
372
+ "/teams/{id}/v1/message:stream",
373
+ operation_id="stream_message_team",
374
+ name="stream_message_team",
375
+ description="Stream a message to an Agno Team (streaming). The Team is identified via the path parameter '{id}'. "
376
+ "Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata. "
377
+ "Returns real-time updates as newline-delimited JSON (NDJSON).",
378
+ response_model_exclude_none=True,
379
+ responses={
380
+ 200: {
381
+ "description": "Streaming response with task updates",
382
+ "content": {
383
+ "text/event-stream": {
384
+ "example": 'event: TaskStatusUpdateEvent\ndata: {"jsonrpc":"2.0","id":"request-123","result":{"taskId":"task-456","status":"working"}}\n\n'
385
+ 'event: Message\ndata: {"jsonrpc":"2.0","id":"request-123","result":{"messageId":"msg-1","role":"agent","parts":[{"kind":"text","text":"Response"}]}}\n\n'
386
+ }
387
+ },
388
+ },
389
+ 400: {"description": "Invalid request"},
390
+ 404: {"description": "Team not found"},
391
+ },
392
+ )
393
+ async def a2a_stream_team(request: Request, id: str):
394
+ if not teams:
395
+ raise HTTPException(status_code=404, detail="Team not found")
396
+
397
+ # Load the request body. Unknown args are passed down as kwargs.
398
+ request_body = await request.json()
399
+ kwargs = await get_request_kwargs(request, a2a_stream_team)
400
+
401
+ # 1. Get the Team to run
402
+ team = get_team_by_id(id, teams)
403
+ if not team:
404
+ raise HTTPException(status_code=404, detail="Team not found")
405
+
406
+ # 2. Map the request to our run_input and run variables
407
+ run_input = await map_a2a_request_to_run_input(request_body, stream=True)
408
+ context_id = request_body.get("params", {}).get("message", {}).get("contextId")
409
+ user_id = request.headers.get("X-User-ID")
410
+ if not user_id:
411
+ user_id = request_body.get("params", {}).get("message", {}).get("metadata", {}).get("userId")
412
+
413
+ # 3. Run the Team and stream the response
414
+ try:
415
+ event_stream = team.arun(
416
+ input=run_input.input_content,
417
+ images=run_input.images,
418
+ videos=run_input.videos,
419
+ audio=run_input.audios,
420
+ files=run_input.files,
421
+ session_id=context_id,
422
+ user_id=user_id,
423
+ stream=True,
424
+ stream_events=True,
425
+ **kwargs,
426
+ )
427
+
428
+ # 4. Stream the response
429
+ return StreamingResponse(
430
+ stream_a2a_response_with_error_handling(event_stream=event_stream, request_id=request_body["id"]), # type: ignore[arg-type]
431
+ media_type="text/event-stream",
432
+ )
433
+
434
+ except Exception as e:
435
+ raise HTTPException(status_code=500, detail=f"Failed to start run: {str(e)}")
436
+
437
+ # ============= WORKFLOWS =============
438
+ @router.get("/workflows/{id}/.well-known/agent-card.json")
439
+ async def get_workflow_card(request: Request, id: str):
440
+ workflow = get_workflow_by_id(id, workflows)
441
+ if not workflow:
442
+ raise HTTPException(status_code=404, detail="Workflow not found")
443
+
444
+ base_url = str(request.base_url).rstrip("/")
445
+ skill = AgentSkill(
446
+ id=workflow.id or "",
447
+ name=workflow.name or "",
448
+ description=workflow.description or "",
449
+ tags=["agno"],
450
+ examples=["search", "ok"],
451
+ output_modes=["application/json"],
452
+ )
453
+ return AgentCard(
454
+ name=workflow.name or "",
455
+ version="1.0.0",
456
+ description=workflow.description or "",
457
+ url=f"{base_url}/a2a/workflows/{workflow.id}/v1/message:stream",
458
+ default_input_modes=["text"],
459
+ default_output_modes=["text"],
460
+ capabilities=AgentCapabilities(streaming=False, push_notifications=False, state_transition_history=False),
461
+ skills=[skill],
462
+ supports_authenticated_extended_card=False,
463
+ )
464
+
465
+ @router.post(
466
+ "/workflows/{id}/v1/message:send",
467
+ operation_id="run_message_workflow",
468
+ name="run_message_workflow",
469
+ description="Send a message to an Agno Workflow (non-streaming). The Workflow is identified via the path parameter '{id}'. "
470
+ "Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata.",
471
+ response_model_exclude_none=True,
472
+ responses={
473
+ 200: {
474
+ "description": "Message sent successfully",
475
+ "content": {
476
+ "application/json": {
477
+ "example": {
478
+ "jsonrpc": "2.0",
479
+ "id": "request-123",
480
+ "result": {
481
+ "task": {
482
+ "id": "task-456",
483
+ "context_id": "context-789",
484
+ "status": "completed",
485
+ "history": [
486
+ {
487
+ "message_id": "msg-1",
488
+ "role": "agent",
489
+ "parts": [{"kind": "text", "text": "Response from agent"}],
490
+ }
491
+ ],
492
+ }
493
+ },
494
+ }
495
+ }
496
+ },
497
+ },
498
+ 400: {"description": "Invalid request"},
499
+ 404: {"description": "Workflow not found"},
500
+ },
501
+ response_model=SendMessageSuccessResponse,
502
+ )
503
+ async def a2a_run_workflow(request: Request, id: str):
504
+ if not workflows:
505
+ raise HTTPException(status_code=404, detail="Workflow not found")
506
+
507
+ # Load the request body. Unknown args are passed down as kwargs.
508
+ request_body = await request.json()
509
+ kwargs = await get_request_kwargs(request, a2a_run_workflow)
510
+
511
+ # 1. Get the Workflow to run
512
+ workflow = get_workflow_by_id(id, workflows)
513
+ if not workflow:
514
+ raise HTTPException(status_code=404, detail="Workflow not found")
515
+
516
+ # 2. Map the request to our run_input and run variables
517
+ run_input = await map_a2a_request_to_run_input(request_body, stream=False)
518
+ context_id = request_body.get("params", {}).get("message", {}).get("contextId")
519
+ user_id = request.headers.get("X-User-ID")
520
+ if not user_id:
521
+ user_id = request_body.get("params", {}).get("message", {}).get("metadata", {}).get("userId")
522
+
523
+ # 3. Run the Workflow
524
+ try:
525
+ response = await workflow.arun(
526
+ input=run_input.input_content,
527
+ images=list(run_input.images) if run_input.images else None,
528
+ videos=list(run_input.videos) if run_input.videos else None,
529
+ audio=list(run_input.audios) if run_input.audios else None,
530
+ files=list(run_input.files) if run_input.files else None,
531
+ session_id=context_id,
532
+ user_id=user_id,
533
+ **kwargs,
534
+ )
535
+
536
+ # 4. Send the response
537
+ a2a_task = map_run_output_to_a2a_task(response)
538
+ return SendMessageSuccessResponse(
539
+ id=request_body.get("id", "unknown"),
540
+ result=a2a_task,
541
+ )
542
+
543
+ # Handle all critical errors
544
+ except Exception as e:
545
+ from a2a.types import Message as A2AMessage
546
+ from a2a.types import Part, Role, TextPart
547
+
548
+ error_message = A2AMessage(
549
+ message_id=str(uuid4()),
550
+ role=Role.agent,
551
+ parts=[Part(root=TextPart(text=f"Error: {str(e)}"))],
552
+ context_id=context_id or str(uuid4()),
553
+ )
554
+ failed_task = Task(
555
+ id=str(uuid4()),
556
+ context_id=context_id or str(uuid4()),
557
+ status=TaskStatus(state=TaskState.failed),
558
+ history=[error_message],
559
+ )
560
+
561
+ return SendMessageSuccessResponse(
562
+ id=request_body.get("id", "unknown"),
563
+ result=failed_task,
564
+ )
565
+
566
+ @router.post(
567
+ "/workflows/{id}/v1/message:stream",
568
+ operation_id="stream_message_workflow",
569
+ name="stream_message_workflow",
570
+ description="Stream a message to an Agno Workflow (streaming). The Workflow is identified via the path parameter '{id}'. "
571
+ "Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata. "
572
+ "Returns real-time updates as newline-delimited JSON (NDJSON).",
573
+ response_model_exclude_none=True,
574
+ responses={
575
+ 200: {
576
+ "description": "Streaming response with task updates",
577
+ "content": {
578
+ "text/event-stream": {
579
+ "example": 'event: TaskStatusUpdateEvent\ndata: {"jsonrpc":"2.0","id":"request-123","result":{"taskId":"task-456","status":"working"}}\n\n'
580
+ 'event: Message\ndata: {"jsonrpc":"2.0","id":"request-123","result":{"messageId":"msg-1","role":"agent","parts":[{"kind":"text","text":"Response"}]}}\n\n'
581
+ }
582
+ },
583
+ },
584
+ 400: {"description": "Invalid request"},
585
+ 404: {"description": "Workflow not found"},
586
+ },
587
+ )
588
+ async def a2a_stream_workflow(request: Request, id: str):
589
+ if not workflows:
590
+ raise HTTPException(status_code=404, detail="Workflow not found")
591
+
592
+ # Load the request body. Unknown args are passed down as kwargs.
593
+ request_body = await request.json()
594
+ kwargs = await get_request_kwargs(request, a2a_stream_workflow)
595
+
596
+ # 1. Get the Workflow to run
597
+ workflow = get_workflow_by_id(id, workflows)
598
+ if not workflow:
599
+ raise HTTPException(status_code=404, detail="Workflow not found")
600
+
601
+ # 2. Map the request to our run_input and run variables
602
+ run_input = await map_a2a_request_to_run_input(request_body, stream=True)
603
+ context_id = request_body.get("params", {}).get("message", {}).get("contextId")
604
+ user_id = request.headers.get("X-User-ID")
605
+ if not user_id:
606
+ user_id = request_body.get("params", {}).get("message", {}).get("metadata", {}).get("userId")
607
+
608
+ # 3. Run the Workflow and stream the response
609
+ try:
610
+ event_stream = workflow.arun(
611
+ input=run_input.input_content,
612
+ images=list(run_input.images) if run_input.images else None,
613
+ videos=list(run_input.videos) if run_input.videos else None,
614
+ audio=list(run_input.audios) if run_input.audios else None,
615
+ files=list(run_input.files) if run_input.files else None,
616
+ session_id=context_id,
617
+ user_id=user_id,
618
+ stream=True,
619
+ stream_events=True,
620
+ **kwargs,
621
+ )
622
+
623
+ # 4. Stream the response
624
+ return StreamingResponse(
625
+ stream_a2a_response_with_error_handling(event_stream=event_stream, request_id=request_body["id"]), # type: ignore[arg-type]
626
+ media_type="text/event-stream",
627
+ )
628
+
629
+ except Exception as e:
630
+ raise HTTPException(status_code=500, detail=f"Failed to start run: {str(e)}")
631
+
632
+ # ============= DEPRECATED ENDPOINTS =============
633
+
37
634
  @router.post(
38
635
  "/message/send",
39
636
  operation_id="send_message",
40
637
  name="send_message",
41
- description="Send a message to an Agno Agent, Team, or Workflow. "
638
+ description="[DEPRECATED] Send a message to an Agno Agent, Team, or Workflow. "
42
639
  "The Agent, Team or Workflow is identified via the 'agentId' field in params.message or X-Agent-ID header. "
43
640
  "Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata.",
44
641
  response_model_exclude_none=True,
@@ -74,8 +671,14 @@ def attach_routes(
74
671
  response_model=SendMessageSuccessResponse,
75
672
  )
76
673
  async def a2a_send_message(request: Request):
674
+ warnings.warn(
675
+ "This endpoint will be deprecated soon. Use /agents/{agents_id}/v1/message:send, /teams/{teams_id}/v1/message:send, or /workflows/{workflows_id}/v1/message:send instead.",
676
+ DeprecationWarning,
677
+ )
678
+
679
+ # Load the request body. Unknown args are passed down as kwargs.
77
680
  request_body = await request.json()
78
- kwargs = await _get_request_kwargs(request, a2a_send_message)
681
+ kwargs = await get_request_kwargs(request, a2a_send_message)
79
682
 
80
683
  # 1. Get the Agent, Team, or Workflow to run
81
684
  agent_id = request_body.get("params", {}).get("message", {}).get("agentId") or request.headers.get("X-Agent-ID")
@@ -84,7 +687,7 @@ def attach_routes(
84
687
  status_code=400,
85
688
  detail="Entity ID required. Provide it via 'agentId' in params.message or 'X-Agent-ID' header.",
86
689
  )
87
- entity: Optional[Union[Agent, Team, Workflow]] = None
690
+ entity: Optional[Union[Agent, RemoteAgent, Team, RemoteTeam, Workflow, RemoteWorkflow]] = None
88
691
  if agents:
89
692
  entity = get_agent_by_id(agent_id, agents)
90
693
  if not entity and teams:
@@ -104,7 +707,7 @@ def attach_routes(
104
707
  # 3. Run the agent, team, or workflow
105
708
  try:
106
709
  if isinstance(entity, Workflow):
107
- response = await entity.arun(
710
+ response = entity.arun(
108
711
  input=run_input.input_content,
109
712
  images=list(run_input.images) if run_input.images else None,
110
713
  videos=list(run_input.videos) if run_input.videos else None,
@@ -115,12 +718,12 @@ def attach_routes(
115
718
  **kwargs,
116
719
  )
117
720
  else:
118
- response = await entity.arun(
721
+ response = entity.arun(
119
722
  input=run_input.input_content,
120
- images=run_input.images,
121
- videos=run_input.videos,
122
- audio=run_input.audios,
123
- files=run_input.files,
723
+ images=run_input.images, # type: ignore
724
+ videos=run_input.videos, # type: ignore
725
+ audio=run_input.audios, # type: ignore
726
+ files=run_input.files, # type: ignore
124
727
  session_id=context_id,
125
728
  user_id=user_id,
126
729
  **kwargs,
@@ -160,7 +763,7 @@ def attach_routes(
160
763
  "/message/stream",
161
764
  operation_id="stream_message",
162
765
  name="stream_message",
163
- description="Stream a message to an Agno Agent, Team, or Workflow."
766
+ description="[DEPRECATED] Stream a message to an Agno Agent, Team, or Workflow. "
164
767
  "The Agent, Team or Workflow is identified via the 'agentId' field in params.message or X-Agent-ID header. "
165
768
  "Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata. "
166
769
  "Returns real-time updates as newline-delimited JSON (NDJSON).",
@@ -169,9 +772,9 @@ def attach_routes(
169
772
  200: {
170
773
  "description": "Streaming response with task updates",
171
774
  "content": {
172
- "application/x-ndjson": {
173
- "example": '{"jsonrpc":"2.0","id":"request-123","result":{"taskId":"task-456","status":"working"}}\n'
174
- '{"jsonrpc":"2.0","id":"request-123","result":{"messageId":"msg-1","role":"agent","parts":[{"kind":"text","text":"Response"}]}}\n'
775
+ "text/event-stream": {
776
+ "example": 'event: TaskStatusUpdateEvent\ndata: {"jsonrpc":"2.0","id":"request-123","result":{"taskId":"task-456","status":"working"}}\n\n'
777
+ 'event: Message\ndata: {"jsonrpc":"2.0","id":"request-123","result":{"messageId":"msg-1","role":"agent","parts":[{"kind":"text","text":"Response"}]}}\n\n'
175
778
  }
176
779
  },
177
780
  },
@@ -180,8 +783,14 @@ def attach_routes(
180
783
  },
181
784
  )
182
785
  async def a2a_stream_message(request: Request):
786
+ warnings.warn(
787
+ "This endpoint will be deprecated soon. Use /agents/{agents_id}/v1/message:stream, /teams/{teams_id}/v1/message:stream, or /workflows/{workflows_id}/v1/message:stream instead.",
788
+ DeprecationWarning,
789
+ )
790
+
791
+ # Load the request body. Unknown args are passed down as kwargs.
183
792
  request_body = await request.json()
184
- kwargs = await _get_request_kwargs(request, a2a_stream_message)
793
+ kwargs = await get_request_kwargs(request, a2a_stream_message)
185
794
 
186
795
  # 1. Get the Agent, Team, or Workflow to run
187
796
  agent_id = request_body.get("params", {}).get("message", {}).get("agentId")
@@ -192,7 +801,7 @@ def attach_routes(
192
801
  status_code=400,
193
802
  detail="Entity ID required. Provide 'agentId' in params.message or 'X-Agent-ID' header.",
194
803
  )
195
- entity: Optional[Union[Agent, Team, Workflow]] = None
804
+ entity: Optional[Union[Agent, RemoteAgent, Team, RemoteTeam, Workflow, RemoteWorkflow]] = None
196
805
  if agents:
197
806
  entity = get_agent_by_id(agent_id, agents)
198
807
  if not entity and teams:
@@ -225,7 +834,7 @@ def attach_routes(
225
834
  **kwargs,
226
835
  )
227
836
  else:
228
- event_stream = entity.arun( # type: ignore[assignment]
837
+ event_stream = entity.arun( # type: ignore
229
838
  input=run_input.input_content,
230
839
  images=run_input.images,
231
840
  videos=run_input.videos,
@@ -241,7 +850,7 @@ def attach_routes(
241
850
  # 4. Stream the response
242
851
  return StreamingResponse(
243
852
  stream_a2a_response_with_error_handling(event_stream=event_stream, request_id=request_body["id"]), # type: ignore[arg-type]
244
- media_type="application/x-ndjson",
853
+ media_type="text/event-stream",
245
854
  )
246
855
 
247
856
  except Exception as e: