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
@@ -181,6 +181,7 @@ class Condition:
181
181
  workflow_session: Optional[WorkflowSession] = None,
182
182
  add_workflow_history_to_steps: Optional[bool] = False,
183
183
  num_history_runs: int = 3,
184
+ background_tasks: Optional[Any] = None,
184
185
  ) -> StepOutput:
185
186
  """Execute the condition and its steps with sequential chaining if condition is true"""
186
187
  log_debug(f"Condition Start: {self.name}", center=True, symbol="-")
@@ -225,6 +226,7 @@ class Condition:
225
226
  workflow_session=workflow_session,
226
227
  add_workflow_history_to_steps=add_workflow_history_to_steps,
227
228
  num_history_runs=num_history_runs,
229
+ background_tasks=background_tasks,
228
230
  )
229
231
 
230
232
  # Handle both single StepOutput and List[StepOutput] (from Loop/Condition/Router steps)
@@ -276,7 +278,7 @@ class Condition:
276
278
  content=f"Condition {self.name} completed with {len(all_results)} results",
277
279
  success=all(result.success for result in all_results) if all_results else True,
278
280
  error=None,
279
- stop=False,
281
+ stop=any(result.stop for result in all_results) if all_results else False,
280
282
  steps=all_results,
281
283
  )
282
284
 
@@ -286,7 +288,6 @@ class Condition:
286
288
  session_id: Optional[str] = None,
287
289
  user_id: Optional[str] = None,
288
290
  stream_events: bool = False,
289
- stream_intermediate_steps: bool = False, # type: ignore
290
291
  stream_executor_events: bool = True,
291
292
  workflow_run_response: Optional[WorkflowRunOutput] = None,
292
293
  step_index: Optional[Union[int, tuple]] = None,
@@ -297,6 +298,7 @@ class Condition:
297
298
  workflow_session: Optional[WorkflowSession] = None,
298
299
  add_workflow_history_to_steps: Optional[bool] = False,
299
300
  num_history_runs: int = 3,
301
+ background_tasks: Optional[Any] = None,
300
302
  ) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
301
303
  """Execute the condition with streaming support - mirrors Loop logic"""
302
304
  log_debug(f"Condition Start: {self.name}", center=True, symbol="-")
@@ -312,9 +314,6 @@ class Condition:
312
314
  condition_result = self._evaluate_condition(step_input, session_state=session_state)
313
315
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
314
316
 
315
- # Considering both stream_events and stream_intermediate_steps (deprecated)
316
- stream_events = stream_events or stream_intermediate_steps
317
-
318
317
  if stream_events and workflow_run_response:
319
318
  # Yield condition started event
320
319
  yield ConditionExecutionStartedEvent(
@@ -380,6 +379,7 @@ class Condition:
380
379
  workflow_session=workflow_session,
381
380
  add_workflow_history_to_steps=add_workflow_history_to_steps,
382
381
  num_history_runs=num_history_runs,
382
+ background_tasks=background_tasks,
383
383
  ):
384
384
  if isinstance(event, StepOutput):
385
385
  step_outputs_for_step.append(event)
@@ -449,6 +449,7 @@ class Condition:
449
449
  step_type=StepType.CONDITION,
450
450
  content=f"Condition {self.name} completed with {len(all_results)} results",
451
451
  success=all(result.success for result in all_results) if all_results else True,
452
+ stop=any(result.stop for result in all_results) if all_results else False,
452
453
  steps=all_results,
453
454
  )
454
455
 
@@ -464,6 +465,7 @@ class Condition:
464
465
  workflow_session: Optional[WorkflowSession] = None,
465
466
  add_workflow_history_to_steps: Optional[bool] = False,
466
467
  num_history_runs: int = 3,
468
+ background_tasks: Optional[Any] = None,
467
469
  ) -> StepOutput:
468
470
  """Async execute the condition and its steps with sequential chaining"""
469
471
  log_debug(f"Condition Start: {self.name}", center=True, symbol="-")
@@ -509,6 +511,7 @@ class Condition:
509
511
  workflow_session=workflow_session,
510
512
  add_workflow_history_to_steps=add_workflow_history_to_steps,
511
513
  num_history_runs=num_history_runs,
514
+ background_tasks=background_tasks,
512
515
  )
513
516
 
514
517
  # Handle both single StepOutput and List[StepOutput]
@@ -558,7 +561,7 @@ class Condition:
558
561
  content=f"Condition {self.name} completed with {len(all_results)} results",
559
562
  success=all(result.success for result in all_results) if all_results else True,
560
563
  error=None,
561
- stop=False,
564
+ stop=any(result.stop for result in all_results) if all_results else False,
562
565
  steps=all_results,
563
566
  )
564
567
 
@@ -568,7 +571,6 @@ class Condition:
568
571
  session_id: Optional[str] = None,
569
572
  user_id: Optional[str] = None,
570
573
  stream_events: bool = False,
571
- stream_intermediate_steps: bool = False,
572
574
  stream_executor_events: bool = True,
573
575
  workflow_run_response: Optional[WorkflowRunOutput] = None,
574
576
  step_index: Optional[Union[int, tuple]] = None,
@@ -579,6 +581,7 @@ class Condition:
579
581
  workflow_session: Optional[WorkflowSession] = None,
580
582
  add_workflow_history_to_steps: Optional[bool] = False,
581
583
  num_history_runs: int = 3,
584
+ background_tasks: Optional[Any] = None,
582
585
  ) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
583
586
  """Async execute the condition with streaming support - mirrors Loop logic"""
584
587
  log_debug(f"Condition Start: {self.name}", center=True, symbol="-")
@@ -594,9 +597,6 @@ class Condition:
594
597
  condition_result = await self._aevaluate_condition(step_input, session_state=session_state)
595
598
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
596
599
 
597
- # Considering both stream_events and stream_intermediate_steps (deprecated)
598
- stream_events = stream_events or stream_intermediate_steps
599
-
600
600
  if stream_events and workflow_run_response:
601
601
  # Yield condition started event
602
602
  yield ConditionExecutionStartedEvent(
@@ -664,6 +664,7 @@ class Condition:
664
664
  workflow_session=workflow_session,
665
665
  add_workflow_history_to_steps=add_workflow_history_to_steps,
666
666
  num_history_runs=num_history_runs,
667
+ background_tasks=background_tasks,
667
668
  ):
668
669
  if isinstance(event, StepOutput):
669
670
  step_outputs_for_step.append(event)
@@ -734,5 +735,6 @@ class Condition:
734
735
  step_type=StepType.CONDITION,
735
736
  content=f"Condition {self.name} completed with {len(all_results)} results",
736
737
  success=all(result.success for result in all_results) if all_results else True,
738
+ stop=any(result.stop for result in all_results) if all_results else False,
737
739
  steps=all_results,
738
740
  )
agno/workflow/loop.py CHANGED
@@ -138,6 +138,7 @@ class Loop:
138
138
  workflow_session: Optional[WorkflowSession] = None,
139
139
  add_workflow_history_to_steps: Optional[bool] = False,
140
140
  num_history_runs: int = 3,
141
+ background_tasks: Optional[Any] = None,
141
142
  ) -> StepOutput:
142
143
  """Execute loop steps with iteration control - mirrors workflow execution logic"""
143
144
  # Use workflow logger for loop orchestration
@@ -148,6 +149,7 @@ class Loop:
148
149
 
149
150
  all_results = []
150
151
  iteration = 0
152
+ early_termination = False
151
153
 
152
154
  while iteration < self.max_iterations:
153
155
  # Execute all steps in this iteration - mirroring workflow logic
@@ -167,6 +169,7 @@ class Loop:
167
169
  workflow_session=workflow_session,
168
170
  add_workflow_history_to_steps=add_workflow_history_to_steps,
169
171
  num_history_runs=num_history_runs,
172
+ background_tasks=background_tasks,
170
173
  )
171
174
 
172
175
  # Handle both single StepOutput and List[StepOutput] (from Loop/Condition steps)
@@ -179,6 +182,7 @@ class Loop:
179
182
 
180
183
  if any(output.stop for output in step_output):
181
184
  logger.info(f"Early termination requested by step {step_name}")
185
+ early_termination = True
182
186
  break
183
187
  else:
184
188
  # Single StepOutput
@@ -188,6 +192,7 @@ class Loop:
188
192
 
189
193
  if step_output.stop:
190
194
  logger.info(f"Early termination requested by step {step_name}")
195
+ early_termination = True
191
196
  break
192
197
 
193
198
  # Update step input for next step
@@ -206,7 +211,11 @@ class Loop:
206
211
  break
207
212
  except Exception as e:
208
213
  logger.warning(f"End condition evaluation failed: {e}")
209
- # Continue with loop if end condition fails
214
+
215
+ # Break out of iteration loop if early termination was requested
216
+ if early_termination:
217
+ log_debug(f"Loop ending early due to step termination request at iteration {iteration}")
218
+ break
210
219
 
211
220
  log_debug(f"Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
212
221
 
@@ -221,6 +230,7 @@ class Loop:
221
230
  step_type=StepType.LOOP,
222
231
  content=f"Loop {self.name} completed {iteration} iterations with {len(flattened_results)} total steps",
223
232
  success=all(result.success for result in flattened_results) if flattened_results else True,
233
+ stop=any(result.stop for result in flattened_results) if flattened_results else False,
224
234
  steps=flattened_results,
225
235
  )
226
236
 
@@ -230,7 +240,6 @@ class Loop:
230
240
  session_id: Optional[str] = None,
231
241
  user_id: Optional[str] = None,
232
242
  stream_events: bool = False,
233
- stream_intermediate_steps: bool = False,
234
243
  stream_executor_events: bool = True,
235
244
  workflow_run_response: Optional[WorkflowRunOutput] = None,
236
245
  step_index: Optional[Union[int, tuple]] = None,
@@ -241,6 +250,7 @@ class Loop:
241
250
  workflow_session: Optional[WorkflowSession] = None,
242
251
  add_workflow_history_to_steps: Optional[bool] = False,
243
252
  num_history_runs: int = 3,
253
+ background_tasks: Optional[Any] = None,
244
254
  ) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
245
255
  """Execute loop steps with streaming support - mirrors workflow execution logic"""
246
256
  log_debug(f"Loop Start: {self.name}", center=True, symbol="=")
@@ -250,9 +260,6 @@ class Loop:
250
260
 
251
261
  loop_step_id = str(uuid4())
252
262
 
253
- # Considering both stream_events and stream_intermediate_steps (deprecated)
254
- stream_events = stream_events or stream_intermediate_steps
255
-
256
263
  if stream_events and workflow_run_response:
257
264
  # Yield loop started event
258
265
  yield LoopExecutionStartedEvent(
@@ -321,6 +328,7 @@ class Loop:
321
328
  add_workflow_history_to_steps=add_workflow_history_to_steps,
322
329
  workflow_session=workflow_session,
323
330
  num_history_runs=num_history_runs,
331
+ background_tasks=background_tasks,
324
332
  ):
325
333
  if isinstance(event, StepOutput):
326
334
  step_outputs_for_iteration.append(event)
@@ -423,6 +431,7 @@ class Loop:
423
431
  step_type=StepType.LOOP,
424
432
  content=f"Loop {self.name} completed {iteration} iterations with {len(flattened_results)} total steps",
425
433
  success=all(result.success for result in flattened_results) if flattened_results else True,
434
+ stop=any(result.stop for result in flattened_results) if flattened_results else False,
426
435
  steps=flattened_results,
427
436
  )
428
437
 
@@ -438,6 +447,7 @@ class Loop:
438
447
  workflow_session: Optional[WorkflowSession] = None,
439
448
  add_workflow_history_to_steps: Optional[bool] = False,
440
449
  num_history_runs: int = 3,
450
+ background_tasks: Optional[Any] = None,
441
451
  ) -> StepOutput:
442
452
  """Execute loop steps asynchronously with iteration control - mirrors workflow execution logic"""
443
453
  # Use workflow logger for async loop orchestration
@@ -450,6 +460,7 @@ class Loop:
450
460
 
451
461
  all_results = []
452
462
  iteration = 0
463
+ early_termination = False
453
464
 
454
465
  while iteration < self.max_iterations:
455
466
  # Execute all steps in this iteration - mirroring workflow logic
@@ -469,6 +480,7 @@ class Loop:
469
480
  workflow_session=workflow_session,
470
481
  add_workflow_history_to_steps=add_workflow_history_to_steps,
471
482
  num_history_runs=num_history_runs,
483
+ background_tasks=background_tasks,
472
484
  )
473
485
 
474
486
  # Handle both single StepOutput and List[StepOutput] (from Loop/Condition steps)
@@ -481,6 +493,7 @@ class Loop:
481
493
 
482
494
  if any(output.stop for output in step_output):
483
495
  logger.info(f"Early termination requested by step {step_name}")
496
+ early_termination = True
484
497
  break
485
498
  else:
486
499
  # Single StepOutput
@@ -490,6 +503,7 @@ class Loop:
490
503
 
491
504
  if step_output.stop:
492
505
  logger.info(f"Early termination requested by step {step_name}")
506
+ early_termination = True
493
507
  break
494
508
 
495
509
  # Update step input for next step
@@ -512,6 +526,11 @@ class Loop:
512
526
  except Exception as e:
513
527
  logger.warning(f"End condition evaluation failed: {e}")
514
528
 
529
+ # Break out of iteration loop if early termination was requested
530
+ if early_termination:
531
+ log_debug(f"Loop ending early due to step termination request at iteration {iteration}")
532
+ break
533
+
515
534
  # Use workflow logger for async loop completion
516
535
  log_debug(f"Async Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
517
536
 
@@ -526,6 +545,7 @@ class Loop:
526
545
  step_type=StepType.LOOP,
527
546
  content=f"Loop {self.name} completed {iteration} iterations with {len(flattened_results)} total steps",
528
547
  success=all(result.success for result in flattened_results) if flattened_results else True,
548
+ stop=any(result.stop for result in flattened_results) if flattened_results else False,
529
549
  steps=flattened_results,
530
550
  )
531
551
 
@@ -535,7 +555,6 @@ class Loop:
535
555
  session_id: Optional[str] = None,
536
556
  user_id: Optional[str] = None,
537
557
  stream_events: bool = False,
538
- stream_intermediate_steps: bool = False,
539
558
  stream_executor_events: bool = True,
540
559
  workflow_run_response: Optional[WorkflowRunOutput] = None,
541
560
  step_index: Optional[Union[int, tuple]] = None,
@@ -546,6 +565,7 @@ class Loop:
546
565
  workflow_session: Optional[WorkflowSession] = None,
547
566
  add_workflow_history_to_steps: Optional[bool] = False,
548
567
  num_history_runs: int = 3,
568
+ background_tasks: Optional[Any] = None,
549
569
  ) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
550
570
  """Execute loop steps with async streaming support - mirrors workflow execution logic"""
551
571
  log_debug(f"Loop Start: {self.name}", center=True, symbol="=")
@@ -555,9 +575,6 @@ class Loop:
555
575
  # Prepare steps first
556
576
  self._prepare_steps()
557
577
 
558
- # Considering both stream_events and stream_intermediate_steps (deprecated)
559
- stream_events = stream_events or stream_intermediate_steps
560
-
561
578
  if stream_events and workflow_run_response:
562
579
  # Yield loop started event
563
580
  yield LoopExecutionStartedEvent(
@@ -626,6 +643,7 @@ class Loop:
626
643
  workflow_session=workflow_session,
627
644
  add_workflow_history_to_steps=add_workflow_history_to_steps,
628
645
  num_history_runs=num_history_runs,
646
+ background_tasks=background_tasks,
629
647
  ):
630
648
  if isinstance(event, StepOutput):
631
649
  step_outputs_for_iteration.append(event)
@@ -731,5 +749,6 @@ class Loop:
731
749
  step_type=StepType.LOOP,
732
750
  content=f"Loop {self.name} completed {iteration} iterations with {len(flattened_results)} total steps",
733
751
  success=all(result.success for result in flattened_results) if flattened_results else True,
752
+ stop=any(result.stop for result in flattened_results) if flattened_results else False,
734
753
  steps=flattened_results,
735
754
  )
agno/workflow/parallel.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  from concurrent.futures import ThreadPoolExecutor, as_completed
3
+ from contextvars import copy_context
3
4
  from copy import deepcopy
4
5
  from dataclasses import dataclass
5
6
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
@@ -100,7 +101,7 @@ class Parallel:
100
101
  step_name=self.name or "Parallel",
101
102
  step_id=str(uuid4()),
102
103
  step_type=StepType.PARALLEL,
103
- content=f"Parallel {self.name or 'execution'} completed with 1 result",
104
+ content=self._build_aggregated_content(step_outputs),
104
105
  executor_name=self.name or "Parallel",
105
106
  images=single_result.images,
106
107
  videos=single_result.videos,
@@ -114,8 +115,8 @@ class Parallel:
114
115
 
115
116
  early_termination_requested = any(output.stop for output in step_outputs if hasattr(output, "stop"))
116
117
 
117
- # Multiple results - aggregate them
118
- aggregated_content = f"Parallel {self.name or 'execution'} completed with {len(step_outputs)} results"
118
+ # Multiple results - aggregate them with actual content from all steps
119
+ aggregated_content = self._build_aggregated_content(step_outputs)
119
120
 
120
121
  # Combine all media from parallel steps
121
122
  all_images = []
@@ -206,6 +207,7 @@ class Parallel:
206
207
  workflow_session: Optional[WorkflowSession] = None,
207
208
  add_workflow_history_to_steps: Optional[bool] = False,
208
209
  num_history_runs: int = 3,
210
+ background_tasks: Optional[Any] = None,
209
211
  ) -> StepOutput:
210
212
  """Execute all steps in parallel and return aggregated result"""
211
213
  # Use workflow logger for parallel orchestration
@@ -243,6 +245,7 @@ class Parallel:
243
245
  num_history_runs=num_history_runs,
244
246
  run_context=run_context,
245
247
  session_state=step_session_state,
248
+ background_tasks=background_tasks,
246
249
  ) # type: ignore[union-attr]
247
250
  return idx, step_result, step_session_state
248
251
  except Exception as exc:
@@ -264,8 +267,9 @@ class Parallel:
264
267
 
265
268
  with ThreadPoolExecutor(max_workers=len(self.steps)) as executor:
266
269
  # Submit all tasks with their original indices
270
+ # Use copy_context().run to propagate context variables to child threads
267
271
  future_to_index = {
268
- executor.submit(execute_step_with_index, indexed_step): indexed_step[0]
272
+ executor.submit(copy_context().run, execute_step_with_index, indexed_step): indexed_step[0]
269
273
  for indexed_step in indexed_steps
270
274
  }
271
275
 
@@ -324,7 +328,6 @@ class Parallel:
324
328
  session_id: Optional[str] = None,
325
329
  user_id: Optional[str] = None,
326
330
  stream_events: bool = False,
327
- stream_intermediate_steps: bool = False,
328
331
  stream_executor_events: bool = True,
329
332
  workflow_run_response: Optional[WorkflowRunOutput] = None,
330
333
  step_index: Optional[Union[int, tuple]] = None,
@@ -335,6 +338,7 @@ class Parallel:
335
338
  workflow_session: Optional[WorkflowSession] = None,
336
339
  add_workflow_history_to_steps: Optional[bool] = False,
337
340
  num_history_runs: int = 3,
341
+ background_tasks: Optional[Any] = None,
338
342
  ) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
339
343
  """Execute all steps in parallel with streaming support"""
340
344
  log_debug(f"Parallel Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="=")
@@ -355,9 +359,6 @@ class Parallel:
355
359
  else:
356
360
  session_state_copies.append({})
357
361
 
358
- # Considering both stream_events and stream_intermediate_steps (deprecated)
359
- stream_events = stream_events or stream_intermediate_steps
360
-
361
362
  if stream_events and workflow_run_response:
362
363
  # Yield parallel step started event
363
364
  yield ParallelExecutionStartedEvent(
@@ -407,10 +408,12 @@ class Parallel:
407
408
  step_index=sub_step_index,
408
409
  store_executor_outputs=store_executor_outputs,
409
410
  session_state=step_session_state,
411
+ run_context=run_context,
410
412
  parent_step_id=parallel_step_id,
411
413
  workflow_session=workflow_session,
412
414
  add_workflow_history_to_steps=add_workflow_history_to_steps,
413
415
  num_history_runs=num_history_runs,
416
+ background_tasks=background_tasks,
414
417
  ):
415
418
  # Put event immediately in queue
416
419
  event_queue.put(("event", idx, event))
@@ -438,7 +441,11 @@ class Parallel:
438
441
 
439
442
  with ThreadPoolExecutor(max_workers=len(self.steps)) as executor:
440
443
  # Submit all tasks
441
- futures = [executor.submit(execute_step_stream_with_index, indexed_step) for indexed_step in indexed_steps]
444
+ # Use copy_context().run to propagate context variables to child threads
445
+ futures = [
446
+ executor.submit(copy_context().run, execute_step_stream_with_index, indexed_step)
447
+ for indexed_step in indexed_steps
448
+ ]
442
449
 
443
450
  # Process events from queue as they arrive
444
451
  completed_steps = 0
@@ -526,6 +533,7 @@ class Parallel:
526
533
  workflow_session: Optional[WorkflowSession] = None,
527
534
  add_workflow_history_to_steps: Optional[bool] = False,
528
535
  num_history_runs: int = 3,
536
+ background_tasks: Optional[Any] = None,
529
537
  ) -> StepOutput:
530
538
  """Execute all steps in parallel using asyncio and return aggregated result"""
531
539
  # Use workflow logger for async parallel orchestration
@@ -562,6 +570,8 @@ class Parallel:
562
570
  add_workflow_history_to_steps=add_workflow_history_to_steps,
563
571
  num_history_runs=num_history_runs,
564
572
  session_state=step_session_state,
573
+ run_context=run_context,
574
+ background_tasks=background_tasks,
565
575
  ) # type: ignore[union-attr]
566
576
  return idx, inner_step_result, step_session_state
567
577
  except Exception as exc:
@@ -644,7 +654,6 @@ class Parallel:
644
654
  session_id: Optional[str] = None,
645
655
  user_id: Optional[str] = None,
646
656
  stream_events: bool = False,
647
- stream_intermediate_steps: bool = False,
648
657
  stream_executor_events: bool = True,
649
658
  workflow_run_response: Optional[WorkflowRunOutput] = None,
650
659
  step_index: Optional[Union[int, tuple]] = None,
@@ -655,6 +664,7 @@ class Parallel:
655
664
  workflow_session: Optional[WorkflowSession] = None,
656
665
  add_workflow_history_to_steps: Optional[bool] = False,
657
666
  num_history_runs: int = 3,
667
+ background_tasks: Optional[Any] = None,
658
668
  ) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
659
669
  """Execute all steps in parallel with async streaming support"""
660
670
  log_debug(f"Parallel Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="=")
@@ -675,9 +685,6 @@ class Parallel:
675
685
  else:
676
686
  session_state_copies.append({})
677
687
 
678
- # Considering both stream_events and stream_intermediate_steps (deprecated)
679
- stream_events = stream_events or stream_intermediate_steps
680
-
681
688
  if stream_events and workflow_run_response:
682
689
  # Yield parallel step started event
683
690
  yield ParallelExecutionStartedEvent(
@@ -732,6 +739,7 @@ class Parallel:
732
739
  workflow_session=workflow_session,
733
740
  add_workflow_history_to_steps=add_workflow_history_to_steps,
734
741
  num_history_runs=num_history_runs,
742
+ background_tasks=background_tasks,
735
743
  ): # type: ignore[union-attr]
736
744
  # Yield events immediately to the queue
737
745
  await event_queue.put(("event", idx, event))