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
@@ -0,0 +1,362 @@
1
+ import time
2
+ from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, List, Literal, Optional, Tuple, Union, overload
3
+
4
+ from fastapi import WebSocket
5
+ from pydantic import BaseModel
6
+
7
+ from agno.media import Audio, File, Image, Video
8
+ from agno.models.message import Message
9
+ from agno.remote.base import BaseRemote, RemoteDb
10
+ from agno.run.workflow import WorkflowRunOutput, WorkflowRunOutputEvent
11
+ from agno.utils.agent import validate_input
12
+ from agno.utils.remote import serialize_input
13
+
14
+ if TYPE_CHECKING:
15
+ from agno.os.routers.workflows.schema import WorkflowResponse
16
+
17
+
18
+ class RemoteWorkflow(BaseRemote):
19
+ # Private cache for workflow config with TTL: (config, timestamp)
20
+ _cached_workflow_config: Optional[Tuple["WorkflowResponse", float]] = None
21
+
22
+ def __init__(
23
+ self,
24
+ base_url: str,
25
+ workflow_id: str,
26
+ timeout: float = 300.0,
27
+ protocol: Literal["agentos", "a2a"] = "agentos",
28
+ a2a_protocol: Literal["json-rpc", "rest"] = "rest",
29
+ config_ttl: float = 300.0,
30
+ ):
31
+ """Initialize RemoteWorkflow for remote execution.
32
+
33
+ Supports two protocols:
34
+ - "agentos": Agno's proprietary AgentOS REST API (default)
35
+ - "a2a": A2A (Agent-to-Agent) protocol for cross-framework communication
36
+
37
+ Args:
38
+ base_url: Base URL for remote instance (e.g., "http://localhost:7777")
39
+ workflow_id: ID of remote workflow on the remote server
40
+ timeout: Request timeout in seconds (default: 300)
41
+ protocol: Communication protocol - "agentos" (default) or "a2a"
42
+ a2a_protocol: For A2A protocol only - Whether to use JSON-RPC or REST protocol.
43
+ config_ttl: Time-to-live for cached config in seconds (default: 300)
44
+ """
45
+ super().__init__(base_url, timeout, protocol, a2a_protocol, config_ttl)
46
+ self.workflow_id = workflow_id
47
+ self._cached_workflow_config = None
48
+ self._config_ttl = config_ttl
49
+
50
+ @property
51
+ def id(self) -> str:
52
+ return self.workflow_id
53
+
54
+ async def get_workflow_config(self) -> "WorkflowResponse":
55
+ """Get the workflow config from remote (always fetches fresh)."""
56
+ from agno.os.routers.workflows.schema import WorkflowResponse
57
+
58
+ if self.protocol == "a2a":
59
+ from agno.client.a2a.schemas import AgentCard
60
+
61
+ agent_card: Optional[AgentCard] = await self.a2a_client.aget_agent_card() # type: ignore
62
+
63
+ return WorkflowResponse(
64
+ id=self.workflow_id,
65
+ name=agent_card.name if agent_card else self.workflow_id,
66
+ description=agent_card.description if agent_card else f"A2A workflow: {self.workflow_id}",
67
+ )
68
+
69
+ # AgentOS protocol: fetch fresh config from remote
70
+ return await self.agentos_client.aget_workflow(self.workflow_id) # type: ignore
71
+
72
+ @property
73
+ def _workflow_config(self) -> "WorkflowResponse":
74
+ """Get the workflow config from remote, cached with TTL."""
75
+ from agno.os.routers.workflows.schema import WorkflowResponse
76
+
77
+ if self.protocol == "a2a":
78
+ from agno.client.a2a.schemas import AgentCard
79
+
80
+ agent_card: Optional[AgentCard] = self.a2a_client.get_agent_card() # type: ignore
81
+
82
+ return WorkflowResponse(
83
+ id=self.workflow_id,
84
+ name=agent_card.name if agent_card else self.workflow_id,
85
+ description=agent_card.description if agent_card else f"A2A workflow: {self.workflow_id}",
86
+ )
87
+
88
+ current_time = time.time()
89
+
90
+ # Check if cache is valid
91
+ if self._cached_workflow_config is not None:
92
+ config, cached_at = self._cached_workflow_config
93
+ if current_time - cached_at < self.config_ttl:
94
+ return config
95
+
96
+ # Fetch fresh config
97
+ config: WorkflowResponse = self.agentos_client.get_workflow(self.workflow_id) # type: ignore
98
+ self._cached_workflow_config = (config, current_time)
99
+ return config
100
+
101
+ async def refresh_config(self) -> "WorkflowResponse":
102
+ """Force refresh the cached workflow config."""
103
+ from agno.os.routers.workflows.schema import WorkflowResponse
104
+
105
+ config: WorkflowResponse = await self.agentos_client.aget_workflow(self.workflow_id) # type: ignore
106
+ self._cached_workflow_config = (config, time.time())
107
+ return config
108
+
109
+ @property
110
+ def name(self) -> Optional[str]:
111
+ if self._workflow_config is not None:
112
+ return self._workflow_config.name
113
+ return None
114
+
115
+ @property
116
+ def description(self) -> Optional[str]:
117
+ if self._workflow_config is not None:
118
+ return self._workflow_config.description
119
+ return None
120
+
121
+ @property
122
+ def db(self) -> Optional[RemoteDb]:
123
+ if (
124
+ self.agentos_client
125
+ and self._config
126
+ and self._workflow_config is not None
127
+ and self._workflow_config.db_id is not None
128
+ ):
129
+ return RemoteDb.from_config(
130
+ db_id=self._workflow_config.db_id,
131
+ client=self.agentos_client,
132
+ config=self._config,
133
+ )
134
+ return None
135
+
136
+ @overload
137
+ async def arun(
138
+ self,
139
+ input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]]] = None,
140
+ additional_data: Optional[Dict[str, Any]] = None,
141
+ user_id: Optional[str] = None,
142
+ run_id: Optional[str] = None,
143
+ session_id: Optional[str] = None,
144
+ session_state: Optional[Dict[str, Any]] = None,
145
+ audio: Optional[List[Audio]] = None,
146
+ images: Optional[List[Image]] = None,
147
+ videos: Optional[List[Video]] = None,
148
+ files: Optional[List[File]] = None,
149
+ stream: Literal[False] = False,
150
+ stream_events: Optional[bool] = None,
151
+ stream_intermediate_steps: Optional[bool] = None,
152
+ background: Optional[bool] = False,
153
+ websocket: Optional[WebSocket] = None,
154
+ background_tasks: Optional[Any] = None,
155
+ auth_token: Optional[str] = None,
156
+ ) -> WorkflowRunOutput: ...
157
+
158
+ @overload
159
+ def arun(
160
+ self,
161
+ input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]]] = None,
162
+ additional_data: Optional[Dict[str, Any]] = None,
163
+ user_id: Optional[str] = None,
164
+ run_id: Optional[str] = None,
165
+ session_id: Optional[str] = None,
166
+ session_state: Optional[Dict[str, Any]] = None,
167
+ audio: Optional[List[Audio]] = None,
168
+ images: Optional[List[Image]] = None,
169
+ videos: Optional[List[Video]] = None,
170
+ files: Optional[List[File]] = None,
171
+ stream: Literal[True] = True,
172
+ stream_events: Optional[bool] = None,
173
+ stream_intermediate_steps: Optional[bool] = None,
174
+ background: Optional[bool] = False,
175
+ websocket: Optional[WebSocket] = None,
176
+ background_tasks: Optional[Any] = None,
177
+ auth_token: Optional[str] = None,
178
+ ) -> AsyncIterator[WorkflowRunOutputEvent]: ...
179
+
180
+ def arun( # type: ignore
181
+ self,
182
+ input: Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]],
183
+ additional_data: Optional[Dict[str, Any]] = None,
184
+ user_id: Optional[str] = None,
185
+ run_id: Optional[str] = None,
186
+ session_id: Optional[str] = None,
187
+ session_state: Optional[Dict[str, Any]] = None,
188
+ audio: Optional[List[Audio]] = None,
189
+ images: Optional[List[Image]] = None,
190
+ videos: Optional[List[Video]] = None,
191
+ files: Optional[List[File]] = None,
192
+ stream: bool = False,
193
+ stream_events: Optional[bool] = None,
194
+ background: Optional[bool] = False,
195
+ websocket: Optional[WebSocket] = None,
196
+ background_tasks: Optional[Any] = None,
197
+ auth_token: Optional[str] = None,
198
+ **kwargs: Any,
199
+ ) -> Union[WorkflowRunOutput, AsyncIterator[WorkflowRunOutputEvent]]:
200
+ # TODO: Deal with background
201
+ validated_input = validate_input(input)
202
+ serialized_input = serialize_input(validated_input)
203
+ headers = self._get_auth_headers(auth_token)
204
+
205
+ # A2A protocol path
206
+ if self.a2a_client:
207
+ return self._arun_a2a( # type: ignore[return-value]
208
+ message=serialized_input,
209
+ stream=stream or False,
210
+ user_id=user_id,
211
+ context_id=session_id, # Map session_id → context_id for A2A
212
+ images=images,
213
+ videos=videos,
214
+ audio=audio,
215
+ files=files,
216
+ headers=headers,
217
+ )
218
+
219
+ # AgentOS protocol path (default)
220
+ if self.agentos_client:
221
+ if stream:
222
+ # Handle streaming response
223
+ return self.agentos_client.run_workflow_stream(
224
+ workflow_id=self.workflow_id,
225
+ message=serialized_input,
226
+ additional_data=additional_data,
227
+ run_id=run_id,
228
+ session_id=session_id,
229
+ user_id=user_id,
230
+ audio=audio,
231
+ images=images,
232
+ videos=videos,
233
+ files=files,
234
+ session_state=session_state,
235
+ stream_events=stream_events,
236
+ headers=headers,
237
+ **kwargs,
238
+ )
239
+ else:
240
+ return self.agentos_client.run_workflow( # type: ignore
241
+ workflow_id=self.workflow_id,
242
+ message=serialized_input,
243
+ additional_data=additional_data,
244
+ run_id=run_id,
245
+ session_id=session_id,
246
+ user_id=user_id,
247
+ audio=audio,
248
+ images=images,
249
+ videos=videos,
250
+ files=files,
251
+ session_state=session_state,
252
+ headers=headers,
253
+ **kwargs,
254
+ )
255
+ else:
256
+ raise ValueError("No client available")
257
+
258
+ def _arun_a2a(
259
+ self,
260
+ message: str,
261
+ stream: bool,
262
+ user_id: Optional[str],
263
+ context_id: Optional[str],
264
+ images: Optional[List[Image]],
265
+ videos: Optional[List[Video]],
266
+ audio: Optional[List[Audio]],
267
+ files: Optional[List[File]],
268
+ headers: Optional[Dict[str, str]],
269
+ ) -> Union[WorkflowRunOutput, AsyncIterator[WorkflowRunOutputEvent]]:
270
+ """Execute via A2A protocol.
271
+
272
+ Args:
273
+ message: Serialized message string
274
+ stream: Whether to stream the response
275
+ user_id: User identifier
276
+ context_id: Session/context ID (maps to session_id)
277
+ images: Images to include
278
+ videos: Videos to include
279
+ audio: Audio files to include
280
+ files: Files to include
281
+ headers: HTTP headers to include in the request (optional)
282
+ Returns:
283
+ WorkflowRunOutput for non-streaming, AsyncIterator[WorkflowRunOutputEvent] for streaming
284
+ """
285
+ if not self.a2a_client:
286
+ raise ValueError("A2A client not available")
287
+ from agno.client.a2a.utils import map_stream_events_to_workflow_run_events
288
+
289
+ if stream:
290
+ # Return async generator for streaming
291
+ event_stream = self.a2a_client.stream_message(
292
+ message=message,
293
+ context_id=context_id,
294
+ user_id=user_id,
295
+ images=list(images) if images else None,
296
+ audio=list(audio) if audio else None,
297
+ videos=list(videos) if videos else None,
298
+ files=list(files) if files else None,
299
+ headers=headers,
300
+ )
301
+ return map_stream_events_to_workflow_run_events(event_stream, workflow_id=self.workflow_id) # type: ignore
302
+ else:
303
+ # Return coroutine for non-streaming
304
+ return self._arun_a2a_send( # type: ignore[return-value]
305
+ message=message,
306
+ user_id=user_id,
307
+ context_id=context_id,
308
+ images=images,
309
+ audio=audio,
310
+ videos=videos,
311
+ files=files,
312
+ headers=headers,
313
+ )
314
+
315
+ async def _arun_a2a_send(
316
+ self,
317
+ message: str,
318
+ user_id: Optional[str],
319
+ context_id: Optional[str],
320
+ images: Optional[List[Image]],
321
+ videos: Optional[List[Video]],
322
+ audio: Optional[List[Audio]],
323
+ files: Optional[List[File]],
324
+ headers: Optional[Dict[str, str]],
325
+ ) -> WorkflowRunOutput:
326
+ """Send a non-streaming A2A message and convert response to WorkflowRunOutput."""
327
+ if not self.a2a_client:
328
+ raise ValueError("A2A client not available")
329
+ from agno.client.a2a.utils import map_task_result_to_workflow_run_output
330
+
331
+ task_result = await self.a2a_client.send_message(
332
+ message=message,
333
+ context_id=context_id,
334
+ user_id=user_id,
335
+ images=list(images) if images else None,
336
+ audio=list(audio) if audio else None,
337
+ videos=list(videos) if videos else None,
338
+ files=list(files) if files else None,
339
+ headers=headers,
340
+ )
341
+ return map_task_result_to_workflow_run_output(task_result, workflow_id=self.workflow_id, user_id=user_id)
342
+
343
+ async def acancel_run(self, run_id: str, auth_token: Optional[str] = None) -> bool:
344
+ """Cancel a running workflow execution.
345
+
346
+ Args:
347
+ run_id (str): The run_id to cancel.
348
+ auth_token: Optional JWT token for authentication.
349
+
350
+ Returns:
351
+ bool: True if the run was found and marked for cancellation, False otherwise.
352
+ """
353
+ headers = self._get_auth_headers(auth_token)
354
+ try:
355
+ await self.get_os_client().cancel_workflow_run(
356
+ workflow_id=self.workflow_id,
357
+ run_id=run_id,
358
+ headers=headers,
359
+ )
360
+ return True
361
+ except Exception:
362
+ return False
agno/workflow/router.py CHANGED
@@ -178,6 +178,7 @@ class Router:
178
178
  workflow_session: Optional[WorkflowSession] = None,
179
179
  add_workflow_history_to_steps: Optional[bool] = False,
180
180
  num_history_runs: int = 3,
181
+ background_tasks: Optional[Any] = None,
181
182
  ) -> StepOutput:
182
183
  """Execute the router and its selected steps with sequential chaining"""
183
184
  log_debug(f"Router Start: {self.name}", center=True, symbol="-")
@@ -219,6 +220,7 @@ class Router:
219
220
  workflow_session=workflow_session,
220
221
  add_workflow_history_to_steps=add_workflow_history_to_steps,
221
222
  num_history_runs=num_history_runs,
223
+ background_tasks=background_tasks,
222
224
  )
223
225
 
224
226
  # Handle both single StepOutput and List[StepOutput]
@@ -264,6 +266,7 @@ class Router:
264
266
  step_type=StepType.ROUTER,
265
267
  content=f"Router {self.name} completed with {len(all_results)} results",
266
268
  success=all(result.success for result in all_results) if all_results else True,
269
+ stop=any(result.stop for result in all_results) if all_results else False,
267
270
  steps=all_results,
268
271
  )
269
272
 
@@ -275,7 +278,6 @@ class Router:
275
278
  run_context: Optional[RunContext] = None,
276
279
  session_state: Optional[Dict[str, Any]] = None,
277
280
  stream_events: bool = False,
278
- stream_intermediate_steps: bool = False,
279
281
  stream_executor_events: bool = True,
280
282
  workflow_run_response: Optional[WorkflowRunOutput] = None,
281
283
  step_index: Optional[Union[int, tuple]] = None,
@@ -284,6 +286,7 @@ class Router:
284
286
  workflow_session: Optional[WorkflowSession] = None,
285
287
  add_workflow_history_to_steps: Optional[bool] = False,
286
288
  num_history_runs: int = 3,
289
+ background_tasks: Optional[Any] = None,
287
290
  ) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
288
291
  """Execute the router with streaming support"""
289
292
  log_debug(f"Router Start: {self.name}", center=True, symbol="-")
@@ -299,9 +302,6 @@ class Router:
299
302
  steps_to_execute = self._route_steps(step_input, session_state=session_state)
300
303
  log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
301
304
 
302
- # Considering both stream_events and stream_intermediate_steps (deprecated)
303
- stream_events = stream_events or stream_intermediate_steps
304
-
305
305
  if stream_events and workflow_run_response:
306
306
  # Yield router started event
307
307
  yield RouterExecutionStartedEvent(
@@ -357,6 +357,7 @@ class Router:
357
357
  workflow_session=workflow_session,
358
358
  add_workflow_history_to_steps=add_workflow_history_to_steps,
359
359
  num_history_runs=num_history_runs,
360
+ background_tasks=background_tasks,
360
361
  ):
361
362
  if isinstance(event, StepOutput):
362
363
  step_outputs_for_step.append(event)
@@ -427,6 +428,7 @@ class Router:
427
428
  step_type=StepType.ROUTER,
428
429
  content=f"Router {self.name} completed with {len(all_results)} results",
429
430
  success=all(result.success for result in all_results) if all_results else True,
431
+ stop=any(result.stop for result in all_results) if all_results else False,
430
432
  steps=all_results,
431
433
  )
432
434
 
@@ -442,6 +444,7 @@ class Router:
442
444
  workflow_session: Optional[WorkflowSession] = None,
443
445
  add_workflow_history_to_steps: Optional[bool] = False,
444
446
  num_history_runs: int = 3,
447
+ background_tasks: Optional[Any] = None,
445
448
  ) -> StepOutput:
446
449
  """Async execute the router and its selected steps with sequential chaining"""
447
450
  log_debug(f"Router Start: {self.name}", center=True, symbol="-")
@@ -484,6 +487,7 @@ class Router:
484
487
  workflow_session=workflow_session,
485
488
  add_workflow_history_to_steps=add_workflow_history_to_steps,
486
489
  num_history_runs=num_history_runs,
490
+ background_tasks=background_tasks,
487
491
  )
488
492
  # Handle both single StepOutput and List[StepOutput]
489
493
  if isinstance(step_output, list):
@@ -531,6 +535,7 @@ class Router:
531
535
  step_type=StepType.ROUTER,
532
536
  content=f"Router {self.name} completed with {len(all_results)} results",
533
537
  success=all(result.success for result in all_results) if all_results else True,
538
+ stop=any(result.stop for result in all_results) if all_results else False,
534
539
  steps=all_results,
535
540
  )
536
541
 
@@ -542,7 +547,6 @@ class Router:
542
547
  run_context: Optional[RunContext] = None,
543
548
  session_state: Optional[Dict[str, Any]] = None,
544
549
  stream_events: bool = False,
545
- stream_intermediate_steps: bool = False,
546
550
  stream_executor_events: bool = True,
547
551
  workflow_run_response: Optional[WorkflowRunOutput] = None,
548
552
  step_index: Optional[Union[int, tuple]] = None,
@@ -551,6 +555,7 @@ class Router:
551
555
  workflow_session: Optional[WorkflowSession] = None,
552
556
  add_workflow_history_to_steps: Optional[bool] = False,
553
557
  num_history_runs: int = 3,
558
+ background_tasks: Optional[Any] = None,
554
559
  ) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
555
560
  """Async execute the router with streaming support"""
556
561
  log_debug(f"Router Start: {self.name}", center=True, symbol="-")
@@ -566,9 +571,6 @@ class Router:
566
571
  steps_to_execute = await self._aroute_steps(step_input, session_state=session_state)
567
572
  log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
568
573
 
569
- # Considering both stream_events and stream_intermediate_steps (deprecated)
570
- stream_events = stream_events or stream_intermediate_steps
571
-
572
574
  if stream_events and workflow_run_response:
573
575
  # Yield router started event
574
576
  yield RouterExecutionStartedEvent(
@@ -626,6 +628,7 @@ class Router:
626
628
  workflow_session=workflow_session,
627
629
  add_workflow_history_to_steps=add_workflow_history_to_steps,
628
630
  num_history_runs=num_history_runs,
631
+ background_tasks=background_tasks,
629
632
  ):
630
633
  if isinstance(event, StepOutput):
631
634
  step_outputs_for_step.append(event)
@@ -697,6 +700,6 @@ class Router:
697
700
  content=f"Router {self.name} completed with {len(all_results)} results",
698
701
  success=all(result.success for result in all_results) if all_results else True,
699
702
  error=None,
700
- stop=False,
703
+ stop=any(result.stop for result in all_results) if all_results else False,
701
704
  steps=all_results,
702
705
  )