agno 2.1.2__py3-none-any.whl → 2.3.13__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 (314) hide show
  1. agno/agent/agent.py +5540 -2273
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/compression/__init__.py +3 -0
  5. agno/compression/manager.py +247 -0
  6. agno/culture/__init__.py +3 -0
  7. agno/culture/manager.py +956 -0
  8. agno/db/async_postgres/__init__.py +3 -0
  9. agno/db/base.py +689 -6
  10. agno/db/dynamo/dynamo.py +933 -37
  11. agno/db/dynamo/schemas.py +174 -10
  12. agno/db/dynamo/utils.py +63 -4
  13. agno/db/firestore/firestore.py +831 -9
  14. agno/db/firestore/schemas.py +51 -0
  15. agno/db/firestore/utils.py +102 -4
  16. agno/db/gcs_json/gcs_json_db.py +660 -12
  17. agno/db/gcs_json/utils.py +60 -26
  18. agno/db/in_memory/in_memory_db.py +287 -14
  19. agno/db/in_memory/utils.py +60 -2
  20. agno/db/json/json_db.py +590 -14
  21. agno/db/json/utils.py +60 -26
  22. agno/db/migrations/manager.py +199 -0
  23. agno/db/migrations/v1_to_v2.py +43 -13
  24. agno/db/migrations/versions/__init__.py +0 -0
  25. agno/db/migrations/versions/v2_3_0.py +938 -0
  26. agno/db/mongo/__init__.py +15 -1
  27. agno/db/mongo/async_mongo.py +2760 -0
  28. agno/db/mongo/mongo.py +879 -11
  29. agno/db/mongo/schemas.py +42 -0
  30. agno/db/mongo/utils.py +80 -8
  31. agno/db/mysql/__init__.py +2 -1
  32. agno/db/mysql/async_mysql.py +2912 -0
  33. agno/db/mysql/mysql.py +946 -68
  34. agno/db/mysql/schemas.py +72 -10
  35. agno/db/mysql/utils.py +198 -7
  36. agno/db/postgres/__init__.py +2 -1
  37. agno/db/postgres/async_postgres.py +2579 -0
  38. agno/db/postgres/postgres.py +942 -57
  39. agno/db/postgres/schemas.py +81 -18
  40. agno/db/postgres/utils.py +164 -2
  41. agno/db/redis/redis.py +671 -7
  42. agno/db/redis/schemas.py +50 -0
  43. agno/db/redis/utils.py +65 -7
  44. agno/db/schemas/__init__.py +2 -1
  45. agno/db/schemas/culture.py +120 -0
  46. agno/db/schemas/evals.py +1 -0
  47. agno/db/schemas/memory.py +17 -2
  48. agno/db/singlestore/schemas.py +63 -0
  49. agno/db/singlestore/singlestore.py +949 -83
  50. agno/db/singlestore/utils.py +60 -2
  51. agno/db/sqlite/__init__.py +2 -1
  52. agno/db/sqlite/async_sqlite.py +2911 -0
  53. agno/db/sqlite/schemas.py +62 -0
  54. agno/db/sqlite/sqlite.py +965 -46
  55. agno/db/sqlite/utils.py +169 -8
  56. agno/db/surrealdb/__init__.py +3 -0
  57. agno/db/surrealdb/metrics.py +292 -0
  58. agno/db/surrealdb/models.py +334 -0
  59. agno/db/surrealdb/queries.py +71 -0
  60. agno/db/surrealdb/surrealdb.py +1908 -0
  61. agno/db/surrealdb/utils.py +147 -0
  62. agno/db/utils.py +2 -0
  63. agno/eval/__init__.py +10 -0
  64. agno/eval/accuracy.py +75 -55
  65. agno/eval/agent_as_judge.py +861 -0
  66. agno/eval/base.py +29 -0
  67. agno/eval/performance.py +16 -7
  68. agno/eval/reliability.py +28 -16
  69. agno/eval/utils.py +35 -17
  70. agno/exceptions.py +27 -2
  71. agno/filters.py +354 -0
  72. agno/guardrails/prompt_injection.py +1 -0
  73. agno/hooks/__init__.py +3 -0
  74. agno/hooks/decorator.py +164 -0
  75. agno/integrations/discord/client.py +1 -1
  76. agno/knowledge/chunking/agentic.py +13 -10
  77. agno/knowledge/chunking/fixed.py +4 -1
  78. agno/knowledge/chunking/semantic.py +9 -4
  79. agno/knowledge/chunking/strategy.py +59 -15
  80. agno/knowledge/embedder/fastembed.py +1 -1
  81. agno/knowledge/embedder/nebius.py +1 -1
  82. agno/knowledge/embedder/ollama.py +8 -0
  83. agno/knowledge/embedder/openai.py +8 -8
  84. agno/knowledge/embedder/sentence_transformer.py +6 -2
  85. agno/knowledge/embedder/vllm.py +262 -0
  86. agno/knowledge/knowledge.py +1618 -318
  87. agno/knowledge/reader/base.py +6 -2
  88. agno/knowledge/reader/csv_reader.py +8 -10
  89. agno/knowledge/reader/docx_reader.py +5 -6
  90. agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
  91. agno/knowledge/reader/json_reader.py +5 -4
  92. agno/knowledge/reader/markdown_reader.py +8 -8
  93. agno/knowledge/reader/pdf_reader.py +17 -19
  94. agno/knowledge/reader/pptx_reader.py +101 -0
  95. agno/knowledge/reader/reader_factory.py +32 -3
  96. agno/knowledge/reader/s3_reader.py +3 -3
  97. agno/knowledge/reader/tavily_reader.py +193 -0
  98. agno/knowledge/reader/text_reader.py +22 -10
  99. agno/knowledge/reader/web_search_reader.py +1 -48
  100. agno/knowledge/reader/website_reader.py +10 -10
  101. agno/knowledge/reader/wikipedia_reader.py +33 -1
  102. agno/knowledge/types.py +1 -0
  103. agno/knowledge/utils.py +72 -7
  104. agno/media.py +22 -6
  105. agno/memory/__init__.py +14 -1
  106. agno/memory/manager.py +544 -83
  107. agno/memory/strategies/__init__.py +15 -0
  108. agno/memory/strategies/base.py +66 -0
  109. agno/memory/strategies/summarize.py +196 -0
  110. agno/memory/strategies/types.py +37 -0
  111. agno/models/aimlapi/aimlapi.py +17 -0
  112. agno/models/anthropic/claude.py +515 -40
  113. agno/models/aws/bedrock.py +102 -21
  114. agno/models/aws/claude.py +131 -274
  115. agno/models/azure/ai_foundry.py +41 -19
  116. agno/models/azure/openai_chat.py +39 -8
  117. agno/models/base.py +1249 -525
  118. agno/models/cerebras/cerebras.py +91 -21
  119. agno/models/cerebras/cerebras_openai.py +21 -2
  120. agno/models/cohere/chat.py +40 -6
  121. agno/models/cometapi/cometapi.py +18 -1
  122. agno/models/dashscope/dashscope.py +2 -3
  123. agno/models/deepinfra/deepinfra.py +18 -1
  124. agno/models/deepseek/deepseek.py +69 -3
  125. agno/models/fireworks/fireworks.py +18 -1
  126. agno/models/google/gemini.py +877 -80
  127. agno/models/google/utils.py +22 -0
  128. agno/models/groq/groq.py +51 -18
  129. agno/models/huggingface/huggingface.py +17 -6
  130. agno/models/ibm/watsonx.py +16 -6
  131. agno/models/internlm/internlm.py +18 -1
  132. agno/models/langdb/langdb.py +13 -1
  133. agno/models/litellm/chat.py +44 -9
  134. agno/models/litellm/litellm_openai.py +18 -1
  135. agno/models/message.py +28 -5
  136. agno/models/meta/llama.py +47 -14
  137. agno/models/meta/llama_openai.py +22 -17
  138. agno/models/mistral/mistral.py +8 -4
  139. agno/models/nebius/nebius.py +6 -7
  140. agno/models/nvidia/nvidia.py +20 -3
  141. agno/models/ollama/chat.py +24 -8
  142. agno/models/openai/chat.py +104 -29
  143. agno/models/openai/responses.py +101 -81
  144. agno/models/openrouter/openrouter.py +60 -3
  145. agno/models/perplexity/perplexity.py +17 -1
  146. agno/models/portkey/portkey.py +7 -6
  147. agno/models/requesty/requesty.py +24 -4
  148. agno/models/response.py +73 -2
  149. agno/models/sambanova/sambanova.py +20 -3
  150. agno/models/siliconflow/siliconflow.py +19 -2
  151. agno/models/together/together.py +20 -3
  152. agno/models/utils.py +254 -8
  153. agno/models/vercel/v0.py +20 -3
  154. agno/models/vertexai/__init__.py +0 -0
  155. agno/models/vertexai/claude.py +190 -0
  156. agno/models/vllm/vllm.py +19 -14
  157. agno/models/xai/xai.py +19 -2
  158. agno/os/app.py +549 -152
  159. agno/os/auth.py +190 -3
  160. agno/os/config.py +23 -0
  161. agno/os/interfaces/a2a/router.py +8 -11
  162. agno/os/interfaces/a2a/utils.py +1 -1
  163. agno/os/interfaces/agui/router.py +18 -3
  164. agno/os/interfaces/agui/utils.py +152 -39
  165. agno/os/interfaces/slack/router.py +55 -37
  166. agno/os/interfaces/slack/slack.py +9 -1
  167. agno/os/interfaces/whatsapp/router.py +0 -1
  168. agno/os/interfaces/whatsapp/security.py +3 -1
  169. agno/os/mcp.py +110 -52
  170. agno/os/middleware/__init__.py +2 -0
  171. agno/os/middleware/jwt.py +676 -112
  172. agno/os/router.py +40 -1478
  173. agno/os/routers/agents/__init__.py +3 -0
  174. agno/os/routers/agents/router.py +599 -0
  175. agno/os/routers/agents/schema.py +261 -0
  176. agno/os/routers/evals/evals.py +96 -39
  177. agno/os/routers/evals/schemas.py +65 -33
  178. agno/os/routers/evals/utils.py +80 -10
  179. agno/os/routers/health.py +10 -4
  180. agno/os/routers/knowledge/knowledge.py +196 -38
  181. agno/os/routers/knowledge/schemas.py +82 -22
  182. agno/os/routers/memory/memory.py +279 -52
  183. agno/os/routers/memory/schemas.py +46 -17
  184. agno/os/routers/metrics/metrics.py +20 -8
  185. agno/os/routers/metrics/schemas.py +16 -16
  186. agno/os/routers/session/session.py +462 -34
  187. agno/os/routers/teams/__init__.py +3 -0
  188. agno/os/routers/teams/router.py +512 -0
  189. agno/os/routers/teams/schema.py +257 -0
  190. agno/os/routers/traces/__init__.py +3 -0
  191. agno/os/routers/traces/schemas.py +414 -0
  192. agno/os/routers/traces/traces.py +499 -0
  193. agno/os/routers/workflows/__init__.py +3 -0
  194. agno/os/routers/workflows/router.py +624 -0
  195. agno/os/routers/workflows/schema.py +75 -0
  196. agno/os/schema.py +256 -693
  197. agno/os/scopes.py +469 -0
  198. agno/os/utils.py +514 -36
  199. agno/reasoning/anthropic.py +80 -0
  200. agno/reasoning/gemini.py +73 -0
  201. agno/reasoning/openai.py +5 -0
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +155 -32
  205. agno/run/base.py +55 -3
  206. agno/run/requirement.py +181 -0
  207. agno/run/team.py +125 -38
  208. agno/run/workflow.py +72 -18
  209. agno/session/agent.py +102 -89
  210. agno/session/summary.py +56 -15
  211. agno/session/team.py +164 -90
  212. agno/session/workflow.py +405 -40
  213. agno/table.py +10 -0
  214. agno/team/team.py +3974 -1903
  215. agno/tools/dalle.py +2 -4
  216. agno/tools/eleven_labs.py +23 -25
  217. agno/tools/exa.py +21 -16
  218. agno/tools/file.py +153 -23
  219. agno/tools/file_generation.py +16 -10
  220. agno/tools/firecrawl.py +15 -7
  221. agno/tools/function.py +193 -38
  222. agno/tools/gmail.py +238 -14
  223. agno/tools/google_drive.py +271 -0
  224. agno/tools/googlecalendar.py +36 -8
  225. agno/tools/googlesheets.py +20 -5
  226. agno/tools/jira.py +20 -0
  227. agno/tools/mcp/__init__.py +10 -0
  228. agno/tools/mcp/mcp.py +331 -0
  229. agno/tools/mcp/multi_mcp.py +347 -0
  230. agno/tools/mcp/params.py +24 -0
  231. agno/tools/mcp_toolbox.py +3 -3
  232. agno/tools/models/nebius.py +5 -5
  233. agno/tools/models_labs.py +20 -10
  234. agno/tools/nano_banana.py +151 -0
  235. agno/tools/notion.py +204 -0
  236. agno/tools/parallel.py +314 -0
  237. agno/tools/postgres.py +76 -36
  238. agno/tools/redshift.py +406 -0
  239. agno/tools/scrapegraph.py +1 -1
  240. agno/tools/shopify.py +1519 -0
  241. agno/tools/slack.py +18 -3
  242. agno/tools/spotify.py +919 -0
  243. agno/tools/tavily.py +146 -0
  244. agno/tools/toolkit.py +25 -0
  245. agno/tools/workflow.py +8 -1
  246. agno/tools/yfinance.py +12 -11
  247. agno/tracing/__init__.py +12 -0
  248. agno/tracing/exporter.py +157 -0
  249. agno/tracing/schemas.py +276 -0
  250. agno/tracing/setup.py +111 -0
  251. agno/utils/agent.py +938 -0
  252. agno/utils/cryptography.py +22 -0
  253. agno/utils/dttm.py +33 -0
  254. agno/utils/events.py +151 -3
  255. agno/utils/gemini.py +15 -5
  256. agno/utils/hooks.py +118 -4
  257. agno/utils/http.py +113 -2
  258. agno/utils/knowledge.py +12 -5
  259. agno/utils/log.py +1 -0
  260. agno/utils/mcp.py +92 -2
  261. agno/utils/media.py +187 -1
  262. agno/utils/merge_dict.py +3 -3
  263. agno/utils/message.py +60 -0
  264. agno/utils/models/ai_foundry.py +9 -2
  265. agno/utils/models/claude.py +49 -14
  266. agno/utils/models/cohere.py +9 -2
  267. agno/utils/models/llama.py +9 -2
  268. agno/utils/models/mistral.py +4 -2
  269. agno/utils/print_response/agent.py +109 -16
  270. agno/utils/print_response/team.py +223 -30
  271. agno/utils/print_response/workflow.py +251 -34
  272. agno/utils/streamlit.py +1 -1
  273. agno/utils/team.py +98 -9
  274. agno/utils/tokens.py +657 -0
  275. agno/vectordb/base.py +39 -7
  276. agno/vectordb/cassandra/cassandra.py +21 -5
  277. agno/vectordb/chroma/chromadb.py +43 -12
  278. agno/vectordb/clickhouse/clickhousedb.py +21 -5
  279. agno/vectordb/couchbase/couchbase.py +29 -5
  280. agno/vectordb/lancedb/lance_db.py +92 -181
  281. agno/vectordb/langchaindb/langchaindb.py +24 -4
  282. agno/vectordb/lightrag/lightrag.py +17 -3
  283. agno/vectordb/llamaindex/llamaindexdb.py +25 -5
  284. agno/vectordb/milvus/milvus.py +50 -37
  285. agno/vectordb/mongodb/__init__.py +7 -1
  286. agno/vectordb/mongodb/mongodb.py +36 -30
  287. agno/vectordb/pgvector/pgvector.py +201 -77
  288. agno/vectordb/pineconedb/pineconedb.py +41 -23
  289. agno/vectordb/qdrant/qdrant.py +67 -54
  290. agno/vectordb/redis/__init__.py +9 -0
  291. agno/vectordb/redis/redisdb.py +682 -0
  292. agno/vectordb/singlestore/singlestore.py +50 -29
  293. agno/vectordb/surrealdb/surrealdb.py +31 -41
  294. agno/vectordb/upstashdb/upstashdb.py +34 -6
  295. agno/vectordb/weaviate/weaviate.py +53 -14
  296. agno/workflow/__init__.py +2 -0
  297. agno/workflow/agent.py +299 -0
  298. agno/workflow/condition.py +120 -18
  299. agno/workflow/loop.py +77 -10
  300. agno/workflow/parallel.py +231 -143
  301. agno/workflow/router.py +118 -17
  302. agno/workflow/step.py +609 -170
  303. agno/workflow/steps.py +73 -6
  304. agno/workflow/types.py +96 -21
  305. agno/workflow/workflow.py +2039 -262
  306. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
  307. agno-2.3.13.dist-info/RECORD +613 -0
  308. agno/tools/googlesearch.py +0 -98
  309. agno/tools/mcp.py +0 -679
  310. agno/tools/memori.py +0 -339
  311. agno-2.1.2.dist-info/RECORD +0 -543
  312. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
  313. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
  314. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/workflow/steps.py CHANGED
@@ -1,8 +1,10 @@
1
+ import warnings
1
2
  from dataclasses import dataclass
2
3
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
3
4
  from uuid import uuid4
4
5
 
5
6
  from agno.run.agent import RunOutputEvent
7
+ from agno.run.base import RunContext
6
8
  from agno.run.team import TeamRunOutputEvent
7
9
  from agno.run.workflow import (
8
10
  StepsExecutionCompletedEvent,
@@ -10,6 +12,7 @@ from agno.run.workflow import (
10
12
  WorkflowRunOutput,
11
13
  WorkflowRunOutputEvent,
12
14
  )
15
+ from agno.session.workflow import WorkflowSession
13
16
  from agno.utils.log import log_debug, logger
14
17
  from agno.workflow.step import Step, StepInput, StepOutput, StepType
15
18
 
@@ -117,8 +120,13 @@ class Steps:
117
120
  session_id: Optional[str] = None,
118
121
  user_id: Optional[str] = None,
119
122
  workflow_run_response: Optional[WorkflowRunOutput] = None,
123
+ run_context: Optional[RunContext] = None,
120
124
  session_state: Optional[Dict[str, Any]] = None,
121
125
  store_executor_outputs: bool = True,
126
+ workflow_session: Optional[WorkflowSession] = None,
127
+ add_workflow_history_to_steps: Optional[bool] = False,
128
+ num_history_runs: int = 3,
129
+ background_tasks: Optional[Any] = None,
122
130
  ) -> StepOutput:
123
131
  """Execute all steps in sequence and return the final result"""
124
132
  log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
@@ -147,7 +155,12 @@ class Steps:
147
155
  user_id=user_id,
148
156
  workflow_run_response=workflow_run_response,
149
157
  store_executor_outputs=store_executor_outputs,
158
+ run_context=run_context,
150
159
  session_state=session_state,
160
+ workflow_session=workflow_session,
161
+ add_workflow_history_to_steps=add_workflow_history_to_steps,
162
+ num_history_runs=num_history_runs,
163
+ background_tasks=background_tasks,
151
164
  )
152
165
 
153
166
  # Handle both single StepOutput and List[StepOutput] (from Loop/Condition/Router steps)
@@ -197,13 +210,20 @@ class Steps:
197
210
  self,
198
211
  step_input: StepInput,
199
212
  workflow_run_response: WorkflowRunOutput,
213
+ run_context: Optional[RunContext] = None,
200
214
  session_state: Optional[Dict[str, Any]] = None,
201
215
  session_id: Optional[str] = None,
202
216
  user_id: Optional[str] = None,
217
+ stream_events: bool = False,
203
218
  stream_intermediate_steps: bool = False,
219
+ stream_executor_events: bool = True,
204
220
  step_index: Optional[Union[int, tuple]] = None,
205
221
  store_executor_outputs: bool = True,
206
222
  parent_step_id: Optional[str] = None,
223
+ workflow_session: Optional[WorkflowSession] = None,
224
+ add_workflow_history_to_steps: Optional[bool] = False,
225
+ num_history_runs: int = 3,
226
+ background_tasks: Optional[Any] = None,
207
227
  ) -> Iterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
208
228
  """Execute all steps in sequence with streaming support"""
209
229
  log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
@@ -212,7 +232,16 @@ class Steps:
212
232
 
213
233
  self._prepare_steps()
214
234
 
215
- if stream_intermediate_steps:
235
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
236
+ if stream_intermediate_steps is not None:
237
+ warnings.warn(
238
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
239
+ DeprecationWarning,
240
+ stacklevel=2,
241
+ )
242
+ stream_events = stream_events or stream_intermediate_steps
243
+
244
+ if stream_events:
216
245
  # Yield steps execution started event
217
246
  yield StepsExecutionStartedEvent(
218
247
  run_id=workflow_run_response.run_id or "",
@@ -254,12 +283,18 @@ class Steps:
254
283
  current_step_input,
255
284
  session_id=session_id,
256
285
  user_id=user_id,
286
+ run_context=run_context,
257
287
  session_state=session_state,
258
- stream_intermediate_steps=stream_intermediate_steps,
288
+ stream_events=stream_events,
289
+ stream_executor_events=stream_executor_events,
259
290
  workflow_run_response=workflow_run_response,
260
291
  step_index=child_step_index,
261
292
  store_executor_outputs=store_executor_outputs,
262
293
  parent_step_id=steps_id,
294
+ workflow_session=workflow_session,
295
+ add_workflow_history_to_steps=add_workflow_history_to_steps,
296
+ num_history_runs=num_history_runs,
297
+ background_tasks=background_tasks,
263
298
  ):
264
299
  if isinstance(event, StepOutput):
265
300
  step_outputs_for_step.append(event)
@@ -294,7 +329,7 @@ class Steps:
294
329
 
295
330
  log_debug(f"Steps End: {self.name} ({len(all_results)} results)", center=True, symbol="-")
296
331
 
297
- if stream_intermediate_steps:
332
+ if stream_events:
298
333
  # Yield steps execution completed event
299
334
  yield StepsExecutionCompletedEvent(
300
335
  run_id=workflow_run_response.run_id or "",
@@ -335,8 +370,13 @@ class Steps:
335
370
  session_id: Optional[str] = None,
336
371
  user_id: Optional[str] = None,
337
372
  workflow_run_response: Optional[WorkflowRunOutput] = None,
373
+ run_context: Optional[RunContext] = None,
338
374
  session_state: Optional[Dict[str, Any]] = None,
339
375
  store_executor_outputs: bool = True,
376
+ workflow_session: Optional[WorkflowSession] = None,
377
+ add_workflow_history_to_steps: Optional[bool] = False,
378
+ num_history_runs: int = 3,
379
+ background_tasks: Optional[Any] = None,
340
380
  ) -> StepOutput:
341
381
  """Execute all steps in sequence asynchronously and return the final result"""
342
382
  log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
@@ -365,7 +405,12 @@ class Steps:
365
405
  user_id=user_id,
366
406
  workflow_run_response=workflow_run_response,
367
407
  store_executor_outputs=store_executor_outputs,
408
+ run_context=run_context,
368
409
  session_state=session_state,
410
+ workflow_session=workflow_session,
411
+ add_workflow_history_to_steps=add_workflow_history_to_steps,
412
+ num_history_runs=num_history_runs,
413
+ background_tasks=background_tasks,
369
414
  )
370
415
 
371
416
  # Handle both single StepOutput and List[StepOutput] (from Loop/Condition/Router steps)
@@ -414,13 +459,20 @@ class Steps:
414
459
  self,
415
460
  step_input: StepInput,
416
461
  workflow_run_response: WorkflowRunOutput,
462
+ run_context: Optional[RunContext] = None,
417
463
  session_state: Optional[Dict[str, Any]] = None,
418
464
  session_id: Optional[str] = None,
419
465
  user_id: Optional[str] = None,
466
+ stream_events: bool = False,
420
467
  stream_intermediate_steps: bool = False,
468
+ stream_executor_events: bool = True,
421
469
  step_index: Optional[Union[int, tuple]] = None,
422
470
  store_executor_outputs: bool = True,
423
471
  parent_step_id: Optional[str] = None,
472
+ workflow_session: Optional[WorkflowSession] = None,
473
+ add_workflow_history_to_steps: Optional[bool] = False,
474
+ num_history_runs: int = 3,
475
+ background_tasks: Optional[Any] = None,
424
476
  ) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
425
477
  """Execute all steps in sequence with async streaming support"""
426
478
  log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
@@ -429,7 +481,16 @@ class Steps:
429
481
 
430
482
  self._prepare_steps()
431
483
 
432
- if stream_intermediate_steps:
484
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
485
+ if stream_intermediate_steps is not None:
486
+ warnings.warn(
487
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
488
+ DeprecationWarning,
489
+ stacklevel=2,
490
+ )
491
+ stream_events = stream_events or stream_intermediate_steps
492
+
493
+ if stream_events:
433
494
  # Yield steps execution started event
434
495
  yield StepsExecutionStartedEvent(
435
496
  run_id=workflow_run_response.run_id or "",
@@ -471,12 +532,18 @@ class Steps:
471
532
  current_step_input,
472
533
  session_id=session_id,
473
534
  user_id=user_id,
535
+ run_context=run_context,
474
536
  session_state=session_state,
475
- stream_intermediate_steps=stream_intermediate_steps,
537
+ stream_events=stream_events,
538
+ stream_executor_events=stream_executor_events,
476
539
  workflow_run_response=workflow_run_response,
477
540
  step_index=child_step_index,
478
541
  store_executor_outputs=store_executor_outputs,
479
542
  parent_step_id=steps_id,
543
+ workflow_session=workflow_session,
544
+ add_workflow_history_to_steps=add_workflow_history_to_steps,
545
+ num_history_runs=num_history_runs,
546
+ background_tasks=background_tasks,
480
547
  ):
481
548
  if isinstance(event, StepOutput):
482
549
  step_outputs_for_step.append(event)
@@ -511,7 +578,7 @@ class Steps:
511
578
 
512
579
  log_debug(f"Steps End: {self.name} ({len(all_results)} results)", center=True, symbol="-")
513
580
  # Yield steps execution completed event
514
- if stream_intermediate_steps:
581
+ if stream_events:
515
582
  yield StepsExecutionCompletedEvent(
516
583
  run_id=workflow_run_response.run_id or "",
517
584
  workflow_name=workflow_run_response.workflow_name or "",
agno/workflow/types.py CHANGED
@@ -1,15 +1,23 @@
1
1
  import json
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum
4
- from typing import Any, Dict, List, Optional, Union
4
+ from typing import Any, Dict, List, Optional, Tuple, Union
5
5
 
6
6
  from fastapi import WebSocket
7
7
  from pydantic import BaseModel
8
8
 
9
9
  from agno.media import Audio, File, Image, Video
10
10
  from agno.models.metrics import Metrics
11
+ from agno.session.workflow import WorkflowSession
11
12
  from agno.utils.log import log_warning
13
+ from agno.utils.media import (
14
+ reconstruct_audio_list,
15
+ reconstruct_files,
16
+ reconstruct_images,
17
+ reconstruct_videos,
18
+ )
12
19
  from agno.utils.serialize import json_serializer
20
+ from agno.utils.timer import Timer
13
21
 
14
22
 
15
23
  @dataclass
@@ -80,6 +88,8 @@ class StepInput:
80
88
  audio: Optional[List[Audio]] = None
81
89
  files: Optional[List[File]] = None
82
90
 
91
+ workflow_session: Optional["WorkflowSession"] = None
92
+
83
93
  def get_input_as_string(self) -> Optional[str]:
84
94
  """Convert input to string representation"""
85
95
  if self.input is None:
@@ -97,10 +107,46 @@ class StepInput:
97
107
  return str(self.input)
98
108
 
99
109
  def get_step_output(self, step_name: str) -> Optional["StepOutput"]:
100
- """Get output from a specific previous step by name"""
110
+ """Get output from a specific previous step by name
111
+
112
+ Searches recursively through nested steps (Parallel, Condition, Router, Loop, Steps)
113
+ to find step outputs at any depth.
114
+ """
115
+ if not self.previous_step_outputs:
116
+ return None
117
+
118
+ # First try direct lookup
119
+ direct = self.previous_step_outputs.get(step_name)
120
+ if direct:
121
+ return direct
122
+
123
+ # Search recursively in nested steps
124
+ return self._search_nested_steps(step_name)
125
+
126
+ def _search_nested_steps(self, step_name: str) -> Optional["StepOutput"]:
127
+ """Recursively search for a step output in nested steps (Parallel, Condition, etc.)"""
101
128
  if not self.previous_step_outputs:
102
129
  return None
103
- return self.previous_step_outputs.get(step_name)
130
+
131
+ for step_output in self.previous_step_outputs.values():
132
+ result = self._search_in_step_output(step_output, step_name)
133
+ if result:
134
+ return result
135
+ return None
136
+
137
+ def _search_in_step_output(self, step_output: "StepOutput", step_name: str) -> Optional["StepOutput"]:
138
+ """Helper to recursively search within a single StepOutput"""
139
+ if not step_output.steps:
140
+ return None
141
+
142
+ for nested_step in step_output.steps:
143
+ if nested_step.step_name == step_name:
144
+ return nested_step
145
+ # Recursively search deeper
146
+ result = self._search_in_step_output(nested_step, step_name)
147
+ if result:
148
+ return result
149
+ return None
104
150
 
105
151
  def get_step_content(self, step_name: str) -> Optional[Union[str, Dict[str, str]]]:
106
152
  """Get content from a specific previous step by name
@@ -171,13 +217,35 @@ class StepInput:
171
217
  # Use the helper method to get the deepest content
172
218
  return self._get_deepest_step_content(last_output) # type: ignore[return-value]
173
219
 
220
+ def get_workflow_history(self, num_runs: Optional[int] = None) -> List[Tuple[str, str]]:
221
+ """Get workflow conversation history as structured data for custom function steps
222
+
223
+ Args:
224
+ num_runs: Number of recent runs to include. If None, returns all available history.
225
+ """
226
+ if not self.workflow_session:
227
+ return []
228
+
229
+ return self.workflow_session.get_workflow_history(num_runs=num_runs)
230
+
231
+ def get_workflow_history_context(self, num_runs: Optional[int] = None) -> Optional[str]:
232
+ """Get formatted workflow conversation history context for custom function steps
233
+
234
+ Args:
235
+ num_runs: Number of recent runs to include. If None, returns all available history.
236
+ """
237
+ if not self.workflow_session:
238
+ return None
239
+
240
+ return self.workflow_session.get_workflow_history_context(num_runs=num_runs)
241
+
174
242
  def to_dict(self) -> Dict[str, Any]:
175
243
  """Convert to dictionary"""
176
244
  # Handle the unified message field
177
245
  input_dict: Optional[Union[str, Dict[str, Any], List[Any]]] = None
178
246
  if self.input is not None:
179
247
  if isinstance(self.input, BaseModel):
180
- input_dict = self.input.model_dump(exclude_none=True)
248
+ input_dict = self.input.model_dump(exclude_none=True, mode="json")
181
249
  elif isinstance(self.input, (dict, list)):
182
250
  input_dict = self.input
183
251
  else:
@@ -249,7 +317,7 @@ class StepOutput:
249
317
  content_dict: Optional[Union[str, Dict[str, Any], List[Any]]] = None
250
318
  if self.content is not None:
251
319
  if isinstance(self.content, BaseModel):
252
- content_dict = self.content.model_dump(exclude_none=True)
320
+ content_dict = self.content.model_dump(exclude_none=True, mode="json")
253
321
  elif isinstance(self.content, (dict, list)):
254
322
  content_dict = self.content
255
323
  else:
@@ -283,21 +351,10 @@ class StepOutput:
283
351
  def from_dict(cls, data: Dict[str, Any]) -> "StepOutput":
284
352
  """Create StepOutput from dictionary"""
285
353
  # Reconstruct media artifacts
286
- images = data.get("images")
287
- if images:
288
- images = [Image.model_validate(img) for img in images]
289
-
290
- videos = data.get("videos")
291
- if videos:
292
- videos = [Video.model_validate(vid) for vid in videos]
293
-
294
- audio = data.get("audio")
295
- if audio:
296
- audio = [Audio.model_validate(aud) for aud in audio]
297
-
298
- files = data.get("files")
299
- if files:
300
- files = [File.model_validate(file) for file in files]
354
+ images = reconstruct_images(data.get("images"))
355
+ videos = reconstruct_videos(data.get("videos"))
356
+ audio = reconstruct_audio_list(data.get("audio"))
357
+ files = reconstruct_files(data.get("files"))
301
358
 
302
359
  metrics_data = data.get("metrics")
303
360
  metrics = None
@@ -385,12 +442,18 @@ class WorkflowMetrics:
385
442
  """Complete metrics for a workflow execution"""
386
443
 
387
444
  steps: Dict[str, StepMetrics]
445
+ # Timer utility for tracking execution time
446
+ timer: Optional[Timer] = None
447
+ # Total workflow execution time
448
+ duration: Optional[float] = None
388
449
 
389
450
  def to_dict(self) -> Dict[str, Any]:
390
451
  """Convert to dictionary"""
391
- return {
452
+ result: Dict[str, Any] = {
392
453
  "steps": {name: step.to_dict() for name, step in self.steps.items()},
454
+ "duration": self.duration,
393
455
  }
456
+ return result
394
457
 
395
458
  @classmethod
396
459
  def from_dict(cls, data: Dict[str, Any]) -> "WorkflowMetrics":
@@ -399,8 +462,20 @@ class WorkflowMetrics:
399
462
 
400
463
  return cls(
401
464
  steps=steps,
465
+ duration=data.get("duration"),
402
466
  )
403
467
 
468
+ def start_timer(self):
469
+ if self.timer is None:
470
+ self.timer = Timer()
471
+ self.timer.start()
472
+
473
+ def stop_timer(self, set_duration: bool = True):
474
+ if self.timer is not None:
475
+ self.timer.stop()
476
+ if set_duration:
477
+ self.duration = self.timer.elapsed
478
+
404
479
 
405
480
  @dataclass
406
481
  class WebSocketHandler: