agno 1.8.0__py3-none-any.whl → 2.0.0a1__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 (583) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +2781 -4126
  4. agno/api/agent.py +9 -65
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +6 -17
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -41
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +5 -21
  11. agno/api/schemas/evals.py +7 -16
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +5 -21
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +11 -7
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +9 -64
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/db/__init__.py +24 -0
  25. agno/db/base.py +245 -0
  26. agno/db/dynamo/__init__.py +3 -0
  27. agno/db/dynamo/dynamo.py +1749 -0
  28. agno/db/dynamo/schemas.py +278 -0
  29. agno/db/dynamo/utils.py +684 -0
  30. agno/db/firestore/__init__.py +3 -0
  31. agno/db/firestore/firestore.py +1438 -0
  32. agno/db/firestore/schemas.py +130 -0
  33. agno/db/firestore/utils.py +278 -0
  34. agno/db/gcs_json/__init__.py +3 -0
  35. agno/db/gcs_json/gcs_json_db.py +1001 -0
  36. agno/db/gcs_json/utils.py +194 -0
  37. agno/db/in_memory/__init__.py +3 -0
  38. agno/db/in_memory/in_memory_db.py +888 -0
  39. agno/db/in_memory/utils.py +172 -0
  40. agno/db/json/__init__.py +3 -0
  41. agno/db/json/json_db.py +1051 -0
  42. agno/db/json/utils.py +196 -0
  43. agno/db/migrations/v1_to_v2.py +162 -0
  44. agno/db/mongo/__init__.py +3 -0
  45. agno/db/mongo/mongo.py +1417 -0
  46. agno/db/mongo/schemas.py +77 -0
  47. agno/db/mongo/utils.py +204 -0
  48. agno/db/mysql/__init__.py +3 -0
  49. agno/db/mysql/mysql.py +1719 -0
  50. agno/db/mysql/schemas.py +124 -0
  51. agno/db/mysql/utils.py +298 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1720 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +281 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1371 -0
  58. agno/db/redis/schemas.py +109 -0
  59. agno/db/redis/utils.py +288 -0
  60. agno/db/schemas/__init__.py +3 -0
  61. agno/db/schemas/evals.py +33 -0
  62. agno/db/schemas/knowledge.py +40 -0
  63. agno/db/schemas/memory.py +46 -0
  64. agno/db/singlestore/__init__.py +3 -0
  65. agno/db/singlestore/schemas.py +116 -0
  66. agno/db/singlestore/singlestore.py +1722 -0
  67. agno/db/singlestore/utils.py +327 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1680 -0
  71. agno/db/sqlite/utils.py +269 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +142 -43
  75. agno/eval/performance.py +88 -23
  76. agno/eval/reliability.py +73 -20
  77. agno/eval/utils.py +23 -13
  78. agno/integrations/discord/__init__.py +3 -0
  79. agno/{app → integrations}/discord/client.py +10 -10
  80. agno/knowledge/__init__.py +2 -2
  81. agno/{document → knowledge}/chunking/agentic.py +2 -2
  82. agno/{document → knowledge}/chunking/document.py +2 -2
  83. agno/{document → knowledge}/chunking/fixed.py +3 -3
  84. agno/{document → knowledge}/chunking/markdown.py +2 -2
  85. agno/{document → knowledge}/chunking/recursive.py +2 -2
  86. agno/{document → knowledge}/chunking/row.py +2 -2
  87. agno/knowledge/chunking/semantic.py +59 -0
  88. agno/knowledge/chunking/strategy.py +121 -0
  89. agno/knowledge/content.py +74 -0
  90. agno/knowledge/document/__init__.py +5 -0
  91. agno/{document → knowledge/document}/base.py +12 -2
  92. agno/knowledge/embedder/__init__.py +5 -0
  93. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  94. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  95. agno/{embedder → knowledge/embedder}/base.py +6 -0
  96. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  97. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  98. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  99. agno/{embedder → knowledge/embedder}/google.py +74 -1
  100. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  101. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  102. agno/knowledge/embedder/langdb.py +22 -0
  103. agno/knowledge/embedder/mistral.py +139 -0
  104. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  105. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  106. agno/knowledge/embedder/openai.py +223 -0
  107. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  108. agno/{embedder → knowledge/embedder}/together.py +1 -1
  109. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  110. agno/knowledge/knowledge.py +1515 -0
  111. agno/knowledge/reader/__init__.py +7 -0
  112. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  113. agno/knowledge/reader/base.py +88 -0
  114. agno/{document → knowledge}/reader/csv_reader.py +68 -15
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/knowledge/reader/gcs_reader.py +67 -0
  118. agno/{document → knowledge}/reader/json_reader.py +30 -9
  119. agno/{document → knowledge}/reader/markdown_reader.py +36 -9
  120. agno/{document → knowledge}/reader/pdf_reader.py +79 -21
  121. agno/knowledge/reader/reader_factory.py +275 -0
  122. agno/knowledge/reader/s3_reader.py +171 -0
  123. agno/{document → knowledge}/reader/text_reader.py +31 -10
  124. agno/knowledge/reader/url_reader.py +84 -0
  125. agno/knowledge/reader/web_search_reader.py +389 -0
  126. agno/{document → knowledge}/reader/website_reader.py +37 -10
  127. agno/knowledge/reader/wikipedia_reader.py +59 -0
  128. agno/knowledge/reader/youtube_reader.py +78 -0
  129. agno/knowledge/remote_content/remote_content.py +88 -0
  130. agno/{reranker → knowledge/reranker}/base.py +1 -1
  131. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  132. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  133. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  134. agno/knowledge/types.py +30 -0
  135. agno/knowledge/utils.py +169 -0
  136. agno/media.py +2 -2
  137. agno/memory/__init__.py +2 -10
  138. agno/memory/manager.py +1003 -148
  139. agno/models/aimlapi/__init__.py +2 -2
  140. agno/models/aimlapi/aimlapi.py +6 -6
  141. agno/models/anthropic/claude.py +129 -82
  142. agno/models/aws/bedrock.py +107 -175
  143. agno/models/aws/claude.py +64 -18
  144. agno/models/azure/ai_foundry.py +73 -23
  145. agno/models/base.py +347 -287
  146. agno/models/cerebras/cerebras.py +84 -27
  147. agno/models/cohere/chat.py +106 -98
  148. agno/models/dashscope/dashscope.py +14 -5
  149. agno/models/google/gemini.py +123 -53
  150. agno/models/groq/groq.py +97 -35
  151. agno/models/huggingface/huggingface.py +92 -27
  152. agno/models/ibm/watsonx.py +72 -13
  153. agno/models/litellm/chat.py +85 -13
  154. agno/models/message.py +38 -144
  155. agno/models/meta/llama.py +85 -49
  156. agno/models/metrics.py +120 -0
  157. agno/models/mistral/mistral.py +90 -21
  158. agno/models/ollama/__init__.py +0 -2
  159. agno/models/ollama/chat.py +84 -46
  160. agno/models/openai/chat.py +135 -27
  161. agno/models/openai/responses.py +233 -115
  162. agno/models/perplexity/perplexity.py +26 -2
  163. agno/models/portkey/portkey.py +0 -7
  164. agno/models/response.py +14 -8
  165. agno/models/utils.py +20 -0
  166. agno/models/vercel/__init__.py +2 -2
  167. agno/models/vercel/v0.py +1 -1
  168. agno/models/vllm/__init__.py +2 -2
  169. agno/models/vllm/vllm.py +3 -3
  170. agno/models/xai/xai.py +10 -10
  171. agno/os/__init__.py +3 -0
  172. agno/os/app.py +393 -0
  173. agno/os/auth.py +47 -0
  174. agno/os/config.py +103 -0
  175. agno/os/interfaces/agui/__init__.py +3 -0
  176. agno/os/interfaces/agui/agui.py +31 -0
  177. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  178. agno/{app → os/interfaces}/agui/utils.py +65 -28
  179. agno/os/interfaces/base.py +21 -0
  180. agno/os/interfaces/slack/__init__.py +3 -0
  181. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  182. agno/os/interfaces/slack/slack.py +33 -0
  183. agno/os/interfaces/whatsapp/__init__.py +3 -0
  184. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  185. agno/os/interfaces/whatsapp/whatsapp.py +30 -0
  186. agno/os/router.py +843 -0
  187. agno/os/routers/__init__.py +3 -0
  188. agno/os/routers/evals/__init__.py +3 -0
  189. agno/os/routers/evals/evals.py +204 -0
  190. agno/os/routers/evals/schemas.py +142 -0
  191. agno/os/routers/evals/utils.py +161 -0
  192. agno/os/routers/knowledge/__init__.py +3 -0
  193. agno/os/routers/knowledge/knowledge.py +413 -0
  194. agno/os/routers/knowledge/schemas.py +118 -0
  195. agno/os/routers/memory/__init__.py +3 -0
  196. agno/os/routers/memory/memory.py +179 -0
  197. agno/os/routers/memory/schemas.py +58 -0
  198. agno/os/routers/metrics/__init__.py +3 -0
  199. agno/os/routers/metrics/metrics.py +58 -0
  200. agno/os/routers/metrics/schemas.py +47 -0
  201. agno/os/routers/session/__init__.py +3 -0
  202. agno/os/routers/session/session.py +163 -0
  203. agno/os/schema.py +892 -0
  204. agno/{app/playground → os}/settings.py +8 -15
  205. agno/os/utils.py +270 -0
  206. agno/reasoning/azure_ai_foundry.py +4 -4
  207. agno/reasoning/deepseek.py +4 -4
  208. agno/reasoning/default.py +6 -11
  209. agno/reasoning/groq.py +4 -4
  210. agno/reasoning/helpers.py +4 -6
  211. agno/reasoning/ollama.py +4 -4
  212. agno/reasoning/openai.py +4 -4
  213. agno/run/{response.py → agent.py} +144 -72
  214. agno/run/base.py +44 -58
  215. agno/run/cancel.py +83 -0
  216. agno/run/team.py +133 -77
  217. agno/run/workflow.py +537 -12
  218. agno/session/__init__.py +10 -0
  219. agno/session/agent.py +244 -0
  220. agno/session/summary.py +225 -0
  221. agno/session/team.py +262 -0
  222. agno/{storage/session/v2 → session}/workflow.py +47 -24
  223. agno/team/__init__.py +15 -16
  224. agno/team/team.py +2967 -4243
  225. agno/tools/agentql.py +14 -5
  226. agno/tools/airflow.py +9 -4
  227. agno/tools/api.py +7 -3
  228. agno/tools/apify.py +2 -46
  229. agno/tools/arxiv.py +8 -3
  230. agno/tools/aws_lambda.py +7 -5
  231. agno/tools/aws_ses.py +7 -1
  232. agno/tools/baidusearch.py +4 -1
  233. agno/tools/bitbucket.py +4 -4
  234. agno/tools/brandfetch.py +14 -11
  235. agno/tools/bravesearch.py +4 -1
  236. agno/tools/brightdata.py +42 -22
  237. agno/tools/browserbase.py +13 -4
  238. agno/tools/calcom.py +12 -10
  239. agno/tools/calculator.py +10 -27
  240. agno/tools/cartesia.py +18 -13
  241. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  242. agno/tools/confluence.py +71 -18
  243. agno/tools/crawl4ai.py +7 -1
  244. agno/tools/csv_toolkit.py +9 -8
  245. agno/tools/dalle.py +18 -11
  246. agno/tools/daytona.py +13 -16
  247. agno/tools/decorator.py +6 -3
  248. agno/tools/desi_vocal.py +16 -7
  249. agno/tools/discord.py +11 -8
  250. agno/tools/docker.py +30 -42
  251. agno/tools/duckdb.py +34 -53
  252. agno/tools/duckduckgo.py +8 -7
  253. agno/tools/e2b.py +62 -62
  254. agno/tools/eleven_labs.py +35 -28
  255. agno/tools/email.py +4 -1
  256. agno/tools/evm.py +7 -1
  257. agno/tools/exa.py +19 -14
  258. agno/tools/fal.py +29 -29
  259. agno/tools/file.py +9 -8
  260. agno/tools/financial_datasets.py +25 -44
  261. agno/tools/firecrawl.py +22 -22
  262. agno/tools/function.py +68 -17
  263. agno/tools/giphy.py +22 -10
  264. agno/tools/github.py +48 -126
  265. agno/tools/gmail.py +46 -62
  266. agno/tools/google_bigquery.py +7 -6
  267. agno/tools/google_maps.py +11 -26
  268. agno/tools/googlesearch.py +7 -2
  269. agno/tools/googlesheets.py +21 -17
  270. agno/tools/hackernews.py +9 -5
  271. agno/tools/jina.py +5 -4
  272. agno/tools/jira.py +18 -9
  273. agno/tools/knowledge.py +31 -32
  274. agno/tools/linear.py +18 -33
  275. agno/tools/linkup.py +5 -1
  276. agno/tools/local_file_system.py +8 -5
  277. agno/tools/lumalab.py +31 -19
  278. agno/tools/mem0.py +18 -12
  279. agno/tools/memori.py +14 -10
  280. agno/tools/mlx_transcribe.py +3 -2
  281. agno/tools/models/azure_openai.py +32 -14
  282. agno/tools/models/gemini.py +58 -31
  283. agno/tools/models/groq.py +29 -20
  284. agno/tools/models/nebius.py +27 -11
  285. agno/tools/models_labs.py +39 -15
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +134 -0
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +57 -26
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +62 -46
  293. agno/tools/openweather.py +14 -12
  294. agno/tools/pandas.py +11 -3
  295. agno/tools/postgres.py +4 -12
  296. agno/tools/pubmed.py +4 -1
  297. agno/tools/python.py +9 -22
  298. agno/tools/reasoning.py +35 -27
  299. agno/tools/reddit.py +11 -26
  300. agno/tools/replicate.py +54 -41
  301. agno/tools/resend.py +4 -1
  302. agno/tools/scrapegraph.py +15 -14
  303. agno/tools/searxng.py +10 -23
  304. agno/tools/serpapi.py +6 -3
  305. agno/tools/serper.py +13 -4
  306. agno/tools/shell.py +9 -2
  307. agno/tools/slack.py +12 -11
  308. agno/tools/sleep.py +3 -2
  309. agno/tools/spider.py +24 -4
  310. agno/tools/sql.py +7 -6
  311. agno/tools/tavily.py +6 -4
  312. agno/tools/telegram.py +12 -4
  313. agno/tools/todoist.py +11 -31
  314. agno/tools/toolkit.py +1 -1
  315. agno/tools/trafilatura.py +22 -6
  316. agno/tools/trello.py +9 -22
  317. agno/tools/twilio.py +10 -3
  318. agno/tools/user_control_flow.py +6 -1
  319. agno/tools/valyu.py +34 -5
  320. agno/tools/visualization.py +19 -28
  321. agno/tools/webbrowser.py +4 -3
  322. agno/tools/webex.py +11 -7
  323. agno/tools/website.py +15 -46
  324. agno/tools/webtools.py +12 -4
  325. agno/tools/whatsapp.py +5 -9
  326. agno/tools/wikipedia.py +20 -13
  327. agno/tools/x.py +14 -13
  328. agno/tools/yfinance.py +13 -40
  329. agno/tools/youtube.py +26 -20
  330. agno/tools/zendesk.py +7 -2
  331. agno/tools/zep.py +10 -7
  332. agno/tools/zoom.py +10 -9
  333. agno/utils/common.py +1 -19
  334. agno/utils/events.py +95 -118
  335. agno/utils/knowledge.py +29 -0
  336. agno/utils/location.py +2 -2
  337. agno/utils/log.py +2 -2
  338. agno/utils/mcp.py +11 -5
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/claude.py +6 -4
  342. agno/utils/models/mistral.py +8 -7
  343. agno/utils/models/schema_utils.py +3 -3
  344. agno/utils/pprint.py +33 -32
  345. agno/utils/print_response/agent.py +779 -0
  346. agno/utils/print_response/team.py +1565 -0
  347. agno/utils/print_response/workflow.py +1451 -0
  348. agno/utils/prompts.py +14 -14
  349. agno/utils/reasoning.py +87 -0
  350. agno/utils/response.py +42 -42
  351. agno/utils/string.py +8 -22
  352. agno/utils/team.py +50 -0
  353. agno/utils/timer.py +2 -2
  354. agno/vectordb/base.py +33 -21
  355. agno/vectordb/cassandra/cassandra.py +287 -23
  356. agno/vectordb/chroma/chromadb.py +482 -59
  357. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  358. agno/vectordb/couchbase/couchbase.py +309 -29
  359. agno/vectordb/lancedb/lance_db.py +360 -21
  360. agno/vectordb/langchaindb/__init__.py +5 -0
  361. agno/vectordb/langchaindb/langchaindb.py +145 -0
  362. agno/vectordb/lightrag/__init__.py +5 -0
  363. agno/vectordb/lightrag/lightrag.py +374 -0
  364. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  365. agno/vectordb/milvus/milvus.py +242 -32
  366. agno/vectordb/mongodb/mongodb.py +200 -24
  367. agno/vectordb/pgvector/pgvector.py +319 -37
  368. agno/vectordb/pineconedb/pineconedb.py +221 -27
  369. agno/vectordb/qdrant/qdrant.py +356 -14
  370. agno/vectordb/singlestore/singlestore.py +286 -29
  371. agno/vectordb/surrealdb/surrealdb.py +187 -7
  372. agno/vectordb/upstashdb/upstashdb.py +342 -26
  373. agno/vectordb/weaviate/weaviate.py +227 -165
  374. agno/workflow/__init__.py +17 -13
  375. agno/workflow/{v2/condition.py → condition.py} +135 -32
  376. agno/workflow/{v2/loop.py → loop.py} +115 -28
  377. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  378. agno/workflow/{v2/router.py → router.py} +133 -32
  379. agno/workflow/{v2/step.py → step.py} +200 -42
  380. agno/workflow/{v2/steps.py → steps.py} +147 -66
  381. agno/workflow/types.py +482 -0
  382. agno/workflow/workflow.py +2394 -696
  383. agno-2.0.0a1.dist-info/METADATA +355 -0
  384. agno-2.0.0a1.dist-info/RECORD +514 -0
  385. agno/agent/metrics.py +0 -107
  386. agno/api/app.py +0 -35
  387. agno/api/playground.py +0 -92
  388. agno/api/schemas/app.py +0 -12
  389. agno/api/schemas/playground.py +0 -22
  390. agno/api/schemas/user.py +0 -35
  391. agno/api/schemas/workspace.py +0 -46
  392. agno/api/user.py +0 -160
  393. agno/api/workflows.py +0 -33
  394. agno/api/workspace.py +0 -175
  395. agno/app/agui/__init__.py +0 -3
  396. agno/app/agui/app.py +0 -17
  397. agno/app/agui/sync_router.py +0 -120
  398. agno/app/base.py +0 -186
  399. agno/app/discord/__init__.py +0 -3
  400. agno/app/fastapi/__init__.py +0 -3
  401. agno/app/fastapi/app.py +0 -107
  402. agno/app/fastapi/async_router.py +0 -457
  403. agno/app/fastapi/sync_router.py +0 -448
  404. agno/app/playground/app.py +0 -228
  405. agno/app/playground/async_router.py +0 -1050
  406. agno/app/playground/deploy.py +0 -249
  407. agno/app/playground/operator.py +0 -183
  408. agno/app/playground/schemas.py +0 -220
  409. agno/app/playground/serve.py +0 -55
  410. agno/app/playground/sync_router.py +0 -1042
  411. agno/app/playground/utils.py +0 -46
  412. agno/app/settings.py +0 -15
  413. agno/app/slack/__init__.py +0 -3
  414. agno/app/slack/app.py +0 -19
  415. agno/app/slack/sync_router.py +0 -92
  416. agno/app/utils.py +0 -54
  417. agno/app/whatsapp/__init__.py +0 -3
  418. agno/app/whatsapp/app.py +0 -15
  419. agno/app/whatsapp/sync_router.py +0 -197
  420. agno/cli/auth_server.py +0 -249
  421. agno/cli/config.py +0 -274
  422. agno/cli/console.py +0 -88
  423. agno/cli/credentials.py +0 -23
  424. agno/cli/entrypoint.py +0 -571
  425. agno/cli/operator.py +0 -357
  426. agno/cli/settings.py +0 -96
  427. agno/cli/ws/ws_cli.py +0 -817
  428. agno/constants.py +0 -13
  429. agno/document/__init__.py +0 -5
  430. agno/document/chunking/semantic.py +0 -45
  431. agno/document/chunking/strategy.py +0 -31
  432. agno/document/reader/__init__.py +0 -5
  433. agno/document/reader/base.py +0 -47
  434. agno/document/reader/docx_reader.py +0 -60
  435. agno/document/reader/gcs/pdf_reader.py +0 -44
  436. agno/document/reader/s3/pdf_reader.py +0 -59
  437. agno/document/reader/s3/text_reader.py +0 -63
  438. agno/document/reader/url_reader.py +0 -59
  439. agno/document/reader/youtube_reader.py +0 -58
  440. agno/embedder/__init__.py +0 -5
  441. agno/embedder/langdb.py +0 -80
  442. agno/embedder/mistral.py +0 -82
  443. agno/embedder/openai.py +0 -78
  444. agno/file/__init__.py +0 -5
  445. agno/file/file.py +0 -16
  446. agno/file/local/csv.py +0 -32
  447. agno/file/local/txt.py +0 -19
  448. agno/infra/app.py +0 -240
  449. agno/infra/base.py +0 -144
  450. agno/infra/context.py +0 -20
  451. agno/infra/db_app.py +0 -52
  452. agno/infra/resource.py +0 -205
  453. agno/infra/resources.py +0 -55
  454. agno/knowledge/agent.py +0 -698
  455. agno/knowledge/arxiv.py +0 -33
  456. agno/knowledge/combined.py +0 -36
  457. agno/knowledge/csv.py +0 -144
  458. agno/knowledge/csv_url.py +0 -124
  459. agno/knowledge/document.py +0 -223
  460. agno/knowledge/docx.py +0 -137
  461. agno/knowledge/firecrawl.py +0 -34
  462. agno/knowledge/gcs/__init__.py +0 -0
  463. agno/knowledge/gcs/base.py +0 -39
  464. agno/knowledge/gcs/pdf.py +0 -125
  465. agno/knowledge/json.py +0 -137
  466. agno/knowledge/langchain.py +0 -71
  467. agno/knowledge/light_rag.py +0 -273
  468. agno/knowledge/llamaindex.py +0 -66
  469. agno/knowledge/markdown.py +0 -154
  470. agno/knowledge/pdf.py +0 -164
  471. agno/knowledge/pdf_bytes.py +0 -42
  472. agno/knowledge/pdf_url.py +0 -148
  473. agno/knowledge/s3/__init__.py +0 -0
  474. agno/knowledge/s3/base.py +0 -64
  475. agno/knowledge/s3/pdf.py +0 -33
  476. agno/knowledge/s3/text.py +0 -34
  477. agno/knowledge/text.py +0 -141
  478. agno/knowledge/url.py +0 -46
  479. agno/knowledge/website.py +0 -179
  480. agno/knowledge/wikipedia.py +0 -32
  481. agno/knowledge/youtube.py +0 -35
  482. agno/memory/agent.py +0 -423
  483. agno/memory/classifier.py +0 -104
  484. agno/memory/db/__init__.py +0 -5
  485. agno/memory/db/base.py +0 -42
  486. agno/memory/db/mongodb.py +0 -189
  487. agno/memory/db/postgres.py +0 -203
  488. agno/memory/db/sqlite.py +0 -193
  489. agno/memory/memory.py +0 -22
  490. agno/memory/row.py +0 -36
  491. agno/memory/summarizer.py +0 -201
  492. agno/memory/summary.py +0 -19
  493. agno/memory/team.py +0 -415
  494. agno/memory/v2/__init__.py +0 -2
  495. agno/memory/v2/db/__init__.py +0 -1
  496. agno/memory/v2/db/base.py +0 -42
  497. agno/memory/v2/db/firestore.py +0 -339
  498. agno/memory/v2/db/mongodb.py +0 -196
  499. agno/memory/v2/db/postgres.py +0 -214
  500. agno/memory/v2/db/redis.py +0 -187
  501. agno/memory/v2/db/schema.py +0 -54
  502. agno/memory/v2/db/sqlite.py +0 -209
  503. agno/memory/v2/manager.py +0 -437
  504. agno/memory/v2/memory.py +0 -1097
  505. agno/memory/v2/schema.py +0 -55
  506. agno/memory/v2/summarizer.py +0 -215
  507. agno/memory/workflow.py +0 -38
  508. agno/models/ollama/tools.py +0 -430
  509. agno/models/qwen/__init__.py +0 -5
  510. agno/playground/__init__.py +0 -10
  511. agno/playground/deploy.py +0 -3
  512. agno/playground/playground.py +0 -3
  513. agno/playground/serve.py +0 -3
  514. agno/playground/settings.py +0 -3
  515. agno/reranker/__init__.py +0 -0
  516. agno/run/v2/__init__.py +0 -0
  517. agno/run/v2/workflow.py +0 -567
  518. agno/storage/__init__.py +0 -0
  519. agno/storage/agent/__init__.py +0 -0
  520. agno/storage/agent/dynamodb.py +0 -1
  521. agno/storage/agent/json.py +0 -1
  522. agno/storage/agent/mongodb.py +0 -1
  523. agno/storage/agent/postgres.py +0 -1
  524. agno/storage/agent/singlestore.py +0 -1
  525. agno/storage/agent/sqlite.py +0 -1
  526. agno/storage/agent/yaml.py +0 -1
  527. agno/storage/base.py +0 -60
  528. agno/storage/dynamodb.py +0 -673
  529. agno/storage/firestore.py +0 -297
  530. agno/storage/gcs_json.py +0 -261
  531. agno/storage/in_memory.py +0 -234
  532. agno/storage/json.py +0 -237
  533. agno/storage/mongodb.py +0 -328
  534. agno/storage/mysql.py +0 -685
  535. agno/storage/postgres.py +0 -682
  536. agno/storage/redis.py +0 -336
  537. agno/storage/session/__init__.py +0 -16
  538. agno/storage/session/agent.py +0 -64
  539. agno/storage/session/team.py +0 -63
  540. agno/storage/session/v2/__init__.py +0 -5
  541. agno/storage/session/workflow.py +0 -61
  542. agno/storage/singlestore.py +0 -606
  543. agno/storage/sqlite.py +0 -646
  544. agno/storage/workflow/__init__.py +0 -0
  545. agno/storage/workflow/mongodb.py +0 -1
  546. agno/storage/workflow/postgres.py +0 -1
  547. agno/storage/workflow/sqlite.py +0 -1
  548. agno/storage/yaml.py +0 -241
  549. agno/tools/thinking.py +0 -73
  550. agno/utils/defaults.py +0 -57
  551. agno/utils/filesystem.py +0 -39
  552. agno/utils/git.py +0 -52
  553. agno/utils/json_io.py +0 -30
  554. agno/utils/load_env.py +0 -19
  555. agno/utils/py_io.py +0 -19
  556. agno/utils/pyproject.py +0 -18
  557. agno/utils/resource_filter.py +0 -31
  558. agno/workflow/v2/__init__.py +0 -21
  559. agno/workflow/v2/types.py +0 -357
  560. agno/workflow/v2/workflow.py +0 -3312
  561. agno/workspace/__init__.py +0 -0
  562. agno/workspace/config.py +0 -325
  563. agno/workspace/enums.py +0 -6
  564. agno/workspace/helpers.py +0 -52
  565. agno/workspace/operator.py +0 -757
  566. agno/workspace/settings.py +0 -158
  567. agno-1.8.0.dist-info/METADATA +0 -979
  568. agno-1.8.0.dist-info/RECORD +0 -565
  569. agno-1.8.0.dist-info/entry_points.txt +0 -3
  570. /agno/{app → db/migrations}/__init__.py +0 -0
  571. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  572. /agno/{cli → integrations}/__init__.py +0 -0
  573. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  574. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  575. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  576. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  577. /agno/{app → os/interfaces}/slack/security.py +0 -0
  578. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  579. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  580. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  581. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
  582. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
  583. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1451 @@
1
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
2
+
3
+ from pydantic import BaseModel
4
+ from rich.console import Group
5
+ from rich.live import Live
6
+ from rich.markdown import Markdown
7
+ from rich.status import Status
8
+ from rich.text import Text
9
+
10
+ from agno.media import Audio, Image, Video
11
+ from agno.models.message import Message
12
+ from agno.run.workflow import (
13
+ ConditionExecutionCompletedEvent,
14
+ ConditionExecutionStartedEvent,
15
+ LoopExecutionCompletedEvent,
16
+ LoopExecutionStartedEvent,
17
+ LoopIterationCompletedEvent,
18
+ LoopIterationStartedEvent,
19
+ ParallelExecutionCompletedEvent,
20
+ ParallelExecutionStartedEvent,
21
+ RouterExecutionCompletedEvent,
22
+ RouterExecutionStartedEvent,
23
+ StepCompletedEvent,
24
+ StepOutputEvent,
25
+ StepsExecutionCompletedEvent,
26
+ StepsExecutionStartedEvent,
27
+ StepStartedEvent,
28
+ WorkflowCompletedEvent,
29
+ WorkflowErrorEvent,
30
+ WorkflowRunOutput,
31
+ WorkflowRunOutputEvent,
32
+ WorkflowStartedEvent,
33
+ )
34
+ from agno.utils.response import create_panel
35
+ from agno.utils.timer import Timer
36
+ from agno.workflow.types import StepOutput
37
+
38
+ if TYPE_CHECKING:
39
+ from agno.workflow.workflow import Workflow
40
+
41
+
42
+ def print_response(
43
+ workflow: "Workflow",
44
+ input: Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]],
45
+ user_id: Optional[str] = None,
46
+ session_id: Optional[str] = None,
47
+ additional_data: Optional[Dict[str, Any]] = None,
48
+ audio: Optional[List[Audio]] = None,
49
+ images: Optional[List[Image]] = None,
50
+ videos: Optional[List[Video]] = None,
51
+ markdown: bool = True,
52
+ show_time: bool = True,
53
+ show_step_details: bool = True,
54
+ console: Optional[Any] = None,
55
+ **kwargs: Any,
56
+ ) -> None:
57
+ """Print workflow execution with rich formatting (non-streaming)"""
58
+ from rich.live import Live
59
+ from rich.markdown import Markdown
60
+ from rich.status import Status
61
+ from rich.text import Text
62
+
63
+ from agno.utils.response import create_panel
64
+ from agno.utils.timer import Timer
65
+
66
+ if console is None:
67
+ from rich.console import Console
68
+
69
+ console = Console()
70
+
71
+ # Show workflow info
72
+ media_info = []
73
+ if audio:
74
+ media_info.append(f"Audio files: {len(audio)}")
75
+ if images:
76
+ media_info.append(f"Images: {len(images)}")
77
+ if videos:
78
+ media_info.append(f"Videos: {len(videos)}")
79
+
80
+ workflow_info = f"""**Workflow:** {workflow.name}"""
81
+ if workflow.description:
82
+ workflow_info += f"""\n\n**Description:** {workflow.description}"""
83
+ workflow_info += f"""\n\n**Steps:** {workflow._get_step_count()} steps"""
84
+ if input:
85
+ if isinstance(input, str):
86
+ workflow_info += f"""\n\n**Message:** {input}"""
87
+ else:
88
+ # Handle structured input message
89
+ if isinstance(input, BaseModel):
90
+ data_display = input.model_dump_json(indent=2, exclude_none=True)
91
+ elif isinstance(input, (dict, list)):
92
+ import json
93
+
94
+ data_display = json.dumps(input, indent=2, default=str)
95
+ else:
96
+ data_display = str(input)
97
+ workflow_info += f"""\n\n**Structured Input:**\n```json\n{data_display}\n```"""
98
+ if user_id:
99
+ workflow_info += f"""\n\n**User ID:** {user_id}"""
100
+ if session_id:
101
+ workflow_info += f"""\n\n**Session ID:** {session_id}"""
102
+ workflow_info = workflow_info.strip()
103
+
104
+ workflow_panel = create_panel(
105
+ content=Markdown(workflow_info) if markdown else workflow_info,
106
+ title="Workflow Information",
107
+ border_style="cyan",
108
+ )
109
+ console.print(workflow_panel) # type: ignore
110
+
111
+ # Start timer
112
+ response_timer = Timer()
113
+ response_timer.start()
114
+
115
+ with Live(console=console) as live_log:
116
+ status = Status("Starting workflow...", spinner="dots")
117
+ live_log.update(status)
118
+
119
+ try:
120
+ # Execute workflow and get the response directly
121
+ workflow_response: WorkflowRunOutput = workflow.run(
122
+ input=input,
123
+ user_id=user_id,
124
+ session_id=session_id,
125
+ additional_data=additional_data,
126
+ audio=audio,
127
+ images=images,
128
+ videos=videos,
129
+ **kwargs,
130
+ ) # type: ignore
131
+
132
+ response_timer.stop()
133
+
134
+ if show_step_details and workflow_response.step_results:
135
+ for i, step_output in enumerate(workflow_response.step_results):
136
+ print_step_output_recursive(step_output, i + 1, markdown, console) # type: ignore
137
+
138
+ # For callable functions, show the content directly since there are no step_results
139
+ elif show_step_details and callable(workflow.steps) and workflow_response.content:
140
+ step_panel = create_panel(
141
+ content=Markdown(workflow_response.content) if markdown else workflow_response.content, # type: ignore
142
+ title="Custom Function (Completed)",
143
+ border_style="orange3",
144
+ )
145
+ console.print(step_panel) # type: ignore
146
+
147
+ # Show final summary
148
+ if workflow_response.metadata:
149
+ status = workflow_response.status.value # type: ignore
150
+ summary_content = ""
151
+ summary_content += f"""\n\n**Status:** {status}"""
152
+ summary_content += f"""\n\n**Steps Completed:** {len(workflow_response.step_results) if workflow_response.step_results else 0}"""
153
+ summary_content = summary_content.strip()
154
+
155
+ summary_panel = create_panel(
156
+ content=Markdown(summary_content) if markdown else summary_content,
157
+ title="Execution Summary",
158
+ border_style="blue",
159
+ )
160
+ console.print(summary_panel) # type: ignore
161
+
162
+ live_log.update("")
163
+
164
+ # Final completion message
165
+ if show_time:
166
+ completion_text = Text(f"Completed in {response_timer.elapsed:.1f}s", style="bold green")
167
+ console.print(completion_text) # type: ignore
168
+
169
+ except Exception as e:
170
+ import traceback
171
+
172
+ traceback.print_exc()
173
+ response_timer.stop()
174
+ error_panel = create_panel(
175
+ content=f"Workflow execution failed: {str(e)}", title="Execution Error", border_style="red"
176
+ )
177
+ console.print(error_panel) # type: ignore
178
+
179
+
180
+ def print_response_stream(
181
+ workflow: "Workflow",
182
+ input: Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]],
183
+ user_id: Optional[str] = None,
184
+ session_id: Optional[str] = None,
185
+ additional_data: Optional[Dict[str, Any]] = None,
186
+ audio: Optional[List[Audio]] = None,
187
+ images: Optional[List[Image]] = None,
188
+ videos: Optional[List[Video]] = None,
189
+ stream_intermediate_steps: bool = False,
190
+ markdown: bool = True,
191
+ show_time: bool = True,
192
+ show_step_details: bool = True,
193
+ console: Optional[Any] = None,
194
+ **kwargs: Any,
195
+ ) -> None:
196
+ """Print workflow execution with clean streaming"""
197
+ if console is None:
198
+ from rich.console import Console
199
+
200
+ console = Console()
201
+
202
+ stream_intermediate_steps = True # With streaming print response, we need to stream intermediate steps
203
+
204
+ # Show workflow info (same as before)
205
+ media_info = []
206
+ if audio:
207
+ media_info.append(f"Audio files: {len(audio)}")
208
+ if images:
209
+ media_info.append(f"Images: {len(images)}")
210
+ if videos:
211
+ media_info.append(f"Videos: {len(videos)}")
212
+
213
+ workflow_info = f"""**Workflow:** {workflow.name}"""
214
+ if workflow.description:
215
+ workflow_info += f"""\n\n**Description:** {workflow.description}"""
216
+ workflow_info += f"""\n\n**Steps:** {workflow._get_step_count()} steps"""
217
+ if input:
218
+ if isinstance(input, str):
219
+ workflow_info += f"""\n\n**Message:** {input}"""
220
+ else:
221
+ # Handle structured input message
222
+ if isinstance(input, BaseModel):
223
+ data_display = input.model_dump_json(indent=2, exclude_none=True)
224
+ elif isinstance(input, (dict, list)):
225
+ import json
226
+
227
+ data_display = json.dumps(input, indent=2, default=str)
228
+ else:
229
+ data_display = str(input)
230
+ workflow_info += f"""\n\n**Structured Input:**\n```json\n{data_display}\n```"""
231
+ if user_id:
232
+ workflow_info += f"""\n\n**User ID:** {user_id}"""
233
+ if session_id:
234
+ workflow_info += f"""\n\n**Session ID:** {session_id}"""
235
+ workflow_info = workflow_info.strip()
236
+
237
+ workflow_panel = create_panel(
238
+ content=Markdown(workflow_info) if markdown else workflow_info,
239
+ title="Workflow Information",
240
+ border_style="cyan",
241
+ )
242
+ console.print(workflow_panel) # type: ignore
243
+
244
+ # Start timer
245
+ response_timer = Timer()
246
+ response_timer.start()
247
+
248
+ # Streaming execution variables with smart step tracking
249
+ current_step_content = ""
250
+ current_step_name = ""
251
+ current_step_index = 0
252
+ step_results = []
253
+ step_started_printed = False
254
+ is_callable_function = callable(workflow.steps)
255
+
256
+ # Smart step hierarchy tracking
257
+ current_primitive_context = None # Current primitive being executed (parallel, loop, etc.)
258
+ step_display_cache = {} # type: ignore
259
+
260
+ def get_step_display_number(step_index: Union[int, tuple], step_name: str = "") -> str:
261
+ """Generate clean two-level step numbering: x.y format only"""
262
+
263
+ # Handle tuple format for child steps
264
+ if isinstance(step_index, tuple):
265
+ if len(step_index) >= 2:
266
+ parent_idx, sub_idx = step_index[0], step_index[1]
267
+
268
+ # Extract base parent index if it's nested
269
+ if isinstance(parent_idx, tuple):
270
+ base_idx = parent_idx[0] if len(parent_idx) > 0 else 0
271
+ while isinstance(base_idx, tuple) and len(base_idx) > 0:
272
+ base_idx = base_idx[0]
273
+ else:
274
+ base_idx = parent_idx
275
+
276
+ # Check context for parallel special case
277
+ if current_primitive_context and current_primitive_context["type"] == "parallel":
278
+ # For parallel child steps, all get the same number based on their actual step_index
279
+ return f"Step {base_idx + 1}.{sub_idx + 1}"
280
+ elif current_primitive_context and current_primitive_context["type"] == "loop":
281
+ iteration = current_primitive_context.get("current_iteration", 1)
282
+ return f"Step {base_idx + 1}.{sub_idx + 1} (Iteration {iteration})"
283
+ else:
284
+ # Regular child step numbering
285
+ return f"Step {base_idx + 1}.{sub_idx + 1}" # type: ignore
286
+ else:
287
+ # Single element tuple - treat as main step
288
+ return f"Step {step_index[0] + 1}"
289
+
290
+ # Handle integer step_index - main step
291
+ if not current_primitive_context:
292
+ # Regular main step
293
+ return f"Step {step_index + 1}"
294
+ else:
295
+ # This shouldn't happen with the new logic, but fallback
296
+ return f"Step {step_index + 1}"
297
+
298
+ with Live(console=console, refresh_per_second=10) as live_log:
299
+ status = Status("Starting workflow...", spinner="dots")
300
+ live_log.update(status)
301
+
302
+ try:
303
+ for response in workflow.run(
304
+ input=input,
305
+ user_id=user_id,
306
+ session_id=session_id,
307
+ additional_data=additional_data,
308
+ audio=audio,
309
+ images=images,
310
+ videos=videos,
311
+ stream=True,
312
+ stream_intermediate_steps=stream_intermediate_steps,
313
+ **kwargs,
314
+ ): # type: ignore
315
+ # Handle the new event types
316
+ if isinstance(response, WorkflowStartedEvent):
317
+ status.update("Workflow started...")
318
+ if is_callable_function:
319
+ current_step_name = "Custom Function"
320
+ current_step_index = 0
321
+ live_log.update(status)
322
+
323
+ elif isinstance(response, StepStartedEvent):
324
+ current_step_name = response.step_name or "Unknown"
325
+ current_step_index = response.step_index or 0 # type: ignore
326
+ current_step_content = ""
327
+ step_started_printed = False
328
+
329
+ # Generate smart step number
330
+ step_display = get_step_display_number(current_step_index, current_step_name)
331
+ status.update(f"Starting {step_display}: {current_step_name}...")
332
+ live_log.update(status)
333
+
334
+ elif isinstance(response, StepCompletedEvent):
335
+ step_name = response.step_name or "Unknown"
336
+ step_index = response.step_index or 0
337
+
338
+ # Generate smart step number for completion (will use cached value)
339
+ step_display = get_step_display_number(step_index, step_name)
340
+ status.update(f"Completed {step_display}: {step_name}")
341
+
342
+ if response.content:
343
+ step_results.append(
344
+ {
345
+ "step_name": step_name,
346
+ "step_index": step_index,
347
+ "content": response.content,
348
+ "event": response.event,
349
+ }
350
+ )
351
+
352
+ # Print the final step result in orange (only once)
353
+ if show_step_details and current_step_content and not step_started_printed:
354
+ live_log.update(status, refresh=True)
355
+
356
+ final_step_panel = create_panel(
357
+ content=Markdown(current_step_content) if markdown else current_step_content,
358
+ title=f"{step_display}: {step_name} (Completed)",
359
+ border_style="orange3",
360
+ )
361
+ console.print(final_step_panel) # type: ignore
362
+ step_started_printed = True
363
+
364
+ elif isinstance(response, LoopExecutionStartedEvent):
365
+ current_step_name = response.step_name or "Loop"
366
+ current_step_index = response.step_index or 0 # type: ignore
367
+ current_step_content = ""
368
+ step_started_printed = False
369
+
370
+ # Set up loop context
371
+ current_primitive_context = {
372
+ "type": "loop",
373
+ "step_index": current_step_index,
374
+ "sub_step_counter": 0,
375
+ "current_iteration": 1,
376
+ "max_iterations": response.max_iterations,
377
+ }
378
+
379
+ # Clear cache for this primitive's sub-steps
380
+ step_display_cache.clear()
381
+
382
+ status.update(f"Starting loop: {current_step_name} (max {response.max_iterations} iterations)...")
383
+ live_log.update(status)
384
+
385
+ elif isinstance(response, LoopIterationStartedEvent):
386
+ if current_primitive_context and current_primitive_context["type"] == "loop":
387
+ current_primitive_context["current_iteration"] = response.iteration
388
+ current_primitive_context["sub_step_counter"] = 0 # Reset for new iteration
389
+ # Clear cache for new iteration
390
+ step_display_cache.clear()
391
+
392
+ status.update(
393
+ f"Loop iteration {response.iteration}/{response.max_iterations}: {response.step_name}..."
394
+ )
395
+ live_log.update(status)
396
+
397
+ elif isinstance(response, LoopIterationCompletedEvent):
398
+ status.update(
399
+ f"Completed iteration {response.iteration}/{response.max_iterations}: {response.step_name}"
400
+ )
401
+
402
+ elif isinstance(response, LoopExecutionCompletedEvent):
403
+ step_name = response.step_name or "Loop"
404
+ step_index = response.step_index or 0
405
+
406
+ status.update(f"Completed loop: {step_name} ({response.total_iterations} iterations)")
407
+ live_log.update(status, refresh=True)
408
+
409
+ # Print loop summary
410
+ if show_step_details:
411
+ summary_content = "**Loop Summary:**\n\n"
412
+ summary_content += (
413
+ f"- Total iterations: {response.total_iterations}/{response.max_iterations}\n"
414
+ )
415
+ summary_content += (
416
+ f"- Total steps executed: {sum(len(iteration) for iteration in response.all_results)}\n"
417
+ )
418
+
419
+ loop_summary_panel = create_panel(
420
+ content=Markdown(summary_content) if markdown else summary_content,
421
+ title=f"Loop {step_name} (Completed)",
422
+ border_style="yellow",
423
+ )
424
+ console.print(loop_summary_panel) # type: ignore
425
+
426
+ # Reset context
427
+ current_primitive_context = None
428
+ step_display_cache.clear()
429
+ step_started_printed = True
430
+
431
+ elif isinstance(response, ParallelExecutionStartedEvent):
432
+ current_step_name = response.step_name or "Parallel Steps"
433
+ current_step_index = response.step_index or 0 # type: ignore
434
+ current_step_content = ""
435
+ step_started_printed = False
436
+
437
+ # Set up parallel context
438
+ current_primitive_context = {
439
+ "type": "parallel",
440
+ "step_index": current_step_index,
441
+ "sub_step_counter": 0,
442
+ "total_steps": response.parallel_step_count,
443
+ }
444
+
445
+ # Clear cache for this primitive's sub-steps
446
+ step_display_cache.clear()
447
+
448
+ # Print parallel execution summary panel
449
+ live_log.update(status, refresh=True)
450
+ parallel_summary = f"**Parallel Steps:** {response.parallel_step_count}"
451
+ # Use get_step_display_number for consistent numbering
452
+ step_display = get_step_display_number(current_step_index, current_step_name)
453
+ parallel_panel = create_panel(
454
+ content=Markdown(parallel_summary) if markdown else parallel_summary,
455
+ title=f"{step_display}: {current_step_name}",
456
+ border_style="cyan",
457
+ )
458
+ console.print(parallel_panel) # type: ignore
459
+
460
+ status.update(
461
+ f"Starting parallel execution: {current_step_name} ({response.parallel_step_count} steps)..."
462
+ )
463
+ live_log.update(status)
464
+
465
+ elif isinstance(response, ParallelExecutionCompletedEvent):
466
+ step_name = response.step_name or "Parallel Steps"
467
+ step_index = response.step_index or 0
468
+
469
+ status.update(f"Completed parallel execution: {step_name}")
470
+
471
+ # Reset context
472
+ current_primitive_context = None
473
+ step_display_cache.clear()
474
+
475
+ elif isinstance(response, ConditionExecutionStartedEvent):
476
+ current_step_name = response.step_name or "Condition"
477
+ current_step_index = response.step_index or 0 # type: ignore
478
+ current_step_content = ""
479
+ step_started_printed = False
480
+
481
+ # Set up condition context
482
+ current_primitive_context = {
483
+ "type": "condition",
484
+ "step_index": current_step_index,
485
+ "sub_step_counter": 0,
486
+ "condition_result": response.condition_result,
487
+ }
488
+
489
+ # Clear cache for this primitive's sub-steps
490
+ step_display_cache.clear()
491
+
492
+ condition_text = "met" if response.condition_result else "not met"
493
+ status.update(f"Starting condition: {current_step_name} (condition {condition_text})...")
494
+ live_log.update(status)
495
+
496
+ elif isinstance(response, ConditionExecutionCompletedEvent):
497
+ step_name = response.step_name or "Condition"
498
+ step_index = response.step_index or 0
499
+
500
+ status.update(f"Completed condition: {step_name}")
501
+
502
+ # Reset context
503
+ current_primitive_context = None
504
+ step_display_cache.clear()
505
+
506
+ elif isinstance(response, RouterExecutionStartedEvent):
507
+ current_step_name = response.step_name or "Router"
508
+ current_step_index = response.step_index or 0 # type: ignore
509
+ current_step_content = ""
510
+ step_started_printed = False
511
+
512
+ # Set up router context
513
+ current_primitive_context = {
514
+ "type": "router",
515
+ "step_index": current_step_index,
516
+ "sub_step_counter": 0,
517
+ "selected_steps": response.selected_steps,
518
+ }
519
+
520
+ # Clear cache for this primitive's sub-steps
521
+ step_display_cache.clear()
522
+
523
+ selected_steps_text = ", ".join(response.selected_steps) if response.selected_steps else "none"
524
+ status.update(f"Starting router: {current_step_name} (selected: {selected_steps_text})...")
525
+ live_log.update(status)
526
+
527
+ elif isinstance(response, RouterExecutionCompletedEvent):
528
+ step_name = response.step_name or "Router"
529
+ step_index = response.step_index or 0
530
+
531
+ status.update(f"Completed router: {step_name}")
532
+
533
+ # Print router summary
534
+ if show_step_details:
535
+ selected_steps_text = ", ".join(response.selected_steps) if response.selected_steps else "none"
536
+ summary_content = "**Router Summary:**\n\n"
537
+ summary_content += f"- Selected steps: {selected_steps_text}\n"
538
+ summary_content += f"- Executed steps: {response.executed_steps or 0}\n"
539
+
540
+ router_summary_panel = create_panel(
541
+ content=Markdown(summary_content) if markdown else summary_content,
542
+ title=f"Router {step_name} (Completed)",
543
+ border_style="purple",
544
+ )
545
+ console.print(router_summary_panel) # type: ignore
546
+
547
+ # Reset context
548
+ current_primitive_context = None
549
+ step_display_cache.clear()
550
+ step_started_printed = True
551
+
552
+ elif isinstance(response, StepsExecutionStartedEvent):
553
+ current_step_name = response.step_name or "Steps"
554
+ current_step_index = response.step_index or 0 # type: ignore
555
+ current_step_content = ""
556
+ step_started_printed = False
557
+ status.update(f"Starting steps: {current_step_name} ({response.steps_count} steps)...")
558
+ live_log.update(status)
559
+
560
+ elif isinstance(response, StepsExecutionCompletedEvent):
561
+ step_name = response.step_name or "Steps"
562
+ step_index = response.step_index or 0
563
+
564
+ status.update(f"Completed steps: {step_name}")
565
+
566
+ # Add results from executed steps to step_results
567
+ if response.step_results:
568
+ for i, step_result in enumerate(response.step_results):
569
+ # Use the same numbering system as other primitives
570
+ step_display_number = get_step_display_number(step_index, step_result.step_name or "")
571
+ step_results.append(
572
+ {
573
+ "step_name": f"{step_display_number}: {step_result.step_name}",
574
+ "step_index": step_index,
575
+ "content": step_result.content,
576
+ "event": "StepsStepResult",
577
+ }
578
+ )
579
+
580
+ # Print steps summary
581
+ if show_step_details:
582
+ summary_content = "**Steps Summary:**\n\n"
583
+ summary_content += f"- Total steps: {response.steps_count or 0}\n"
584
+ summary_content += f"- Executed steps: {response.executed_steps or 0}\n"
585
+
586
+ steps_summary_panel = create_panel(
587
+ content=Markdown(summary_content) if markdown else summary_content,
588
+ title=f"Steps {step_name} (Completed)",
589
+ border_style="yellow",
590
+ )
591
+ console.print(steps_summary_panel) # type: ignore
592
+
593
+ step_started_printed = True
594
+
595
+ elif isinstance(response, WorkflowCompletedEvent):
596
+ status.update("Workflow completed!")
597
+
598
+ # For callable functions, print the final content block here since there are no step events
599
+ if is_callable_function and show_step_details and current_step_content and not step_started_printed:
600
+ final_step_panel = create_panel(
601
+ content=Markdown(current_step_content) if markdown else current_step_content,
602
+ title="Custom Function (Completed)",
603
+ border_style="orange3",
604
+ )
605
+ console.print(final_step_panel) # type: ignore
606
+ step_started_printed = True
607
+
608
+ live_log.update(status, refresh=True)
609
+
610
+ # Show final summary
611
+ if response.metadata:
612
+ status = response.status
613
+ summary_content = ""
614
+ summary_content += f"""\n\n**Status:** {status}"""
615
+ summary_content += (
616
+ f"""\n\n**Steps Completed:** {len(response.step_results) if response.step_results else 0}"""
617
+ )
618
+ summary_content = summary_content.strip()
619
+
620
+ summary_panel = create_panel(
621
+ content=Markdown(summary_content) if markdown else summary_content,
622
+ title="Execution Summary",
623
+ border_style="blue",
624
+ )
625
+ console.print(summary_panel) # type: ignore
626
+
627
+ else:
628
+ # Handle streaming content
629
+ if isinstance(response, str):
630
+ response_str = response
631
+ elif isinstance(response, StepOutputEvent):
632
+ response_str = response.content or "" # type: ignore
633
+ else:
634
+ from agno.run.agent import RunContentEvent
635
+ from agno.run.team import RunContentEvent as TeamRunContentEvent
636
+
637
+ current_step_executor_type = None
638
+ # Handle both integer and tuple step indices for parallel execution
639
+ actual_step_index = current_step_index
640
+ if isinstance(current_step_index, tuple):
641
+ # For tuple indices, use the first element (parent step index)
642
+ actual_step_index = current_step_index[0]
643
+ # If it's nested tuple, keep extracting until we get an integer
644
+ while isinstance(actual_step_index, tuple) and len(actual_step_index) > 0:
645
+ actual_step_index = actual_step_index[0]
646
+
647
+ if not is_callable_function and workflow.steps and actual_step_index < len(workflow.steps): # type: ignore
648
+ step = workflow.steps[actual_step_index] # type: ignore
649
+ if hasattr(step, "executor_type"):
650
+ current_step_executor_type = step.executor_type
651
+
652
+ # Check if this is a streaming content event from agent or team
653
+ if isinstance(response, (TeamRunContentEvent, WorkflowRunOutputEvent)): # type: ignore
654
+ # Check if this is a team's final structured output
655
+ is_structured_output = (
656
+ isinstance(response, TeamRunContentEvent)
657
+ and hasattr(response, "content_type")
658
+ and response.content_type != "str"
659
+ and response.content_type != ""
660
+ )
661
+ response_str = response.content # type: ignore
662
+ elif isinstance(response, RunContentEvent) and current_step_executor_type != "team":
663
+ response_str = response.content # type: ignore
664
+ else:
665
+ continue
666
+
667
+ # Use the unified formatting function for consistency
668
+ response_str = format_step_content_for_display(response_str) # type: ignore
669
+
670
+ # Filter out empty responses and add to current step content
671
+ if response_str and response_str.strip():
672
+ # If it's a structured output from a team, replace the content instead of appending
673
+ if "is_structured_output" in locals() and is_structured_output:
674
+ current_step_content = response_str
675
+ else:
676
+ current_step_content += response_str
677
+
678
+ # Live update the step panel with streaming content
679
+ if show_step_details and not step_started_printed:
680
+ # Generate smart step number for streaming title (will use cached value)
681
+ step_display = get_step_display_number(current_step_index, current_step_name)
682
+ title = f"{step_display}: {current_step_name} (Streaming...)"
683
+ if is_callable_function:
684
+ title = "Custom Function (Streaming...)"
685
+
686
+ # Show the streaming content live in orange panel
687
+ live_step_panel = create_panel(
688
+ content=Markdown(current_step_content) if markdown else current_step_content,
689
+ title=title,
690
+ border_style="orange3",
691
+ )
692
+
693
+ # Create group with status and current step content
694
+ group = Group(status, live_step_panel)
695
+ live_log.update(group)
696
+
697
+ response_timer.stop()
698
+
699
+ live_log.update("")
700
+
701
+ # Final completion message
702
+ if show_time:
703
+ completion_text = Text(f"Completed in {response_timer.elapsed:.1f}s", style="bold green")
704
+ console.print(completion_text) # type: ignore
705
+
706
+ except Exception as e:
707
+ import traceback
708
+
709
+ traceback.print_exc()
710
+ response_timer.stop()
711
+ error_panel = create_panel(
712
+ content=f"Workflow execution failed: {str(e)}", title="Execution Error", border_style="red"
713
+ )
714
+ console.print(error_panel) # type: ignore
715
+
716
+
717
+ def print_step_output_recursive(
718
+ step_output: StepOutput, step_number: int, markdown: bool, console, depth: int = 0
719
+ ) -> None:
720
+ """Recursively print step output and its nested steps"""
721
+ from rich.markdown import Markdown
722
+
723
+ from agno.utils.response import create_panel
724
+
725
+ # Print the current step
726
+ if step_output.content:
727
+ formatted_content = format_step_content_for_display(step_output)
728
+
729
+ # Create title with proper nesting indication
730
+ if depth == 0:
731
+ title = f"Step {step_number}: {step_output.step_name} (Completed)"
732
+ else:
733
+ title = f"{' ' * depth}└─ {step_output.step_name} (Completed)"
734
+
735
+ step_panel = create_panel(
736
+ content=Markdown(formatted_content) if markdown else formatted_content,
737
+ title=title,
738
+ border_style="orange3",
739
+ )
740
+ console.print(step_panel)
741
+
742
+ # Print nested steps if they exist
743
+ if step_output.steps:
744
+ for j, nested_step in enumerate(step_output.steps):
745
+ print_step_output_recursive(nested_step, j + 1, markdown, console, depth + 1)
746
+
747
+
748
+ def format_step_content_for_display(step_output: StepOutput) -> str:
749
+ """Format content for display, handling structured outputs. Works for both raw content and StepOutput objects."""
750
+ # If it's a StepOutput, extract the content
751
+ if hasattr(step_output, "content"):
752
+ actual_content = step_output.content
753
+ else:
754
+ actual_content = step_output
755
+
756
+ if not actual_content:
757
+ return ""
758
+
759
+ # If it's already a string, return as-is
760
+ if isinstance(actual_content, str):
761
+ return actual_content
762
+
763
+ # If it's a structured output (BaseModel or dict), format it nicely
764
+ if isinstance(actual_content, BaseModel):
765
+ return f"**Structured Output:**\n\n```json\n{actual_content.model_dump_json(indent=2, exclude_none=True)}\n```"
766
+ elif isinstance(actual_content, (dict, list)):
767
+ import json
768
+
769
+ return f"**Structured Output:**\n\n```json\n{json.dumps(actual_content, indent=2, default=str)}\n```"
770
+ else:
771
+ # Fallback to string conversion
772
+ return str(actual_content)
773
+
774
+
775
+ async def aprint_response(
776
+ workflow: "Workflow",
777
+ input: Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]],
778
+ additional_data: Optional[Dict[str, Any]] = None,
779
+ user_id: Optional[str] = None,
780
+ session_id: Optional[str] = None,
781
+ audio: Optional[List[Audio]] = None,
782
+ images: Optional[List[Image]] = None,
783
+ videos: Optional[List[Video]] = None,
784
+ markdown: bool = True,
785
+ show_time: bool = True,
786
+ show_step_details: bool = True,
787
+ console: Optional[Any] = None,
788
+ **kwargs: Any,
789
+ ) -> None:
790
+ """Print workflow execution with rich formatting (non-streaming)"""
791
+ from rich.live import Live
792
+ from rich.markdown import Markdown
793
+ from rich.status import Status
794
+ from rich.text import Text
795
+
796
+ from agno.utils.response import create_panel
797
+ from agno.utils.timer import Timer
798
+
799
+ if console is None:
800
+ from rich.console import Console
801
+
802
+ console = Console()
803
+
804
+ # Show workflow info
805
+ media_info = []
806
+ if audio:
807
+ media_info.append(f"Audio files: {len(audio)}")
808
+ if images:
809
+ media_info.append(f"Images: {len(images)}")
810
+ if videos:
811
+ media_info.append(f"Videos: {len(videos)}")
812
+
813
+ workflow_info = f"""**Workflow:** {workflow.name}"""
814
+ if workflow.description:
815
+ workflow_info += f"""\n\n**Description:** {workflow.description}"""
816
+ workflow_info += f"""\n\n**Steps:** {workflow._get_step_count()} steps"""
817
+ if input:
818
+ if isinstance(input, str):
819
+ workflow_info += f"""\n\n**Message:** {input}"""
820
+ else:
821
+ # Handle structured input message
822
+ if isinstance(input, BaseModel):
823
+ data_display = input.model_dump_json(indent=2, exclude_none=True)
824
+ elif isinstance(input, (dict, list)):
825
+ import json
826
+
827
+ data_display = json.dumps(input, indent=2, default=str)
828
+ else:
829
+ data_display = str(input)
830
+ workflow_info += f"""\n\n**Structured Input:**\n```json\n{data_display}\n```"""
831
+ if user_id:
832
+ workflow_info += f"""\n\n**User ID:** {user_id}"""
833
+ if session_id:
834
+ workflow_info += f"""\n\n**Session ID:** {session_id}"""
835
+ workflow_info = workflow_info.strip()
836
+
837
+ workflow_panel = create_panel(
838
+ content=Markdown(workflow_info) if markdown else workflow_info,
839
+ title="Workflow Information",
840
+ border_style="cyan",
841
+ )
842
+ console.print(workflow_panel) # type: ignore
843
+
844
+ # Start timer
845
+ response_timer = Timer()
846
+ response_timer.start()
847
+
848
+ with Live(console=console) as live_log:
849
+ status = Status("Starting async workflow...\n", spinner="dots")
850
+ live_log.update(status)
851
+
852
+ try:
853
+ # Execute workflow and get the response directly
854
+ workflow_response: WorkflowRunOutput = await workflow.arun(
855
+ input=input,
856
+ user_id=user_id,
857
+ session_id=session_id,
858
+ additional_data=additional_data,
859
+ audio=audio,
860
+ images=images,
861
+ videos=videos,
862
+ **kwargs,
863
+ ) # type: ignore
864
+
865
+ response_timer.stop()
866
+
867
+ if show_step_details and workflow_response.step_results:
868
+ for i, step_output in enumerate(workflow_response.step_results):
869
+ print_step_output_recursive(step_output, i + 1, markdown, console) # type: ignore
870
+
871
+ # For callable functions, show the content directly since there are no step_results
872
+ elif show_step_details and callable(workflow.steps) and workflow_response.content:
873
+ step_panel = create_panel(
874
+ content=Markdown(workflow_response.content) if markdown else workflow_response.content, # type: ignore
875
+ title="Custom Function (Completed)",
876
+ border_style="orange3",
877
+ )
878
+ console.print(step_panel) # type: ignore
879
+
880
+ # Show final summary
881
+ if workflow_response.metadata:
882
+ status = workflow_response.status.value # type: ignore
883
+ summary_content = ""
884
+ summary_content += f"""\n\n**Status:** {status}"""
885
+ summary_content += f"""\n\n**Steps Completed:** {len(workflow_response.step_results) if workflow_response.step_results else 0}"""
886
+ summary_content = summary_content.strip()
887
+
888
+ summary_panel = create_panel(
889
+ content=Markdown(summary_content) if markdown else summary_content,
890
+ title="Execution Summary",
891
+ border_style="blue",
892
+ )
893
+ console.print(summary_panel) # type: ignore
894
+
895
+ live_log.update("")
896
+
897
+ # Final completion message
898
+ if show_time:
899
+ completion_text = Text(f"Completed in {response_timer.elapsed:.1f}s", style="bold green")
900
+ console.print(completion_text) # type: ignore
901
+
902
+ except Exception as e:
903
+ import traceback
904
+
905
+ traceback.print_exc()
906
+ response_timer.stop()
907
+ error_panel = create_panel(
908
+ content=f"Workflow execution failed: {str(e)}", title="Execution Error", border_style="red"
909
+ )
910
+ console.print(error_panel) # type: ignore
911
+
912
+
913
+ async def aprint_response_stream(
914
+ workflow: "Workflow",
915
+ input: Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]],
916
+ additional_data: Optional[Dict[str, Any]] = None,
917
+ user_id: Optional[str] = None,
918
+ session_id: Optional[str] = None,
919
+ audio: Optional[List[Audio]] = None,
920
+ images: Optional[List[Image]] = None,
921
+ videos: Optional[List[Video]] = None,
922
+ stream_intermediate_steps: bool = False,
923
+ markdown: bool = True,
924
+ show_time: bool = True,
925
+ show_step_details: bool = True,
926
+ console: Optional[Any] = None,
927
+ **kwargs: Any,
928
+ ) -> None:
929
+ """Print workflow execution with clean streaming - orange step blocks displayed once"""
930
+ if console is None:
931
+ from rich.console import Console
932
+
933
+ console = Console()
934
+
935
+ stream_intermediate_steps = True # With streaming print response, we need to stream intermediate steps
936
+
937
+ # Show workflow info (same as before)
938
+ media_info = []
939
+ if audio:
940
+ media_info.append(f"Audio files: {len(audio)}")
941
+ if images:
942
+ media_info.append(f"Images: {len(images)}")
943
+ if videos:
944
+ media_info.append(f"Videos: {len(videos)}")
945
+
946
+ workflow_info = f"""**Workflow:** {workflow.name}"""
947
+ if workflow.description:
948
+ workflow_info += f"""\n\n**Description:** {workflow.description}"""
949
+ workflow_info += f"""\n\n**Steps:** {workflow._get_step_count()} steps"""
950
+ if input:
951
+ if isinstance(input, str):
952
+ workflow_info += f"""\n\n**Message:** {input}"""
953
+ else:
954
+ # Handle structured input message
955
+ if isinstance(input, BaseModel):
956
+ data_display = input.model_dump_json(indent=2, exclude_none=True)
957
+ elif isinstance(input, (dict, list)):
958
+ import json
959
+
960
+ data_display = json.dumps(input, indent=2, default=str)
961
+ else:
962
+ data_display = str(input)
963
+ workflow_info += f"""\n\n**Structured Input:**\n```json\n{data_display}\n```"""
964
+ if user_id:
965
+ workflow_info += f"""\n\n**User ID:** {user_id}"""
966
+ if session_id:
967
+ workflow_info += f"""\n\n**Session ID:** {session_id}"""
968
+ workflow_info = workflow_info.strip()
969
+
970
+ workflow_panel = create_panel(
971
+ content=Markdown(workflow_info) if markdown else workflow_info,
972
+ title="Workflow Information",
973
+ border_style="cyan",
974
+ )
975
+ console.print(workflow_panel) # type: ignore
976
+
977
+ # Start timer
978
+ response_timer = Timer()
979
+ response_timer.start()
980
+
981
+ # Streaming execution variables
982
+ current_step_content = ""
983
+ current_step_name = ""
984
+ current_step_index = 0
985
+ step_results = []
986
+ step_started_printed = False
987
+ is_callable_function = callable(workflow.steps)
988
+
989
+ # Smart step hierarchy tracking
990
+ current_primitive_context = None # Current primitive being executed (parallel, loop, etc.)
991
+ step_display_cache = {} # type: ignore
992
+
993
+ def get_step_display_number(step_index: Union[int, tuple], step_name: str = "") -> str:
994
+ """Generate clean two-level step numbering: x.y format only"""
995
+
996
+ # Handle tuple format for child steps
997
+ if isinstance(step_index, tuple):
998
+ if len(step_index) >= 2:
999
+ parent_idx, sub_idx = step_index[0], step_index[1]
1000
+
1001
+ # Extract base parent index if it's nested
1002
+ if isinstance(parent_idx, tuple):
1003
+ base_idx = parent_idx[0] if len(parent_idx) > 0 else 0
1004
+ while isinstance(base_idx, tuple) and len(base_idx) > 0:
1005
+ base_idx = base_idx[0]
1006
+ else:
1007
+ base_idx = parent_idx
1008
+
1009
+ # Check context for parallel special case
1010
+ if current_primitive_context and current_primitive_context["type"] == "parallel":
1011
+ # For parallel child steps, all get the same number based on their actual step_index
1012
+ return f"Step {base_idx + 1}.{sub_idx + 1}"
1013
+ elif current_primitive_context and current_primitive_context["type"] == "loop":
1014
+ iteration = current_primitive_context.get("current_iteration", 1)
1015
+ return f"Step {base_idx + 1}.{sub_idx + 1} (Iteration {iteration})"
1016
+ else:
1017
+ # Regular child step numbering
1018
+ return f"Step {base_idx + 1}.{sub_idx + 1}" # type: ignore
1019
+ else:
1020
+ # Single element tuple - treat as main step
1021
+ return f"Step {step_index[0] + 1}"
1022
+
1023
+ # Handle integer step_index - main step
1024
+ if not current_primitive_context:
1025
+ # Regular main step
1026
+ return f"Step {step_index + 1}"
1027
+ else:
1028
+ # This shouldn't happen with the new logic, but fallback
1029
+ return f"Step {step_index + 1}"
1030
+
1031
+ with Live(console=console, refresh_per_second=10) as live_log:
1032
+ status = Status("Starting async workflow...", spinner="dots")
1033
+ live_log.update(status)
1034
+
1035
+ try:
1036
+ async for response in await workflow.arun(
1037
+ input=input,
1038
+ additional_data=additional_data,
1039
+ user_id=user_id,
1040
+ session_id=session_id,
1041
+ audio=audio,
1042
+ images=images,
1043
+ videos=videos,
1044
+ stream=True,
1045
+ stream_intermediate_steps=stream_intermediate_steps,
1046
+ **kwargs,
1047
+ ): # type: ignore
1048
+ # Handle the new event types
1049
+ if isinstance(response, WorkflowStartedEvent):
1050
+ status.update("Workflow started...")
1051
+ if is_callable_function:
1052
+ current_step_name = "Custom Function"
1053
+ current_step_index = 0
1054
+ live_log.update(status)
1055
+
1056
+ elif isinstance(response, StepStartedEvent):
1057
+ current_step_name = response.step_name or "Unknown"
1058
+ current_step_index = response.step_index or 0 # type: ignore
1059
+ current_step_content = ""
1060
+ step_started_printed = False
1061
+
1062
+ # Generate smart step number
1063
+ step_display = get_step_display_number(current_step_index, current_step_name)
1064
+ status.update(f"Starting {step_display}: {current_step_name}...")
1065
+ live_log.update(status)
1066
+
1067
+ elif isinstance(response, StepCompletedEvent):
1068
+ step_name = response.step_name or "Unknown"
1069
+ step_index = response.step_index or 0
1070
+
1071
+ # Generate smart step number for completion (will use cached value)
1072
+ step_display = get_step_display_number(step_index, step_name)
1073
+ status.update(f"Completed {step_display}: {step_name}")
1074
+
1075
+ if response.content:
1076
+ step_results.append(
1077
+ {
1078
+ "step_name": step_name,
1079
+ "step_index": step_index,
1080
+ "content": response.content,
1081
+ "event": response.event,
1082
+ }
1083
+ )
1084
+
1085
+ # Print the final step result in orange (only once)
1086
+ if show_step_details and current_step_content and not step_started_printed:
1087
+ live_log.update(status, refresh=True)
1088
+
1089
+ final_step_panel = create_panel(
1090
+ content=Markdown(current_step_content) if markdown else current_step_content,
1091
+ title=f"{step_display}: {step_name} (Completed)",
1092
+ border_style="orange3",
1093
+ )
1094
+ console.print(final_step_panel) # type: ignore
1095
+ step_started_printed = True
1096
+
1097
+ elif isinstance(response, LoopExecutionStartedEvent):
1098
+ current_step_name = response.step_name or "Loop"
1099
+ current_step_index = response.step_index or 0 # type: ignore
1100
+ current_step_content = ""
1101
+ step_started_printed = False
1102
+
1103
+ # Set up loop context
1104
+ current_primitive_context = {
1105
+ "type": "loop",
1106
+ "step_index": current_step_index,
1107
+ "sub_step_counter": 0,
1108
+ "current_iteration": 1,
1109
+ "max_iterations": response.max_iterations,
1110
+ }
1111
+
1112
+ # Clear cache for this primitive's sub-steps
1113
+ step_display_cache.clear()
1114
+
1115
+ status.update(f"Starting loop: {current_step_name} (max {response.max_iterations} iterations)...")
1116
+ live_log.update(status)
1117
+
1118
+ elif isinstance(response, LoopIterationStartedEvent):
1119
+ if current_primitive_context and current_primitive_context["type"] == "loop":
1120
+ current_primitive_context["current_iteration"] = response.iteration
1121
+ current_primitive_context["sub_step_counter"] = 0 # Reset for new iteration
1122
+ # Clear cache for new iteration
1123
+ step_display_cache.clear()
1124
+
1125
+ status.update(
1126
+ f"Loop iteration {response.iteration}/{response.max_iterations}: {response.step_name}..."
1127
+ )
1128
+ live_log.update(status)
1129
+
1130
+ elif isinstance(response, LoopIterationCompletedEvent):
1131
+ status.update(
1132
+ f"Completed iteration {response.iteration}/{response.max_iterations}: {response.step_name}"
1133
+ )
1134
+
1135
+ elif isinstance(response, LoopExecutionCompletedEvent):
1136
+ step_name = response.step_name or "Loop"
1137
+ step_index = response.step_index or 0
1138
+
1139
+ status.update(f"Completed loop: {step_name} ({response.total_iterations} iterations)")
1140
+ live_log.update(status, refresh=True)
1141
+
1142
+ # Print loop summary
1143
+ if show_step_details:
1144
+ summary_content = "**Loop Summary:**\n\n"
1145
+ summary_content += (
1146
+ f"- Total iterations: {response.total_iterations}/{response.max_iterations}\n"
1147
+ )
1148
+ summary_content += (
1149
+ f"- Total steps executed: {sum(len(iteration) for iteration in response.all_results)}\n"
1150
+ )
1151
+
1152
+ loop_summary_panel = create_panel(
1153
+ content=Markdown(summary_content) if markdown else summary_content,
1154
+ title=f"Loop {step_name} (Completed)",
1155
+ border_style="yellow",
1156
+ )
1157
+ console.print(loop_summary_panel) # type: ignore
1158
+
1159
+ # Reset context
1160
+ current_primitive_context = None
1161
+ step_display_cache.clear()
1162
+ step_started_printed = True
1163
+
1164
+ elif isinstance(response, ParallelExecutionStartedEvent):
1165
+ current_step_name = response.step_name or "Parallel Steps"
1166
+ current_step_index = response.step_index or 0 # type: ignore
1167
+ current_step_content = ""
1168
+ step_started_printed = False
1169
+
1170
+ # Set up parallel context
1171
+ current_primitive_context = {
1172
+ "type": "parallel",
1173
+ "step_index": current_step_index,
1174
+ "sub_step_counter": 0,
1175
+ "total_steps": response.parallel_step_count,
1176
+ }
1177
+
1178
+ # Clear cache for this primitive's sub-steps
1179
+ step_display_cache.clear()
1180
+
1181
+ # Print parallel execution summary panel
1182
+ live_log.update(status, refresh=True)
1183
+ parallel_summary = f"**Parallel Steps:** {response.parallel_step_count}"
1184
+ # Use get_step_display_number for consistent numbering
1185
+ step_display = get_step_display_number(current_step_index, current_step_name)
1186
+ parallel_panel = create_panel(
1187
+ content=Markdown(parallel_summary) if markdown else parallel_summary,
1188
+ title=f"{step_display}: {current_step_name}",
1189
+ border_style="cyan",
1190
+ )
1191
+ console.print(parallel_panel) # type: ignore
1192
+
1193
+ status.update(
1194
+ f"Starting parallel execution: {current_step_name} ({response.parallel_step_count} steps)..."
1195
+ )
1196
+ live_log.update(status)
1197
+
1198
+ elif isinstance(response, ParallelExecutionCompletedEvent):
1199
+ step_name = response.step_name or "Parallel Steps"
1200
+ step_index = response.step_index or 0
1201
+
1202
+ status.update(f"Completed parallel execution: {step_name}")
1203
+
1204
+ # Reset context
1205
+ current_primitive_context = None
1206
+ step_display_cache.clear()
1207
+
1208
+ elif isinstance(response, ConditionExecutionStartedEvent):
1209
+ current_step_name = response.step_name or "Condition"
1210
+ current_step_index = response.step_index or 0 # type: ignore
1211
+ current_step_content = ""
1212
+ step_started_printed = False
1213
+
1214
+ # Set up condition context
1215
+ current_primitive_context = {
1216
+ "type": "condition",
1217
+ "step_index": current_step_index,
1218
+ "sub_step_counter": 0,
1219
+ "condition_result": response.condition_result,
1220
+ }
1221
+
1222
+ # Clear cache for this primitive's sub-steps
1223
+ step_display_cache.clear()
1224
+
1225
+ condition_text = "met" if response.condition_result else "not met"
1226
+ status.update(f"Starting condition: {current_step_name} (condition {condition_text})...")
1227
+ live_log.update(status)
1228
+
1229
+ elif isinstance(response, ConditionExecutionCompletedEvent):
1230
+ step_name = response.step_name or "Condition"
1231
+ step_index = response.step_index or 0
1232
+
1233
+ status.update(f"Completed condition: {step_name}")
1234
+
1235
+ # Reset context
1236
+ current_primitive_context = None
1237
+ step_display_cache.clear()
1238
+
1239
+ elif isinstance(response, RouterExecutionStartedEvent):
1240
+ current_step_name = response.step_name or "Router"
1241
+ current_step_index = response.step_index or 0 # type: ignore
1242
+ current_step_content = ""
1243
+ step_started_printed = False
1244
+
1245
+ # Set up router context
1246
+ current_primitive_context = {
1247
+ "type": "router",
1248
+ "step_index": current_step_index,
1249
+ "sub_step_counter": 0,
1250
+ "selected_steps": response.selected_steps,
1251
+ }
1252
+
1253
+ # Clear cache for this primitive's sub-steps
1254
+ step_display_cache.clear()
1255
+
1256
+ selected_steps_text = ", ".join(response.selected_steps) if response.selected_steps else "none"
1257
+ status.update(f"Starting router: {current_step_name} (selected: {selected_steps_text})...")
1258
+ live_log.update(status)
1259
+
1260
+ elif isinstance(response, RouterExecutionCompletedEvent):
1261
+ step_name = response.step_name or "Router"
1262
+ step_index = response.step_index or 0
1263
+
1264
+ status.update(f"Completed router: {step_name}")
1265
+
1266
+ # Print router summary
1267
+ if show_step_details:
1268
+ selected_steps_text = ", ".join(response.selected_steps) if response.selected_steps else "none"
1269
+ summary_content = "**Router Summary:**\n\n"
1270
+ summary_content += f"- Selected steps: {selected_steps_text}\n"
1271
+ summary_content += f"- Executed steps: {response.executed_steps or 0}\n"
1272
+
1273
+ router_summary_panel = create_panel(
1274
+ content=Markdown(summary_content) if markdown else summary_content,
1275
+ title=f"Router {step_name} (Completed)",
1276
+ border_style="purple",
1277
+ )
1278
+ console.print(router_summary_panel) # type: ignore
1279
+
1280
+ # Reset context
1281
+ current_primitive_context = None
1282
+ step_display_cache.clear()
1283
+ step_started_printed = True
1284
+
1285
+ elif isinstance(response, StepsExecutionStartedEvent):
1286
+ current_step_name = response.step_name or "Steps"
1287
+ current_step_index = response.step_index or 0 # type: ignore
1288
+ current_step_content = ""
1289
+ step_started_printed = False
1290
+ status.update(f"Starting steps: {current_step_name} ({response.steps_count} steps)...")
1291
+ live_log.update(status)
1292
+
1293
+ elif isinstance(response, StepsExecutionCompletedEvent):
1294
+ step_name = response.step_name or "Steps"
1295
+ step_index = response.step_index or 0
1296
+
1297
+ status.update(f"Completed steps: {step_name}")
1298
+
1299
+ # Add results from executed steps to step_results
1300
+ if response.step_results:
1301
+ for i, step_result in enumerate(response.step_results):
1302
+ # Use the same numbering system as other primitives
1303
+ step_display_number = get_step_display_number(step_index, step_result.step_name or "")
1304
+ step_results.append(
1305
+ {
1306
+ "step_name": f"{step_display_number}: {step_result.step_name}",
1307
+ "step_index": step_index,
1308
+ "content": step_result.content,
1309
+ "event": "StepsStepResult",
1310
+ }
1311
+ )
1312
+
1313
+ # Print steps summary
1314
+ if show_step_details:
1315
+ summary_content = "**Steps Summary:**\n\n"
1316
+ summary_content += f"- Total steps: {response.steps_count or 0}\n"
1317
+ summary_content += f"- Executed steps: {response.executed_steps or 0}\n"
1318
+
1319
+ steps_summary_panel = create_panel(
1320
+ content=Markdown(summary_content) if markdown else summary_content,
1321
+ title=f"Steps {step_name} (Completed)",
1322
+ border_style="yellow",
1323
+ )
1324
+ console.print(steps_summary_panel) # type: ignore
1325
+
1326
+ step_started_printed = True
1327
+
1328
+ elif isinstance(response, WorkflowCompletedEvent):
1329
+ status.update("Workflow completed!")
1330
+
1331
+ # For callable functions, print the final content block here since there are no step events
1332
+ if is_callable_function and show_step_details and current_step_content and not step_started_printed:
1333
+ final_step_panel = create_panel(
1334
+ content=Markdown(current_step_content) if markdown else current_step_content,
1335
+ title="Custom Function (Completed)",
1336
+ border_style="orange3",
1337
+ )
1338
+ console.print(final_step_panel) # type: ignore
1339
+ step_started_printed = True
1340
+
1341
+ live_log.update(status, refresh=True)
1342
+
1343
+ # Show final summary
1344
+ if response.metadata:
1345
+ status = response.status
1346
+ summary_content = ""
1347
+ summary_content += f"""\n\n**Status:** {status}"""
1348
+ summary_content += (
1349
+ f"""\n\n**Steps Completed:** {len(response.step_results) if response.step_results else 0}"""
1350
+ )
1351
+ summary_content = summary_content.strip()
1352
+
1353
+ summary_panel = create_panel(
1354
+ content=Markdown(summary_content) if markdown else summary_content,
1355
+ title="Execution Summary",
1356
+ border_style="blue",
1357
+ )
1358
+ console.print(summary_panel) # type: ignore
1359
+
1360
+ else:
1361
+ if isinstance(response, str):
1362
+ response_str = response
1363
+ elif isinstance(response, StepOutputEvent):
1364
+ # Handle StepOutputEvent objects yielded from workflow
1365
+ response_str = response.content or "" # type: ignore
1366
+ else:
1367
+ from agno.run.agent import RunContentEvent
1368
+ from agno.run.team import RunContentEvent as TeamRunContentEvent
1369
+
1370
+ current_step_executor_type = None
1371
+ # Handle both integer and tuple step indices for parallel execution
1372
+ actual_step_index = current_step_index
1373
+ if isinstance(current_step_index, tuple):
1374
+ # For tuple indices, use the first element (parent step index)
1375
+ actual_step_index = current_step_index[0]
1376
+ # If it's nested tuple, keep extracting until we get an integer
1377
+ while isinstance(actual_step_index, tuple) and len(actual_step_index) > 0:
1378
+ actual_step_index = actual_step_index[0]
1379
+
1380
+ # Check if this is a streaming content event from agent or team
1381
+ if isinstance(
1382
+ response,
1383
+ (RunContentEvent, TeamRunContentEvent, WorkflowRunOutputEvent), # type: ignore
1384
+ ): # type: ignore
1385
+ # Handle WorkflowErrorEvent specifically
1386
+ if isinstance(response, WorkflowErrorEvent): # type: ignore
1387
+ response_str = response.error or "Workflow execution error" # type: ignore
1388
+ else:
1389
+ # Extract the content from the streaming event
1390
+ response_str = response.content # type: ignore
1391
+
1392
+ # Check if this is a team's final structured output
1393
+ is_structured_output = (
1394
+ isinstance(response, TeamRunContentEvent)
1395
+ and hasattr(response, "content_type")
1396
+ and response.content_type != "str"
1397
+ and response.content_type != ""
1398
+ )
1399
+ elif isinstance(response, RunContentEvent) and current_step_executor_type != "team":
1400
+ response_str = response.content # type: ignore
1401
+ else:
1402
+ continue
1403
+
1404
+ # Use the unified formatting function for consistency
1405
+ response_str = format_step_content_for_display(response_str) # type: ignore
1406
+
1407
+ # Filter out empty responses and add to current step content
1408
+ if response_str and response_str.strip():
1409
+ # If it's a structured output from a team, replace the content instead of appending
1410
+ if "is_structured_output" in locals() and is_structured_output:
1411
+ current_step_content = response_str
1412
+ else:
1413
+ current_step_content += response_str
1414
+
1415
+ # Live update the step panel with streaming content
1416
+ if show_step_details and not step_started_printed:
1417
+ # Generate smart step number for streaming title (will use cached value)
1418
+ step_display = get_step_display_number(current_step_index, current_step_name)
1419
+ title = f"{step_display}: {current_step_name} (Streaming...)"
1420
+ if is_callable_function:
1421
+ title = "Custom Function (Streaming...)"
1422
+
1423
+ # Show the streaming content live in orange panel
1424
+ live_step_panel = create_panel(
1425
+ content=Markdown(current_step_content) if markdown else current_step_content,
1426
+ title=title,
1427
+ border_style="orange3",
1428
+ )
1429
+
1430
+ # Create group with status and current step content
1431
+ group = Group(status, live_step_panel)
1432
+ live_log.update(group)
1433
+
1434
+ response_timer.stop()
1435
+
1436
+ live_log.update("")
1437
+
1438
+ # Final completion message
1439
+ if show_time:
1440
+ completion_text = Text(f"Completed in {response_timer.elapsed:.1f}s", style="bold green")
1441
+ console.print(completion_text) # type: ignore
1442
+
1443
+ except Exception as e:
1444
+ import traceback
1445
+
1446
+ traceback.print_exc()
1447
+ response_timer.stop()
1448
+ error_panel = create_panel(
1449
+ content=f"Workflow execution failed: {str(e)}", title="Execution Error", border_style="red"
1450
+ )
1451
+ console.print(error_panel) # type: ignore