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
@@ -95,7 +95,6 @@ async def map_a2a_request_to_run_input(request_body: dict, stream: bool = True)
95
95
  ```json
96
96
  {
97
97
  "jsonrpc": "2.0",
98
- "method": "message/send",
99
98
  "id": "id",
100
99
  "params": {
101
100
  "message": {
@@ -110,7 +109,7 @@ async def map_a2a_request_to_run_input(request_body: dict, stream: bool = True)
110
109
 
111
110
  Returns:
112
111
  RunInput: The Agno RunInput
113
- stream: Wheter we are in stream mode
112
+ stream: Whether we are in stream mode
114
113
  """
115
114
 
116
115
  # 1. Validate the request
@@ -325,7 +324,7 @@ async def stream_a2a_response(
325
324
  final=False,
326
325
  )
327
326
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
328
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
327
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
329
328
 
330
329
  # 2. Send all content and secondary events
331
330
 
@@ -341,7 +340,7 @@ async def stream_a2a_response(
341
340
  metadata={"agno_content_category": "content"},
342
341
  )
343
342
  response = SendStreamingMessageSuccessResponse(id=request_id, result=message)
344
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
343
+ yield f"event: Message\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
345
344
 
346
345
  # Send tool call events
347
346
  elif isinstance(event, (ToolCallStartedEvent, TeamToolCallStartedEvent)):
@@ -361,7 +360,7 @@ async def stream_a2a_response(
361
360
  metadata=metadata,
362
361
  )
363
362
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
364
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
363
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
365
364
 
366
365
  elif isinstance(event, (ToolCallCompletedEvent, TeamToolCallCompletedEvent)):
367
366
  metadata = {"agno_event_type": "tool_call_completed"}
@@ -380,7 +379,7 @@ async def stream_a2a_response(
380
379
  metadata=metadata,
381
380
  )
382
381
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
383
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
382
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
384
383
 
385
384
  # Send reasoning events
386
385
  elif isinstance(event, (ReasoningStartedEvent, TeamReasoningStartedEvent)):
@@ -392,7 +391,7 @@ async def stream_a2a_response(
392
391
  metadata={"agno_event_type": "reasoning_started"},
393
392
  )
394
393
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
395
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
394
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
396
395
 
397
396
  elif isinstance(event, (ReasoningStepEvent, TeamReasoningStepEvent)):
398
397
  if event.reasoning_content:
@@ -415,7 +414,7 @@ async def stream_a2a_response(
415
414
  metadata={"agno_content_category": "reasoning", "agno_event_type": "reasoning_step"},
416
415
  )
417
416
  response = SendStreamingMessageSuccessResponse(id=request_id, result=reasoning_message)
418
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
417
+ yield f"event: Message\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
419
418
 
420
419
  elif isinstance(event, (ReasoningCompletedEvent, TeamReasoningCompletedEvent)):
421
420
  status_event = TaskStatusUpdateEvent(
@@ -426,7 +425,7 @@ async def stream_a2a_response(
426
425
  metadata={"agno_event_type": "reasoning_completed"},
427
426
  )
428
427
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
429
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
428
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
430
429
 
431
430
  # Send memory update events
432
431
  elif isinstance(event, (MemoryUpdateStartedEvent, TeamMemoryUpdateStartedEvent)):
@@ -438,7 +437,7 @@ async def stream_a2a_response(
438
437
  metadata={"agno_event_type": "memory_update_started"},
439
438
  )
440
439
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
441
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
440
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
442
441
 
443
442
  elif isinstance(event, (MemoryUpdateCompletedEvent, TeamMemoryUpdateCompletedEvent)):
444
443
  status_event = TaskStatusUpdateEvent(
@@ -449,7 +448,7 @@ async def stream_a2a_response(
449
448
  metadata={"agno_event_type": "memory_update_completed"},
450
449
  )
451
450
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
452
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
451
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
453
452
 
454
453
  # Send workflow events
455
454
  elif isinstance(event, WorkflowStepStartedEvent):
@@ -465,7 +464,7 @@ async def stream_a2a_response(
465
464
  metadata=metadata,
466
465
  )
467
466
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
468
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
467
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
469
468
 
470
469
  elif isinstance(event, WorkflowStepCompletedEvent):
471
470
  metadata = {"agno_event_type": "workflow_step_completed"}
@@ -480,7 +479,7 @@ async def stream_a2a_response(
480
479
  metadata=metadata,
481
480
  )
482
481
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
483
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
482
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
484
483
 
485
484
  elif isinstance(event, WorkflowStepErrorEvent):
486
485
  metadata = {"agno_event_type": "workflow_step_error"}
@@ -497,7 +496,7 @@ async def stream_a2a_response(
497
496
  metadata=metadata,
498
497
  )
499
498
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
500
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
499
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
501
500
 
502
501
  # Send loop events
503
502
  elif isinstance(event, LoopExecutionStartedEvent):
@@ -515,7 +514,7 @@ async def stream_a2a_response(
515
514
  metadata=metadata,
516
515
  )
517
516
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
518
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
517
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
519
518
 
520
519
  elif isinstance(event, LoopIterationStartedEvent):
521
520
  metadata = {"agno_event_type": "loop_iteration_started"}
@@ -534,7 +533,7 @@ async def stream_a2a_response(
534
533
  metadata=metadata,
535
534
  )
536
535
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
537
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
536
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
538
537
 
539
538
  elif isinstance(event, LoopIterationCompletedEvent):
540
539
  metadata = {"agno_event_type": "loop_iteration_completed"}
@@ -553,7 +552,7 @@ async def stream_a2a_response(
553
552
  metadata=metadata,
554
553
  )
555
554
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
556
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
555
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
557
556
 
558
557
  elif isinstance(event, LoopExecutionCompletedEvent):
559
558
  metadata = {"agno_event_type": "loop_execution_completed"}
@@ -570,7 +569,7 @@ async def stream_a2a_response(
570
569
  metadata=metadata,
571
570
  )
572
571
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
573
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
572
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
574
573
 
575
574
  # Send parallel events
576
575
  elif isinstance(event, ParallelExecutionStartedEvent):
@@ -588,7 +587,7 @@ async def stream_a2a_response(
588
587
  metadata=metadata,
589
588
  )
590
589
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
591
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
590
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
592
591
 
593
592
  elif isinstance(event, ParallelExecutionCompletedEvent):
594
593
  metadata = {"agno_event_type": "parallel_execution_completed"}
@@ -605,7 +604,7 @@ async def stream_a2a_response(
605
604
  metadata=metadata,
606
605
  )
607
606
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
608
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
607
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
609
608
 
610
609
  # Send condition events
611
610
  elif isinstance(event, ConditionExecutionStartedEvent):
@@ -623,7 +622,7 @@ async def stream_a2a_response(
623
622
  metadata=metadata,
624
623
  )
625
624
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
626
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
625
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
627
626
 
628
627
  elif isinstance(event, ConditionExecutionCompletedEvent):
629
628
  metadata = {"agno_event_type": "condition_execution_completed"}
@@ -642,7 +641,7 @@ async def stream_a2a_response(
642
641
  metadata=metadata,
643
642
  )
644
643
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
645
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
644
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
646
645
 
647
646
  # Send router events
648
647
  elif isinstance(event, RouterExecutionStartedEvent):
@@ -660,7 +659,7 @@ async def stream_a2a_response(
660
659
  metadata=metadata,
661
660
  )
662
661
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
663
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
662
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
664
663
 
665
664
  elif isinstance(event, RouterExecutionCompletedEvent):
666
665
  metadata = {"agno_event_type": "router_execution_completed"}
@@ -679,7 +678,7 @@ async def stream_a2a_response(
679
678
  metadata=metadata,
680
679
  )
681
680
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
682
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
681
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
683
682
 
684
683
  # Send steps events
685
684
  elif isinstance(event, StepsExecutionStartedEvent):
@@ -697,7 +696,7 @@ async def stream_a2a_response(
697
696
  metadata=metadata,
698
697
  )
699
698
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
700
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
699
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
701
700
 
702
701
  elif isinstance(event, StepsExecutionCompletedEvent):
703
702
  metadata = {"agno_event_type": "steps_execution_completed"}
@@ -716,7 +715,7 @@ async def stream_a2a_response(
716
715
  metadata=metadata,
717
716
  )
718
717
  response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
719
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
718
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
720
719
 
721
720
  # Capture completion event for final task construction
722
721
  elif isinstance(event, (RunCompletedEvent, TeamRunCompletedEvent, WorkflowCompletedEvent)):
@@ -748,7 +747,7 @@ async def stream_a2a_response(
748
747
  final=True,
749
748
  )
750
749
  response = SendStreamingMessageSuccessResponse(id=request_id, result=final_status_event)
751
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
750
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
752
751
 
753
752
  # 4. Send final task
754
753
  # Handle cancelled case
@@ -778,7 +777,7 @@ async def stream_a2a_response(
778
777
  history=[final_message],
779
778
  )
780
779
  response = SendStreamingMessageSuccessResponse(id=request_id, result=task)
781
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
780
+ yield f"event: Task\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
782
781
  return
783
782
 
784
783
  # Build from completion_event if available, otherwise use accumulated content
@@ -846,8 +845,8 @@ async def stream_a2a_response(
846
845
 
847
846
  # Handle all other data as Message metadata
848
847
  final_metadata: Dict[str, Any] = {}
849
- if hasattr(completion_event, "metrics") and completion_event.metrics:
850
- final_metadata["metrics"] = completion_event.metrics.__dict__
848
+ if hasattr(completion_event, "metrics") and completion_event.metrics: # type: ignore
849
+ final_metadata["metrics"] = completion_event.metrics.to_dict() # type: ignore
851
850
  if hasattr(completion_event, "metadata") and completion_event.metadata:
852
851
  final_metadata.update(completion_event.metadata)
853
852
 
@@ -880,7 +879,7 @@ async def stream_a2a_response(
880
879
  artifacts=artifacts if artifacts else None,
881
880
  )
882
881
  response = SendStreamingMessageSuccessResponse(id=request_id, result=task)
883
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
882
+ yield f"event: Task\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
884
883
 
885
884
 
886
885
  async def stream_a2a_response_with_error_handling(
@@ -904,7 +903,7 @@ async def stream_a2a_response_with_error_handling(
904
903
  final=True,
905
904
  )
906
905
  response = SendStreamingMessageSuccessResponse(id=request_id, result=failed_status_event)
907
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
906
+ yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
908
907
 
909
908
  # Send failed Task
910
909
  error_message = A2AMessage(
@@ -921,4 +920,4 @@ async def stream_a2a_response_with_error_handling(
921
920
  )
922
921
 
923
922
  response = SendStreamingMessageSuccessResponse(id=request_id, result=failed_task)
924
- yield json.dumps(response.model_dump(exclude_none=True)) + "\n"
923
+ yield f"event: Task\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
@@ -1,13 +1,15 @@
1
1
  """Main class for the AG-UI app, used to expose an Agno Agent or Team in an AG-UI compatible format."""
2
2
 
3
- from typing import List, Optional
3
+ from typing import List, Optional, Union
4
4
 
5
5
  from fastapi.routing import APIRouter
6
6
 
7
7
  from agno.agent import Agent
8
+ from agno.agent.remote import RemoteAgent
8
9
  from agno.os.interfaces.agui.router import attach_routes
9
10
  from agno.os.interfaces.base import BaseInterface
10
11
  from agno.team import Team
12
+ from agno.team.remote import RemoteTeam
11
13
 
12
14
 
13
15
  class AGUI(BaseInterface):
@@ -17,8 +19,8 @@ class AGUI(BaseInterface):
17
19
 
18
20
  def __init__(
19
21
  self,
20
- agent: Optional[Agent] = None,
21
- team: Optional[Team] = None,
22
+ agent: Optional[Union[Agent, RemoteAgent]] = None,
23
+ team: Optional[Union[Team, RemoteTeam]] = None,
22
24
  prefix: str = "",
23
25
  tags: Optional[List[str]] = None,
24
26
  ):
@@ -2,37 +2,43 @@
2
2
 
3
3
  import logging
4
4
  import uuid
5
- from typing import AsyncIterator, Optional
6
-
7
- from ag_ui.core import (
8
- BaseEvent,
9
- EventType,
10
- RunAgentInput,
11
- RunErrorEvent,
12
- RunStartedEvent,
13
- )
14
- from ag_ui.encoder import EventEncoder
5
+ from typing import AsyncIterator, Optional, Union
6
+
7
+ try:
8
+ from ag_ui.core import (
9
+ BaseEvent,
10
+ EventType,
11
+ RunAgentInput,
12
+ RunErrorEvent,
13
+ RunStartedEvent,
14
+ )
15
+ from ag_ui.encoder import EventEncoder
16
+ except ImportError as e:
17
+ raise ImportError("`ag_ui` not installed. Please install it with `pip install -U ag-ui`") from e
18
+
15
19
  from fastapi import APIRouter
16
20
  from fastapi.responses import StreamingResponse
17
21
 
18
- from agno.agent.agent import Agent
22
+ from agno.agent import Agent, RemoteAgent
19
23
  from agno.os.interfaces.agui.utils import (
20
24
  async_stream_agno_response_as_agui_events,
21
25
  convert_agui_messages_to_agno_messages,
22
26
  validate_agui_state,
23
27
  )
28
+ from agno.team.remote import RemoteTeam
24
29
  from agno.team.team import Team
25
30
 
26
31
  logger = logging.getLogger(__name__)
27
32
 
28
33
 
29
- async def run_agent(agent: Agent, run_input: RunAgentInput) -> AsyncIterator[BaseEvent]:
34
+ async def run_agent(agent: Union[Agent, RemoteAgent], run_input: RunAgentInput) -> AsyncIterator[BaseEvent]:
30
35
  """Run the contextual Agent, mapping AG-UI input messages to Agno format, and streaming the response in AG-UI format."""
31
36
  run_id = run_input.run_id or str(uuid.uuid4())
32
37
 
33
38
  try:
34
39
  # Preparing the input for the Agent and emitting the run started event
35
40
  messages = convert_agui_messages_to_agno_messages(run_input.messages or [])
41
+
36
42
  yield RunStartedEvent(type=EventType.RUN_STARTED, thread_id=run_input.thread_id, run_id=run_id)
37
43
 
38
44
  # Look for user_id in run_input.forwarded_props
@@ -44,13 +50,14 @@ async def run_agent(agent: Agent, run_input: RunAgentInput) -> AsyncIterator[Bas
44
50
  session_state = validate_agui_state(run_input.state, run_input.thread_id)
45
51
 
46
52
  # Request streaming response from agent
47
- response_stream = agent.arun(
53
+ response_stream = agent.arun( # type: ignore
48
54
  input=messages,
49
55
  session_id=run_input.thread_id,
50
56
  stream=True,
51
57
  stream_events=True,
52
58
  user_id=user_id,
53
59
  session_state=session_state,
60
+ run_id=run_id,
54
61
  )
55
62
 
56
63
  # Stream the response content in AG-UI format
@@ -67,7 +74,7 @@ async def run_agent(agent: Agent, run_input: RunAgentInput) -> AsyncIterator[Bas
67
74
  yield RunErrorEvent(type=EventType.RUN_ERROR, message=str(e))
68
75
 
69
76
 
70
- async def run_team(team: Team, input: RunAgentInput) -> AsyncIterator[BaseEvent]:
77
+ async def run_team(team: Union[Team, RemoteTeam], input: RunAgentInput) -> AsyncIterator[BaseEvent]:
71
78
  """Run the contextual Team, mapping AG-UI input messages to Agno format, and streaming the response in AG-UI format."""
72
79
  run_id = input.run_id or str(uuid.uuid4())
73
80
  try:
@@ -84,13 +91,14 @@ async def run_team(team: Team, input: RunAgentInput) -> AsyncIterator[BaseEvent]
84
91
  session_state = validate_agui_state(input.state, input.thread_id)
85
92
 
86
93
  # Request streaming response from team
87
- response_stream = team.arun(
94
+ response_stream = team.arun( # type: ignore
88
95
  input=messages,
89
96
  session_id=input.thread_id,
90
97
  stream=True,
91
98
  stream_steps=True,
92
99
  user_id=user_id,
93
100
  session_state=session_state,
101
+ run_id=run_id,
94
102
  )
95
103
 
96
104
  # Stream the response content in AG-UI format
@@ -104,7 +112,9 @@ async def run_team(team: Team, input: RunAgentInput) -> AsyncIterator[BaseEvent]
104
112
  yield RunErrorEvent(type=EventType.RUN_ERROR, message=str(e))
105
113
 
106
114
 
107
- def attach_routes(router: APIRouter, agent: Optional[Agent] = None, team: Optional[Team] = None) -> APIRouter:
115
+ def attach_routes(
116
+ router: APIRouter, agent: Optional[Union[Agent, RemoteAgent]] = None, team: Optional[Union[Team, RemoteTeam]] = None
117
+ ) -> APIRouter:
108
118
  if agent is None and team is None:
109
119
  raise ValueError("Either agent or team must be provided.")
110
120
 
@@ -28,7 +28,7 @@ from agno.models.message import Message
28
28
  from agno.run.agent import RunContentEvent, RunEvent, RunOutputEvent, RunPausedEvent
29
29
  from agno.run.team import RunContentEvent as TeamRunContentEvent
30
30
  from agno.run.team import TeamRunEvent, TeamRunOutputEvent
31
- from agno.utils.log import log_warning
31
+ from agno.utils.log import log_debug, log_warning
32
32
  from agno.utils.message import get_text_from_message
33
33
 
34
34
 
@@ -116,23 +116,43 @@ class EventBuffer:
116
116
 
117
117
  def convert_agui_messages_to_agno_messages(messages: List[AGUIMessage]) -> List[Message]:
118
118
  """Convert AG-UI messages to Agno messages."""
119
- result = []
119
+ # First pass: collect all tool_call_ids that have results
120
+ tool_call_ids_with_results: Set[str] = set()
121
+ for msg in messages:
122
+ if msg.role == "tool" and msg.tool_call_id:
123
+ tool_call_ids_with_results.add(msg.tool_call_id)
124
+
125
+ # Second pass: convert messages
126
+ result: List[Message] = []
127
+ seen_tool_call_ids: Set[str] = set()
128
+
120
129
  for msg in messages:
121
130
  if msg.role == "tool":
131
+ # Deduplicate tool results - keep only first occurrence
132
+ if msg.tool_call_id in seen_tool_call_ids:
133
+ log_debug(f"Skipping duplicate AGUI tool result: {msg.tool_call_id}")
134
+ continue
135
+ seen_tool_call_ids.add(msg.tool_call_id)
122
136
  result.append(Message(role="tool", tool_call_id=msg.tool_call_id, content=msg.content))
137
+
123
138
  elif msg.role == "assistant":
124
139
  tool_calls = None
125
140
  if msg.tool_calls:
126
- tool_calls = [call.model_dump() for call in msg.tool_calls]
127
- result.append(
128
- Message(
129
- role="assistant",
130
- content=msg.content,
131
- tool_calls=tool_calls,
132
- )
133
- )
141
+ # Filter tool_calls to only those with results in this message sequence
142
+ filtered_calls = [call for call in msg.tool_calls if call.id in tool_call_ids_with_results]
143
+ if filtered_calls:
144
+ tool_calls = [call.model_dump() for call in filtered_calls]
145
+ result.append(Message(role="assistant", content=msg.content, tool_calls=tool_calls))
146
+
134
147
  elif msg.role == "user":
135
148
  result.append(Message(role="user", content=msg.content))
149
+
150
+ elif msg.role == "system":
151
+ pass # Skip - agent builds its own system message from configuration
152
+
153
+ else:
154
+ log_warning(f"Unknown AGUI message role: {msg.role}")
155
+
136
156
  return result
137
157
 
138
158
 
@@ -250,7 +270,25 @@ def _create_events_from_chunk(
250
270
  parent_message_id = event_buffer.get_parent_message_id_for_tool_call()
251
271
 
252
272
  if not parent_message_id:
253
- parent_message_id = current_message_id
273
+ # Create parent message for tool calls without preceding assistant message
274
+ parent_message_id = str(uuid.uuid4())
275
+
276
+ # Emit a text message to serve as the parent
277
+ text_start = TextMessageStartEvent(
278
+ type=EventType.TEXT_MESSAGE_START,
279
+ message_id=parent_message_id,
280
+ role="assistant",
281
+ )
282
+ events_to_emit.append(text_start)
283
+
284
+ text_end = TextMessageEndEvent(
285
+ type=EventType.TEXT_MESSAGE_END,
286
+ message_id=parent_message_id,
287
+ )
288
+ events_to_emit.append(text_end)
289
+
290
+ # Set this as the pending parent for subsequent tool calls in this batch
291
+ event_buffer.set_pending_tool_calls_parent_id(parent_message_id)
254
292
 
255
293
  start_event = ToolCallStartEvent(
256
294
  type=EventType.TOOL_CALL_START,
@@ -341,58 +379,60 @@ def _create_completion_events(
341
379
  end_message_event = TextMessageEndEvent(type=EventType.TEXT_MESSAGE_END, message_id=message_id)
342
380
  events_to_emit.append(end_message_event)
343
381
 
344
- # emit frontend tool calls, i.e. external_execution=True
345
- if isinstance(chunk, RunPausedEvent) and chunk.tools is not None:
346
- # First, emit an assistant message for external tool calls
347
- assistant_message_id = str(uuid.uuid4())
348
- assistant_start_event = TextMessageStartEvent(
349
- type=EventType.TEXT_MESSAGE_START,
350
- message_id=assistant_message_id,
351
- role="assistant",
352
- )
353
- events_to_emit.append(assistant_start_event)
354
-
355
- # Add any text content if present for the assistant message
356
- if chunk.content:
357
- content_event = TextMessageContentEvent(
358
- type=EventType.TEXT_MESSAGE_CONTENT,
382
+ # Emit external execution tools
383
+ if isinstance(chunk, RunPausedEvent):
384
+ external_tools = chunk.tools_awaiting_external_execution
385
+ if external_tools:
386
+ # First, emit an assistant message for external tool calls
387
+ assistant_message_id = str(uuid.uuid4())
388
+ assistant_start_event = TextMessageStartEvent(
389
+ type=EventType.TEXT_MESSAGE_START,
359
390
  message_id=assistant_message_id,
360
- delta=str(chunk.content),
391
+ role="assistant",
361
392
  )
362
- events_to_emit.append(content_event)
363
-
364
- # End the assistant message
365
- assistant_end_event = TextMessageEndEvent(
366
- type=EventType.TEXT_MESSAGE_END,
367
- message_id=assistant_message_id,
368
- )
369
- events_to_emit.append(assistant_end_event)
370
-
371
- # Now emit the tool call events with the assistant message as parent
372
- for tool in chunk.tools:
373
- if tool.tool_call_id is None or tool.tool_name is None:
374
- continue
393
+ events_to_emit.append(assistant_start_event)
394
+
395
+ # Add any text content if present for the assistant message
396
+ if chunk.content:
397
+ content_event = TextMessageContentEvent(
398
+ type=EventType.TEXT_MESSAGE_CONTENT,
399
+ message_id=assistant_message_id,
400
+ delta=str(chunk.content),
401
+ )
402
+ events_to_emit.append(content_event)
375
403
 
376
- start_event = ToolCallStartEvent(
377
- type=EventType.TOOL_CALL_START,
378
- tool_call_id=tool.tool_call_id,
379
- tool_call_name=tool.tool_name,
380
- parent_message_id=assistant_message_id, # Use the assistant message as parent
404
+ # End the assistant message
405
+ assistant_end_event = TextMessageEndEvent(
406
+ type=EventType.TEXT_MESSAGE_END,
407
+ message_id=assistant_message_id,
381
408
  )
382
- events_to_emit.append(start_event)
409
+ events_to_emit.append(assistant_end_event)
410
+
411
+ # Emit tool call events for external execution
412
+ for tool in external_tools:
413
+ if tool.tool_call_id is None or tool.tool_name is None:
414
+ continue
415
+
416
+ start_event = ToolCallStartEvent(
417
+ type=EventType.TOOL_CALL_START,
418
+ tool_call_id=tool.tool_call_id,
419
+ tool_call_name=tool.tool_name,
420
+ parent_message_id=assistant_message_id, # Use the assistant message as parent
421
+ )
422
+ events_to_emit.append(start_event)
383
423
 
384
- args_event = ToolCallArgsEvent(
385
- type=EventType.TOOL_CALL_ARGS,
386
- tool_call_id=tool.tool_call_id,
387
- delta=json.dumps(tool.tool_args),
388
- )
389
- events_to_emit.append(args_event)
424
+ args_event = ToolCallArgsEvent(
425
+ type=EventType.TOOL_CALL_ARGS,
426
+ tool_call_id=tool.tool_call_id,
427
+ delta=json.dumps(tool.tool_args),
428
+ )
429
+ events_to_emit.append(args_event)
390
430
 
391
- end_event = ToolCallEndEvent(
392
- type=EventType.TOOL_CALL_END,
393
- tool_call_id=tool.tool_call_id,
394
- )
395
- events_to_emit.append(end_event)
431
+ end_event = ToolCallEndEvent(
432
+ type=EventType.TOOL_CALL_END,
433
+ tool_call_id=tool.tool_call_id,
434
+ )
435
+ events_to_emit.append(end_event)
396
436
 
397
437
  run_finished_event = RunFinishedEvent(type=EventType.RUN_FINISHED, thread_id=thread_id, run_id=run_id)
398
438
  events_to_emit.append(run_finished_event)
@@ -1,19 +1,19 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import List, Optional
2
+ from typing import List, Optional, Union
3
3
 
4
4
  from fastapi import APIRouter
5
5
 
6
- from agno.agent import Agent
7
- from agno.team import Team
8
- from agno.workflow.workflow import Workflow
6
+ from agno.agent import Agent, RemoteAgent
7
+ from agno.team import RemoteTeam, Team
8
+ from agno.workflow import RemoteWorkflow, Workflow
9
9
 
10
10
 
11
11
  class BaseInterface(ABC):
12
12
  type: str
13
13
  version: str = "1.0"
14
- agent: Optional[Agent] = None
15
- team: Optional[Team] = None
16
- workflow: Optional[Workflow] = None
14
+ agent: Optional[Union[Agent, RemoteAgent]] = None
15
+ team: Optional[Union[Team, RemoteTeam]] = None
16
+ workflow: Optional[Union[Workflow, RemoteWorkflow]] = None
17
17
 
18
18
  prefix: str
19
19
  tags: List[str]