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/agent.py ADDED
@@ -0,0 +1,299 @@
1
+ """WorkflowAgent - A restricted Agent for workflow orchestration"""
2
+
3
+ from typing import TYPE_CHECKING, Any, Callable, Optional
4
+
5
+ from agno.agent import Agent
6
+ from agno.models.base import Model
7
+ from agno.run import RunContext
8
+ from agno.workflow.types import WebSocketHandler
9
+
10
+ if TYPE_CHECKING:
11
+ from agno.session.workflow import WorkflowSession
12
+ from agno.workflow.types import WorkflowExecutionInput
13
+
14
+
15
+ class WorkflowAgent(Agent):
16
+ """
17
+ A restricted Agent class specifically designed for workflow orchestration.
18
+ This agent can:
19
+ 1. Decide whether to run the workflow or answer directly from history
20
+ 2. Call the workflow execution tool when needed
21
+ 3. Access workflow session history for context
22
+ Restrictions:
23
+ - Only model configuration allowed
24
+ - No custom tools (tools are set by workflow)
25
+ - No knowledge base
26
+ - Limited configuration options
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ model: Model,
32
+ instructions: Optional[str] = None,
33
+ add_workflow_history: bool = True,
34
+ num_history_runs: int = 5,
35
+ ):
36
+ """
37
+ Initialize WorkflowAgent with restricted parameters.
38
+ Args:
39
+ model: The model to use for the agent (required)
40
+ instructions: Custom instructions (will be combined with workflow context)
41
+ add_workflow_history: Whether to add workflow history to context (default: True)
42
+ num_history_runs: Number of previous workflow runs to include in context (default: 5)
43
+ """
44
+ self.add_workflow_history = add_workflow_history
45
+
46
+ default_instructions = """You are a workflow orchestration agent. Your job is to help users by either:
47
+ 1. **Answering directly** from the workflow history context if the question can be answered from previous runs
48
+ 2. **Running the workflow** by calling the run_workflow tool ONCE when you need to process a new query
49
+
50
+ Guidelines:
51
+ - ALWAYS check the workflow history first before calling the tool
52
+ - Answer directly from history if:
53
+ * The user asks about something already in history
54
+ * The user asks for comparisons/analysis of things in history (e.g., "compare X and Y")
55
+ * The user asks follow-up questions about previous results
56
+ - Only call the run_workflow tool for NEW topics not covered in history
57
+ - IMPORTANT: Do NOT call the tool multiple times. Call it once and use the result.
58
+ - Keep your responses concise and helpful
59
+ - When you must call the workflow, pass a clear and concise query
60
+
61
+ {workflow_context}
62
+ """
63
+
64
+ if instructions:
65
+ if "{workflow_context}" not in instructions:
66
+ # Add the workflow context placeholder
67
+ final_instructions = f"{instructions}\n\n{{workflow_context}}"
68
+ else:
69
+ final_instructions = instructions
70
+ else:
71
+ final_instructions = default_instructions
72
+
73
+ super().__init__(
74
+ model=model,
75
+ instructions=final_instructions,
76
+ resolve_in_context=True,
77
+ num_history_runs=num_history_runs,
78
+ )
79
+
80
+ def create_workflow_tool(
81
+ self,
82
+ workflow: "Any", # Workflow type
83
+ session: "WorkflowSession",
84
+ execution_input: "WorkflowExecutionInput",
85
+ run_context: RunContext,
86
+ stream: bool = False,
87
+ ) -> Callable:
88
+ """
89
+ Create the workflow execution tool that this agent can call.
90
+ This is similar to how Agent has search_knowledge_base() method.
91
+ Args:
92
+ workflow: The workflow instance
93
+ session: The workflow session
94
+ execution_input: The execution input
95
+ run_context: The run context
96
+ stream: Whether to stream the workflow execution
97
+ Returns:
98
+ Callable tool function
99
+ """
100
+ from datetime import datetime
101
+ from uuid import uuid4
102
+
103
+ from pydantic import BaseModel
104
+
105
+ from agno.run.workflow import WorkflowRunOutput
106
+ from agno.utils.log import log_debug
107
+ from agno.workflow.types import WorkflowExecutionInput
108
+
109
+ def run_workflow(query: str):
110
+ """
111
+ Execute the complete workflow with the given query.
112
+ Use this tool when you need to run the workflow to answer the user's question.
113
+
114
+ Args:
115
+ query: The input query/question to process through the workflow
116
+ Returns:
117
+ The workflow execution result (str in non-streaming, generator in streaming)
118
+ """
119
+ # Reload session to get latest data from database
120
+ # This ensures we don't overwrite any updates made after the tool was created
121
+ session_from_db = workflow.get_session(session_id=session.session_id)
122
+ if session_from_db is None:
123
+ session_from_db = session # Fallback to closure session if reload fails
124
+ log_debug(f"Fallback to closure session: {len(session_from_db.runs or [])} runs")
125
+ else:
126
+ log_debug(f"Reloaded session before tool execution: {len(session_from_db.runs or [])} runs")
127
+
128
+ # Create a new run ID for this execution
129
+ run_id = str(uuid4())
130
+
131
+ workflow_run_response = WorkflowRunOutput(
132
+ run_id=run_id,
133
+ input=execution_input.input, # Use original user input
134
+ session_id=session_from_db.session_id,
135
+ workflow_id=workflow.id,
136
+ workflow_name=workflow.name,
137
+ created_at=int(datetime.now().timestamp()),
138
+ )
139
+
140
+ workflow_execution_input = WorkflowExecutionInput(
141
+ input=query, # Agent's refined query for execution
142
+ additional_data=execution_input.additional_data,
143
+ audio=execution_input.audio,
144
+ images=execution_input.images,
145
+ videos=execution_input.videos,
146
+ files=execution_input.files,
147
+ )
148
+
149
+ # ===== EXECUTION LOGIC (Based on streaming mode) =====
150
+ if stream:
151
+ final_content = ""
152
+ for event in workflow._execute_stream(
153
+ session=session_from_db,
154
+ run_context=run_context,
155
+ execution_input=workflow_execution_input,
156
+ workflow_run_response=workflow_run_response,
157
+ stream_events=True,
158
+ ):
159
+ yield event
160
+
161
+ # Capture final content from WorkflowCompletedEvent
162
+ from agno.run.workflow import WorkflowCompletedEvent
163
+
164
+ if isinstance(event, WorkflowCompletedEvent):
165
+ final_content = str(event.content) if event.content else ""
166
+
167
+ return final_content
168
+ else:
169
+ # NON-STREAMING MODE: Execute synchronously
170
+ result = workflow._execute(
171
+ session=session_from_db,
172
+ execution_input=workflow_execution_input,
173
+ workflow_run_response=workflow_run_response,
174
+ run_context=run_context,
175
+ )
176
+
177
+ if isinstance(result.content, str):
178
+ return result.content
179
+ elif isinstance(result.content, BaseModel):
180
+ return result.content.model_dump_json(exclude_none=True)
181
+ else:
182
+ return str(result.content)
183
+
184
+ return run_workflow
185
+
186
+ def async_create_workflow_tool(
187
+ self,
188
+ workflow: "Any", # Workflow type
189
+ session: "WorkflowSession",
190
+ execution_input: "WorkflowExecutionInput",
191
+ run_context: RunContext,
192
+ stream: bool = False,
193
+ websocket_handler: Optional[WebSocketHandler] = None,
194
+ ) -> Callable:
195
+ """
196
+ Create the async workflow execution tool that this agent can call.
197
+ This is the async counterpart of create_workflow_tool.
198
+
199
+ Args:
200
+ workflow: The workflow instance
201
+ session: The workflow session
202
+ execution_input: The execution input
203
+ run_context: The run context
204
+ stream: Whether to stream the workflow execution
205
+
206
+ Returns:
207
+ Async callable tool function
208
+ """
209
+ from datetime import datetime
210
+ from uuid import uuid4
211
+
212
+ from pydantic import BaseModel
213
+
214
+ from agno.run.workflow import WorkflowRunOutput
215
+ from agno.utils.log import log_debug
216
+ from agno.workflow.types import WorkflowExecutionInput
217
+
218
+ async def run_workflow(query: str):
219
+ """
220
+ Execute the complete workflow with the given query asynchronously.
221
+ Use this tool when you need to run the workflow to answer the user's question.
222
+
223
+ Args:
224
+ query: The input query/question to process through the workflow
225
+
226
+ Returns:
227
+ The workflow execution result (str in non-streaming, async generator in streaming)
228
+ """
229
+ # Reload session to get latest data from database
230
+ # This ensures we don't overwrite any updates made after the tool was created
231
+ # Use async or sync method based on database type
232
+ if workflow._has_async_db():
233
+ session_from_db = await workflow.aget_session(session_id=session.session_id)
234
+ else:
235
+ session_from_db = workflow.get_session(session_id=session.session_id)
236
+
237
+ if session_from_db is None:
238
+ session_from_db = session # Fallback to closure session if reload fails
239
+ log_debug(f"Fallback to closure session: {len(session_from_db.runs or [])} runs")
240
+ else:
241
+ log_debug(f"Reloaded session before async tool execution: {len(session_from_db.runs or [])} runs")
242
+
243
+ # Create a new run ID for this execution
244
+ run_id = str(uuid4())
245
+
246
+ workflow_run_response = WorkflowRunOutput(
247
+ run_id=run_id,
248
+ input=execution_input.input, # Use original user input
249
+ session_id=session_from_db.session_id,
250
+ workflow_id=workflow.id,
251
+ workflow_name=workflow.name,
252
+ created_at=int(datetime.now().timestamp()),
253
+ )
254
+
255
+ workflow_execution_input = WorkflowExecutionInput(
256
+ input=query, # Agent's refined query for execution
257
+ additional_data=execution_input.additional_data,
258
+ audio=execution_input.audio,
259
+ images=execution_input.images,
260
+ videos=execution_input.videos,
261
+ files=execution_input.files,
262
+ )
263
+
264
+ if stream:
265
+ final_content = ""
266
+ async for event in workflow._aexecute_stream(
267
+ session_id=session_from_db.session_id,
268
+ user_id=session_from_db.user_id,
269
+ execution_input=workflow_execution_input,
270
+ workflow_run_response=workflow_run_response,
271
+ run_context=run_context,
272
+ stream_events=True,
273
+ websocket_handler=websocket_handler,
274
+ ):
275
+ yield event
276
+
277
+ from agno.run.workflow import WorkflowCompletedEvent
278
+
279
+ if isinstance(event, WorkflowCompletedEvent):
280
+ final_content = str(event.content) if event.content else ""
281
+
282
+ yield final_content
283
+ else:
284
+ result = await workflow._aexecute(
285
+ session_id=session_from_db.session_id,
286
+ user_id=session_from_db.user_id,
287
+ execution_input=workflow_execution_input,
288
+ workflow_run_response=workflow_run_response,
289
+ run_context=run_context,
290
+ )
291
+
292
+ if isinstance(result.content, str):
293
+ yield result.content
294
+ elif isinstance(result.content, BaseModel):
295
+ yield result.content.model_dump_json(exclude_none=True)
296
+ else:
297
+ yield str(result.content)
298
+
299
+ return run_workflow
@@ -1,9 +1,11 @@
1
1
  import inspect
2
+ import warnings
2
3
  from dataclasses import dataclass
3
4
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
4
5
  from uuid import uuid4
5
6
 
6
7
  from agno.run.agent import RunOutputEvent
8
+ from agno.run.base import RunContext
7
9
  from agno.run.team import TeamRunOutputEvent
8
10
  from agno.run.workflow import (
9
11
  ConditionExecutionCompletedEvent,
@@ -11,6 +13,7 @@ from agno.run.workflow import (
11
13
  WorkflowRunOutput,
12
14
  WorkflowRunOutputEvent,
13
15
  )
16
+ from agno.session.workflow import WorkflowSession
14
17
  from agno.utils.log import log_debug, logger
15
18
  from agno.workflow.step import Step
16
19
  from agno.workflow.types import StepInput, StepOutput, StepType
@@ -110,13 +113,16 @@ class Condition:
110
113
  audio=current_audio + all_audio,
111
114
  )
112
115
 
113
- def _evaluate_condition(self, step_input: StepInput) -> bool:
116
+ def _evaluate_condition(self, step_input: StepInput, session_state: Optional[Dict[str, Any]] = None) -> bool:
114
117
  """Evaluate the condition and return boolean result"""
115
118
  if isinstance(self.evaluator, bool):
116
119
  return self.evaluator
117
120
 
118
121
  if callable(self.evaluator):
119
- result = self.evaluator(step_input)
122
+ if session_state is not None and self._evaluator_has_session_state_param():
123
+ result = self.evaluator(step_input, session_state=session_state) # type: ignore[call-arg]
124
+ else:
125
+ result = self.evaluator(step_input)
120
126
 
121
127
  if isinstance(result, bool):
122
128
  return result
@@ -126,16 +132,24 @@ class Condition:
126
132
 
127
133
  return False
128
134
 
129
- async def _aevaluate_condition(self, step_input: StepInput) -> bool:
135
+ async def _aevaluate_condition(self, step_input: StepInput, session_state: Optional[Dict[str, Any]] = None) -> bool:
130
136
  """Async version of condition evaluation"""
131
137
  if isinstance(self.evaluator, bool):
132
138
  return self.evaluator
133
139
 
134
140
  if callable(self.evaluator):
141
+ has_session_state = session_state is not None and self._evaluator_has_session_state_param()
142
+
135
143
  if inspect.iscoroutinefunction(self.evaluator):
136
- result = await self.evaluator(step_input)
144
+ if has_session_state:
145
+ result = await self.evaluator(step_input, session_state=session_state) # type: ignore[call-arg]
146
+ else:
147
+ result = await self.evaluator(step_input)
137
148
  else:
138
- result = self.evaluator(step_input)
149
+ if has_session_state:
150
+ result = self.evaluator(step_input, session_state=session_state) # type: ignore[call-arg]
151
+ else:
152
+ result = self.evaluator(step_input)
139
153
 
140
154
  if isinstance(result, bool):
141
155
  return result
@@ -145,6 +159,17 @@ class Condition:
145
159
 
146
160
  return False
147
161
 
162
+ def _evaluator_has_session_state_param(self) -> bool:
163
+ """Check if the evaluator function has a session_state parameter"""
164
+ if not callable(self.evaluator):
165
+ return False
166
+
167
+ try:
168
+ sig = inspect.signature(self.evaluator)
169
+ return "session_state" in sig.parameters
170
+ except Exception:
171
+ return False
172
+
148
173
  def execute(
149
174
  self,
150
175
  step_input: StepInput,
@@ -152,7 +177,12 @@ class Condition:
152
177
  user_id: Optional[str] = None,
153
178
  workflow_run_response: Optional[WorkflowRunOutput] = None,
154
179
  store_executor_outputs: bool = True,
180
+ run_context: Optional[RunContext] = None,
155
181
  session_state: Optional[Dict[str, Any]] = None,
182
+ workflow_session: Optional[WorkflowSession] = None,
183
+ add_workflow_history_to_steps: Optional[bool] = False,
184
+ num_history_runs: int = 3,
185
+ background_tasks: Optional[Any] = None,
156
186
  ) -> StepOutput:
157
187
  """Execute the condition and its steps with sequential chaining if condition is true"""
158
188
  log_debug(f"Condition Start: {self.name}", center=True, symbol="-")
@@ -162,7 +192,11 @@ class Condition:
162
192
  self._prepare_steps()
163
193
 
164
194
  # Evaluate the condition
165
- condition_result = self._evaluate_condition(step_input)
195
+ if run_context is not None and run_context.session_state is not None:
196
+ condition_result = self._evaluate_condition(step_input, session_state=run_context.session_state)
197
+ else:
198
+ condition_result = self._evaluate_condition(step_input, session_state=session_state)
199
+
166
200
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
167
201
 
168
202
  if not condition_result:
@@ -188,7 +222,12 @@ class Condition:
188
222
  user_id=user_id,
189
223
  workflow_run_response=workflow_run_response,
190
224
  store_executor_outputs=store_executor_outputs,
225
+ run_context=run_context,
191
226
  session_state=session_state,
227
+ workflow_session=workflow_session,
228
+ add_workflow_history_to_steps=add_workflow_history_to_steps,
229
+ num_history_runs=num_history_runs,
230
+ background_tasks=background_tasks,
192
231
  )
193
232
 
194
233
  # Handle both single StepOutput and List[StepOutput] (from Loop/Condition/Router steps)
@@ -249,12 +288,19 @@ class Condition:
249
288
  step_input: StepInput,
250
289
  session_id: Optional[str] = None,
251
290
  user_id: Optional[str] = None,
252
- stream_intermediate_steps: bool = False,
291
+ stream_events: bool = False,
292
+ stream_intermediate_steps: bool = False, # type: ignore
293
+ stream_executor_events: bool = True,
253
294
  workflow_run_response: Optional[WorkflowRunOutput] = None,
254
295
  step_index: Optional[Union[int, tuple]] = None,
255
296
  store_executor_outputs: bool = True,
297
+ run_context: Optional[RunContext] = None,
256
298
  session_state: Optional[Dict[str, Any]] = None,
257
299
  parent_step_id: Optional[str] = None,
300
+ workflow_session: Optional[WorkflowSession] = None,
301
+ add_workflow_history_to_steps: Optional[bool] = False,
302
+ num_history_runs: int = 3,
303
+ background_tasks: Optional[Any] = None,
258
304
  ) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
259
305
  """Execute the condition with streaming support - mirrors Loop logic"""
260
306
  log_debug(f"Condition Start: {self.name}", center=True, symbol="-")
@@ -264,10 +310,22 @@ class Condition:
264
310
  self._prepare_steps()
265
311
 
266
312
  # Evaluate the condition
267
- condition_result = self._evaluate_condition(step_input)
313
+ if run_context is not None and run_context.session_state is not None:
314
+ condition_result = self._evaluate_condition(step_input, session_state=run_context.session_state)
315
+ else:
316
+ condition_result = self._evaluate_condition(step_input, session_state=session_state)
268
317
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
269
318
 
270
- if stream_intermediate_steps and workflow_run_response:
319
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
320
+ if stream_intermediate_steps is not None:
321
+ warnings.warn(
322
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
323
+ DeprecationWarning,
324
+ stacklevel=2,
325
+ )
326
+ stream_events = stream_events or stream_intermediate_steps
327
+
328
+ if stream_events and workflow_run_response:
271
329
  # Yield condition started event
272
330
  yield ConditionExecutionStartedEvent(
273
331
  run_id=workflow_run_response.run_id or "",
@@ -282,7 +340,7 @@ class Condition:
282
340
  )
283
341
 
284
342
  if not condition_result:
285
- if stream_intermediate_steps and workflow_run_response:
343
+ if stream_events and workflow_run_response:
286
344
  # Yield condition completed event for empty case
287
345
  yield ConditionExecutionCompletedEvent(
288
346
  run_id=workflow_run_response.run_id or "",
@@ -321,12 +379,18 @@ class Condition:
321
379
  current_step_input,
322
380
  session_id=session_id,
323
381
  user_id=user_id,
324
- stream_intermediate_steps=stream_intermediate_steps,
382
+ stream_events=stream_events,
383
+ stream_executor_events=stream_executor_events,
325
384
  workflow_run_response=workflow_run_response,
326
385
  step_index=child_step_index,
327
386
  store_executor_outputs=store_executor_outputs,
387
+ run_context=run_context,
328
388
  session_state=session_state,
329
389
  parent_step_id=conditional_step_id,
390
+ workflow_session=workflow_session,
391
+ add_workflow_history_to_steps=add_workflow_history_to_steps,
392
+ num_history_runs=num_history_runs,
393
+ background_tasks=background_tasks,
330
394
  ):
331
395
  if isinstance(event, StepOutput):
332
396
  step_outputs_for_step.append(event)
@@ -374,7 +438,7 @@ class Condition:
374
438
  break
375
439
 
376
440
  log_debug(f"Condition End: {self.name} ({len(all_results)} results)", center=True, symbol="-")
377
- if stream_intermediate_steps and workflow_run_response:
441
+ if stream_events and workflow_run_response:
378
442
  # Yield condition completed event
379
443
  yield ConditionExecutionCompletedEvent(
380
444
  run_id=workflow_run_response.run_id or "",
@@ -406,7 +470,12 @@ class Condition:
406
470
  user_id: Optional[str] = None,
407
471
  workflow_run_response: Optional[WorkflowRunOutput] = None,
408
472
  store_executor_outputs: bool = True,
473
+ run_context: Optional[RunContext] = None,
409
474
  session_state: Optional[Dict[str, Any]] = None,
475
+ workflow_session: Optional[WorkflowSession] = None,
476
+ add_workflow_history_to_steps: Optional[bool] = False,
477
+ num_history_runs: int = 3,
478
+ background_tasks: Optional[Any] = None,
410
479
  ) -> StepOutput:
411
480
  """Async execute the condition and its steps with sequential chaining"""
412
481
  log_debug(f"Condition Start: {self.name}", center=True, symbol="-")
@@ -416,7 +485,10 @@ class Condition:
416
485
  self._prepare_steps()
417
486
 
418
487
  # Evaluate the condition
419
- condition_result = await self._aevaluate_condition(step_input)
488
+ if run_context is not None and run_context.session_state is not None:
489
+ condition_result = await self._aevaluate_condition(step_input, session_state=run_context.session_state)
490
+ else:
491
+ condition_result = await self._aevaluate_condition(step_input, session_state=session_state)
420
492
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
421
493
 
422
494
  if not condition_result:
@@ -444,7 +516,12 @@ class Condition:
444
516
  user_id=user_id,
445
517
  workflow_run_response=workflow_run_response,
446
518
  store_executor_outputs=store_executor_outputs,
519
+ run_context=run_context,
447
520
  session_state=session_state,
521
+ workflow_session=workflow_session,
522
+ add_workflow_history_to_steps=add_workflow_history_to_steps,
523
+ num_history_runs=num_history_runs,
524
+ background_tasks=background_tasks,
448
525
  )
449
526
 
450
527
  # Handle both single StepOutput and List[StepOutput]
@@ -503,12 +580,19 @@ class Condition:
503
580
  step_input: StepInput,
504
581
  session_id: Optional[str] = None,
505
582
  user_id: Optional[str] = None,
583
+ stream_events: bool = False,
506
584
  stream_intermediate_steps: bool = False,
585
+ stream_executor_events: bool = True,
507
586
  workflow_run_response: Optional[WorkflowRunOutput] = None,
508
587
  step_index: Optional[Union[int, tuple]] = None,
509
588
  store_executor_outputs: bool = True,
589
+ run_context: Optional[RunContext] = None,
510
590
  session_state: Optional[Dict[str, Any]] = None,
511
591
  parent_step_id: Optional[str] = None,
592
+ workflow_session: Optional[WorkflowSession] = None,
593
+ add_workflow_history_to_steps: Optional[bool] = False,
594
+ num_history_runs: int = 3,
595
+ background_tasks: Optional[Any] = None,
512
596
  ) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
513
597
  """Async execute the condition with streaming support - mirrors Loop logic"""
514
598
  log_debug(f"Condition Start: {self.name}", center=True, symbol="-")
@@ -518,10 +602,22 @@ class Condition:
518
602
  self._prepare_steps()
519
603
 
520
604
  # Evaluate the condition
521
- condition_result = await self._aevaluate_condition(step_input)
605
+ if run_context is not None and run_context.session_state is not None:
606
+ condition_result = await self._aevaluate_condition(step_input, session_state=run_context.session_state)
607
+ else:
608
+ condition_result = await self._aevaluate_condition(step_input, session_state=session_state)
522
609
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
523
610
 
524
- if stream_intermediate_steps and workflow_run_response:
611
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
612
+ if stream_intermediate_steps is not None:
613
+ warnings.warn(
614
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
615
+ DeprecationWarning,
616
+ stacklevel=2,
617
+ )
618
+ stream_events = stream_events or stream_intermediate_steps
619
+
620
+ if stream_events and workflow_run_response:
525
621
  # Yield condition started event
526
622
  yield ConditionExecutionStartedEvent(
527
623
  run_id=workflow_run_response.run_id or "",
@@ -536,7 +632,7 @@ class Condition:
536
632
  )
537
633
 
538
634
  if not condition_result:
539
- if stream_intermediate_steps and workflow_run_response:
635
+ if stream_events and workflow_run_response:
540
636
  # Yield condition completed event for empty case
541
637
  yield ConditionExecutionCompletedEvent(
542
638
  run_id=workflow_run_response.run_id or "",
@@ -577,12 +673,18 @@ class Condition:
577
673
  current_step_input,
578
674
  session_id=session_id,
579
675
  user_id=user_id,
580
- stream_intermediate_steps=stream_intermediate_steps,
676
+ stream_events=stream_events,
677
+ stream_executor_events=stream_executor_events,
581
678
  workflow_run_response=workflow_run_response,
582
679
  step_index=child_step_index,
583
680
  store_executor_outputs=store_executor_outputs,
681
+ run_context=run_context,
584
682
  session_state=session_state,
585
683
  parent_step_id=conditional_step_id,
684
+ workflow_session=workflow_session,
685
+ add_workflow_history_to_steps=add_workflow_history_to_steps,
686
+ num_history_runs=num_history_runs,
687
+ background_tasks=background_tasks,
586
688
  ):
587
689
  if isinstance(event, StepOutput):
588
690
  step_outputs_for_step.append(event)
@@ -631,7 +733,7 @@ class Condition:
631
733
 
632
734
  log_debug(f"Condition End: {self.name} ({len(all_results)} results)", center=True, symbol="-")
633
735
 
634
- if stream_intermediate_steps and workflow_run_response:
736
+ if stream_events and workflow_run_response:
635
737
  # Yield condition completed event
636
738
  yield ConditionExecutionCompletedEvent(
637
739
  run_id=workflow_run_response.run_id or "",