agno 1.8.1__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 (580) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +2778 -4123
  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/memory/__init__.py +2 -10
  137. agno/memory/manager.py +1003 -148
  138. agno/models/aimlapi/__init__.py +2 -2
  139. agno/models/aimlapi/aimlapi.py +6 -6
  140. agno/models/anthropic/claude.py +129 -82
  141. agno/models/aws/bedrock.py +107 -175
  142. agno/models/aws/claude.py +64 -18
  143. agno/models/azure/ai_foundry.py +73 -23
  144. agno/models/base.py +347 -287
  145. agno/models/cerebras/cerebras.py +84 -27
  146. agno/models/cohere/chat.py +106 -98
  147. agno/models/google/gemini.py +100 -42
  148. agno/models/groq/groq.py +97 -35
  149. agno/models/huggingface/huggingface.py +92 -27
  150. agno/models/ibm/watsonx.py +72 -13
  151. agno/models/litellm/chat.py +85 -13
  152. agno/models/message.py +38 -144
  153. agno/models/meta/llama.py +85 -49
  154. agno/models/metrics.py +120 -0
  155. agno/models/mistral/mistral.py +90 -21
  156. agno/models/ollama/__init__.py +0 -2
  157. agno/models/ollama/chat.py +84 -46
  158. agno/models/openai/chat.py +121 -23
  159. agno/models/openai/responses.py +178 -105
  160. agno/models/perplexity/perplexity.py +26 -2
  161. agno/models/portkey/portkey.py +0 -7
  162. agno/models/response.py +14 -8
  163. agno/models/utils.py +20 -0
  164. agno/models/vercel/__init__.py +2 -2
  165. agno/models/vercel/v0.py +1 -1
  166. agno/models/vllm/__init__.py +2 -2
  167. agno/models/vllm/vllm.py +3 -3
  168. agno/models/xai/xai.py +10 -10
  169. agno/os/__init__.py +3 -0
  170. agno/os/app.py +393 -0
  171. agno/os/auth.py +47 -0
  172. agno/os/config.py +103 -0
  173. agno/os/interfaces/agui/__init__.py +3 -0
  174. agno/os/interfaces/agui/agui.py +31 -0
  175. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  176. agno/{app → os/interfaces}/agui/utils.py +65 -28
  177. agno/os/interfaces/base.py +21 -0
  178. agno/os/interfaces/slack/__init__.py +3 -0
  179. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  180. agno/os/interfaces/slack/slack.py +33 -0
  181. agno/os/interfaces/whatsapp/__init__.py +3 -0
  182. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  183. agno/os/interfaces/whatsapp/whatsapp.py +30 -0
  184. agno/os/router.py +843 -0
  185. agno/os/routers/__init__.py +3 -0
  186. agno/os/routers/evals/__init__.py +3 -0
  187. agno/os/routers/evals/evals.py +204 -0
  188. agno/os/routers/evals/schemas.py +142 -0
  189. agno/os/routers/evals/utils.py +161 -0
  190. agno/os/routers/knowledge/__init__.py +3 -0
  191. agno/os/routers/knowledge/knowledge.py +413 -0
  192. agno/os/routers/knowledge/schemas.py +118 -0
  193. agno/os/routers/memory/__init__.py +3 -0
  194. agno/os/routers/memory/memory.py +179 -0
  195. agno/os/routers/memory/schemas.py +58 -0
  196. agno/os/routers/metrics/__init__.py +3 -0
  197. agno/os/routers/metrics/metrics.py +58 -0
  198. agno/os/routers/metrics/schemas.py +47 -0
  199. agno/os/routers/session/__init__.py +3 -0
  200. agno/os/routers/session/session.py +163 -0
  201. agno/os/schema.py +892 -0
  202. agno/{app/playground → os}/settings.py +8 -15
  203. agno/os/utils.py +270 -0
  204. agno/reasoning/azure_ai_foundry.py +4 -4
  205. agno/reasoning/deepseek.py +4 -4
  206. agno/reasoning/default.py +6 -11
  207. agno/reasoning/groq.py +4 -4
  208. agno/reasoning/helpers.py +4 -6
  209. agno/reasoning/ollama.py +4 -4
  210. agno/reasoning/openai.py +4 -4
  211. agno/run/{response.py → agent.py} +144 -72
  212. agno/run/base.py +44 -58
  213. agno/run/cancel.py +83 -0
  214. agno/run/team.py +133 -77
  215. agno/run/workflow.py +537 -12
  216. agno/session/__init__.py +10 -0
  217. agno/session/agent.py +244 -0
  218. agno/session/summary.py +225 -0
  219. agno/session/team.py +262 -0
  220. agno/{storage/session/v2 → session}/workflow.py +47 -24
  221. agno/team/__init__.py +15 -16
  222. agno/team/team.py +2961 -4253
  223. agno/tools/agentql.py +14 -5
  224. agno/tools/airflow.py +9 -4
  225. agno/tools/api.py +7 -3
  226. agno/tools/apify.py +2 -46
  227. agno/tools/arxiv.py +8 -3
  228. agno/tools/aws_lambda.py +7 -5
  229. agno/tools/aws_ses.py +7 -1
  230. agno/tools/baidusearch.py +4 -1
  231. agno/tools/bitbucket.py +4 -4
  232. agno/tools/brandfetch.py +14 -11
  233. agno/tools/bravesearch.py +4 -1
  234. agno/tools/brightdata.py +42 -22
  235. agno/tools/browserbase.py +13 -4
  236. agno/tools/calcom.py +12 -10
  237. agno/tools/calculator.py +10 -27
  238. agno/tools/cartesia.py +18 -13
  239. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  240. agno/tools/confluence.py +8 -8
  241. agno/tools/crawl4ai.py +7 -1
  242. agno/tools/csv_toolkit.py +9 -8
  243. agno/tools/dalle.py +18 -11
  244. agno/tools/daytona.py +13 -16
  245. agno/tools/decorator.py +6 -3
  246. agno/tools/desi_vocal.py +16 -7
  247. agno/tools/discord.py +11 -8
  248. agno/tools/docker.py +30 -42
  249. agno/tools/duckdb.py +34 -53
  250. agno/tools/duckduckgo.py +8 -7
  251. agno/tools/e2b.py +61 -61
  252. agno/tools/eleven_labs.py +35 -28
  253. agno/tools/email.py +4 -1
  254. agno/tools/evm.py +7 -1
  255. agno/tools/exa.py +19 -14
  256. agno/tools/fal.py +29 -29
  257. agno/tools/file.py +9 -8
  258. agno/tools/financial_datasets.py +25 -44
  259. agno/tools/firecrawl.py +22 -22
  260. agno/tools/function.py +68 -17
  261. agno/tools/giphy.py +22 -10
  262. agno/tools/github.py +48 -126
  263. agno/tools/gmail.py +45 -61
  264. agno/tools/google_bigquery.py +7 -6
  265. agno/tools/google_maps.py +11 -26
  266. agno/tools/googlesearch.py +7 -2
  267. agno/tools/googlesheets.py +21 -17
  268. agno/tools/hackernews.py +9 -5
  269. agno/tools/jina.py +5 -4
  270. agno/tools/jira.py +18 -9
  271. agno/tools/knowledge.py +31 -32
  272. agno/tools/linear.py +18 -33
  273. agno/tools/linkup.py +5 -1
  274. agno/tools/local_file_system.py +8 -5
  275. agno/tools/lumalab.py +31 -19
  276. agno/tools/mem0.py +18 -12
  277. agno/tools/memori.py +14 -10
  278. agno/tools/mlx_transcribe.py +3 -2
  279. agno/tools/models/azure_openai.py +32 -14
  280. agno/tools/models/gemini.py +58 -31
  281. agno/tools/models/groq.py +29 -20
  282. agno/tools/models/nebius.py +27 -11
  283. agno/tools/models_labs.py +39 -15
  284. agno/tools/moviepy_video.py +7 -6
  285. agno/tools/neo4j.py +10 -8
  286. agno/tools/newspaper.py +7 -2
  287. agno/tools/newspaper4k.py +8 -3
  288. agno/tools/openai.py +57 -26
  289. agno/tools/openbb.py +12 -11
  290. agno/tools/opencv.py +62 -46
  291. agno/tools/openweather.py +14 -12
  292. agno/tools/pandas.py +11 -3
  293. agno/tools/postgres.py +4 -12
  294. agno/tools/pubmed.py +4 -1
  295. agno/tools/python.py +9 -22
  296. agno/tools/reasoning.py +35 -27
  297. agno/tools/reddit.py +11 -26
  298. agno/tools/replicate.py +54 -41
  299. agno/tools/resend.py +4 -1
  300. agno/tools/scrapegraph.py +15 -14
  301. agno/tools/searxng.py +10 -23
  302. agno/tools/serpapi.py +6 -3
  303. agno/tools/serper.py +13 -4
  304. agno/tools/shell.py +9 -2
  305. agno/tools/slack.py +12 -11
  306. agno/tools/sleep.py +3 -2
  307. agno/tools/spider.py +24 -4
  308. agno/tools/sql.py +7 -6
  309. agno/tools/tavily.py +6 -4
  310. agno/tools/telegram.py +12 -4
  311. agno/tools/todoist.py +11 -31
  312. agno/tools/toolkit.py +1 -1
  313. agno/tools/trafilatura.py +22 -6
  314. agno/tools/trello.py +9 -22
  315. agno/tools/twilio.py +10 -3
  316. agno/tools/user_control_flow.py +6 -1
  317. agno/tools/valyu.py +34 -5
  318. agno/tools/visualization.py +19 -28
  319. agno/tools/webbrowser.py +4 -3
  320. agno/tools/webex.py +11 -7
  321. agno/tools/website.py +15 -46
  322. agno/tools/webtools.py +12 -4
  323. agno/tools/whatsapp.py +5 -9
  324. agno/tools/wikipedia.py +20 -13
  325. agno/tools/x.py +14 -13
  326. agno/tools/yfinance.py +13 -40
  327. agno/tools/youtube.py +26 -20
  328. agno/tools/zendesk.py +7 -2
  329. agno/tools/zep.py +10 -7
  330. agno/tools/zoom.py +10 -9
  331. agno/utils/common.py +1 -19
  332. agno/utils/events.py +95 -118
  333. agno/utils/knowledge.py +29 -0
  334. agno/utils/log.py +2 -2
  335. agno/utils/mcp.py +11 -5
  336. agno/utils/media.py +39 -0
  337. agno/utils/message.py +12 -1
  338. agno/utils/models/claude.py +6 -4
  339. agno/utils/models/mistral.py +8 -7
  340. agno/utils/models/schema_utils.py +3 -3
  341. agno/utils/pprint.py +33 -32
  342. agno/utils/print_response/agent.py +779 -0
  343. agno/utils/print_response/team.py +1565 -0
  344. agno/utils/print_response/workflow.py +1451 -0
  345. agno/utils/prompts.py +14 -14
  346. agno/utils/reasoning.py +87 -0
  347. agno/utils/response.py +42 -42
  348. agno/utils/string.py +8 -22
  349. agno/utils/team.py +50 -0
  350. agno/utils/timer.py +2 -2
  351. agno/vectordb/base.py +33 -21
  352. agno/vectordb/cassandra/cassandra.py +287 -23
  353. agno/vectordb/chroma/chromadb.py +482 -59
  354. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  355. agno/vectordb/couchbase/couchbase.py +309 -29
  356. agno/vectordb/lancedb/lance_db.py +360 -21
  357. agno/vectordb/langchaindb/__init__.py +5 -0
  358. agno/vectordb/langchaindb/langchaindb.py +145 -0
  359. agno/vectordb/lightrag/__init__.py +5 -0
  360. agno/vectordb/lightrag/lightrag.py +374 -0
  361. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  362. agno/vectordb/milvus/milvus.py +242 -32
  363. agno/vectordb/mongodb/mongodb.py +200 -24
  364. agno/vectordb/pgvector/pgvector.py +319 -37
  365. agno/vectordb/pineconedb/pineconedb.py +221 -27
  366. agno/vectordb/qdrant/qdrant.py +334 -14
  367. agno/vectordb/singlestore/singlestore.py +286 -29
  368. agno/vectordb/surrealdb/surrealdb.py +187 -7
  369. agno/vectordb/upstashdb/upstashdb.py +342 -26
  370. agno/vectordb/weaviate/weaviate.py +227 -165
  371. agno/workflow/__init__.py +17 -13
  372. agno/workflow/{v2/condition.py → condition.py} +135 -32
  373. agno/workflow/{v2/loop.py → loop.py} +115 -28
  374. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  375. agno/workflow/{v2/router.py → router.py} +133 -32
  376. agno/workflow/{v2/step.py → step.py} +200 -42
  377. agno/workflow/{v2/steps.py → steps.py} +147 -66
  378. agno/workflow/types.py +482 -0
  379. agno/workflow/workflow.py +2394 -696
  380. agno-2.0.0a1.dist-info/METADATA +355 -0
  381. agno-2.0.0a1.dist-info/RECORD +514 -0
  382. agno/agent/metrics.py +0 -107
  383. agno/api/app.py +0 -35
  384. agno/api/playground.py +0 -92
  385. agno/api/schemas/app.py +0 -12
  386. agno/api/schemas/playground.py +0 -22
  387. agno/api/schemas/user.py +0 -35
  388. agno/api/schemas/workspace.py +0 -46
  389. agno/api/user.py +0 -160
  390. agno/api/workflows.py +0 -33
  391. agno/api/workspace.py +0 -175
  392. agno/app/agui/__init__.py +0 -3
  393. agno/app/agui/app.py +0 -17
  394. agno/app/agui/sync_router.py +0 -120
  395. agno/app/base.py +0 -186
  396. agno/app/discord/__init__.py +0 -3
  397. agno/app/fastapi/__init__.py +0 -3
  398. agno/app/fastapi/app.py +0 -107
  399. agno/app/fastapi/async_router.py +0 -457
  400. agno/app/fastapi/sync_router.py +0 -448
  401. agno/app/playground/app.py +0 -228
  402. agno/app/playground/async_router.py +0 -1050
  403. agno/app/playground/deploy.py +0 -249
  404. agno/app/playground/operator.py +0 -183
  405. agno/app/playground/schemas.py +0 -220
  406. agno/app/playground/serve.py +0 -55
  407. agno/app/playground/sync_router.py +0 -1042
  408. agno/app/playground/utils.py +0 -46
  409. agno/app/settings.py +0 -15
  410. agno/app/slack/__init__.py +0 -3
  411. agno/app/slack/app.py +0 -19
  412. agno/app/slack/sync_router.py +0 -92
  413. agno/app/utils.py +0 -54
  414. agno/app/whatsapp/__init__.py +0 -3
  415. agno/app/whatsapp/app.py +0 -15
  416. agno/app/whatsapp/sync_router.py +0 -197
  417. agno/cli/auth_server.py +0 -249
  418. agno/cli/config.py +0 -274
  419. agno/cli/console.py +0 -88
  420. agno/cli/credentials.py +0 -23
  421. agno/cli/entrypoint.py +0 -571
  422. agno/cli/operator.py +0 -357
  423. agno/cli/settings.py +0 -96
  424. agno/cli/ws/ws_cli.py +0 -817
  425. agno/constants.py +0 -13
  426. agno/document/__init__.py +0 -5
  427. agno/document/chunking/semantic.py +0 -45
  428. agno/document/chunking/strategy.py +0 -31
  429. agno/document/reader/__init__.py +0 -5
  430. agno/document/reader/base.py +0 -47
  431. agno/document/reader/docx_reader.py +0 -60
  432. agno/document/reader/gcs/pdf_reader.py +0 -44
  433. agno/document/reader/s3/pdf_reader.py +0 -59
  434. agno/document/reader/s3/text_reader.py +0 -63
  435. agno/document/reader/url_reader.py +0 -59
  436. agno/document/reader/youtube_reader.py +0 -58
  437. agno/embedder/__init__.py +0 -5
  438. agno/embedder/langdb.py +0 -80
  439. agno/embedder/mistral.py +0 -82
  440. agno/embedder/openai.py +0 -78
  441. agno/file/__init__.py +0 -5
  442. agno/file/file.py +0 -16
  443. agno/file/local/csv.py +0 -32
  444. agno/file/local/txt.py +0 -19
  445. agno/infra/app.py +0 -240
  446. agno/infra/base.py +0 -144
  447. agno/infra/context.py +0 -20
  448. agno/infra/db_app.py +0 -52
  449. agno/infra/resource.py +0 -205
  450. agno/infra/resources.py +0 -55
  451. agno/knowledge/agent.py +0 -702
  452. agno/knowledge/arxiv.py +0 -33
  453. agno/knowledge/combined.py +0 -36
  454. agno/knowledge/csv.py +0 -144
  455. agno/knowledge/csv_url.py +0 -124
  456. agno/knowledge/document.py +0 -223
  457. agno/knowledge/docx.py +0 -137
  458. agno/knowledge/firecrawl.py +0 -34
  459. agno/knowledge/gcs/__init__.py +0 -0
  460. agno/knowledge/gcs/base.py +0 -39
  461. agno/knowledge/gcs/pdf.py +0 -125
  462. agno/knowledge/json.py +0 -137
  463. agno/knowledge/langchain.py +0 -71
  464. agno/knowledge/light_rag.py +0 -273
  465. agno/knowledge/llamaindex.py +0 -66
  466. agno/knowledge/markdown.py +0 -154
  467. agno/knowledge/pdf.py +0 -164
  468. agno/knowledge/pdf_bytes.py +0 -42
  469. agno/knowledge/pdf_url.py +0 -148
  470. agno/knowledge/s3/__init__.py +0 -0
  471. agno/knowledge/s3/base.py +0 -64
  472. agno/knowledge/s3/pdf.py +0 -33
  473. agno/knowledge/s3/text.py +0 -34
  474. agno/knowledge/text.py +0 -141
  475. agno/knowledge/url.py +0 -46
  476. agno/knowledge/website.py +0 -179
  477. agno/knowledge/wikipedia.py +0 -32
  478. agno/knowledge/youtube.py +0 -35
  479. agno/memory/agent.py +0 -423
  480. agno/memory/classifier.py +0 -104
  481. agno/memory/db/__init__.py +0 -5
  482. agno/memory/db/base.py +0 -42
  483. agno/memory/db/mongodb.py +0 -189
  484. agno/memory/db/postgres.py +0 -203
  485. agno/memory/db/sqlite.py +0 -193
  486. agno/memory/memory.py +0 -22
  487. agno/memory/row.py +0 -36
  488. agno/memory/summarizer.py +0 -201
  489. agno/memory/summary.py +0 -19
  490. agno/memory/team.py +0 -415
  491. agno/memory/v2/__init__.py +0 -2
  492. agno/memory/v2/db/__init__.py +0 -1
  493. agno/memory/v2/db/base.py +0 -42
  494. agno/memory/v2/db/firestore.py +0 -339
  495. agno/memory/v2/db/mongodb.py +0 -196
  496. agno/memory/v2/db/postgres.py +0 -214
  497. agno/memory/v2/db/redis.py +0 -187
  498. agno/memory/v2/db/schema.py +0 -54
  499. agno/memory/v2/db/sqlite.py +0 -209
  500. agno/memory/v2/manager.py +0 -437
  501. agno/memory/v2/memory.py +0 -1097
  502. agno/memory/v2/schema.py +0 -55
  503. agno/memory/v2/summarizer.py +0 -215
  504. agno/memory/workflow.py +0 -38
  505. agno/models/ollama/tools.py +0 -430
  506. agno/models/qwen/__init__.py +0 -5
  507. agno/playground/__init__.py +0 -10
  508. agno/playground/deploy.py +0 -3
  509. agno/playground/playground.py +0 -3
  510. agno/playground/serve.py +0 -3
  511. agno/playground/settings.py +0 -3
  512. agno/reranker/__init__.py +0 -0
  513. agno/run/v2/__init__.py +0 -0
  514. agno/run/v2/workflow.py +0 -567
  515. agno/storage/__init__.py +0 -0
  516. agno/storage/agent/__init__.py +0 -0
  517. agno/storage/agent/dynamodb.py +0 -1
  518. agno/storage/agent/json.py +0 -1
  519. agno/storage/agent/mongodb.py +0 -1
  520. agno/storage/agent/postgres.py +0 -1
  521. agno/storage/agent/singlestore.py +0 -1
  522. agno/storage/agent/sqlite.py +0 -1
  523. agno/storage/agent/yaml.py +0 -1
  524. agno/storage/base.py +0 -60
  525. agno/storage/dynamodb.py +0 -673
  526. agno/storage/firestore.py +0 -297
  527. agno/storage/gcs_json.py +0 -261
  528. agno/storage/in_memory.py +0 -234
  529. agno/storage/json.py +0 -237
  530. agno/storage/mongodb.py +0 -328
  531. agno/storage/mysql.py +0 -685
  532. agno/storage/postgres.py +0 -682
  533. agno/storage/redis.py +0 -336
  534. agno/storage/session/__init__.py +0 -16
  535. agno/storage/session/agent.py +0 -64
  536. agno/storage/session/team.py +0 -63
  537. agno/storage/session/v2/__init__.py +0 -5
  538. agno/storage/session/workflow.py +0 -61
  539. agno/storage/singlestore.py +0 -606
  540. agno/storage/sqlite.py +0 -646
  541. agno/storage/workflow/__init__.py +0 -0
  542. agno/storage/workflow/mongodb.py +0 -1
  543. agno/storage/workflow/postgres.py +0 -1
  544. agno/storage/workflow/sqlite.py +0 -1
  545. agno/storage/yaml.py +0 -241
  546. agno/tools/thinking.py +0 -73
  547. agno/utils/defaults.py +0 -57
  548. agno/utils/filesystem.py +0 -39
  549. agno/utils/git.py +0 -52
  550. agno/utils/json_io.py +0 -30
  551. agno/utils/load_env.py +0 -19
  552. agno/utils/py_io.py +0 -19
  553. agno/utils/pyproject.py +0 -18
  554. agno/utils/resource_filter.py +0 -31
  555. agno/workflow/v2/__init__.py +0 -21
  556. agno/workflow/v2/types.py +0 -357
  557. agno/workflow/v2/workflow.py +0 -3312
  558. agno/workspace/__init__.py +0 -0
  559. agno/workspace/config.py +0 -325
  560. agno/workspace/enums.py +0 -6
  561. agno/workspace/helpers.py +0 -52
  562. agno/workspace/operator.py +0 -757
  563. agno/workspace/settings.py +0 -158
  564. agno-1.8.1.dist-info/METADATA +0 -982
  565. agno-1.8.1.dist-info/RECORD +0 -566
  566. agno-1.8.1.dist-info/entry_points.txt +0 -3
  567. /agno/{app → db/migrations}/__init__.py +0 -0
  568. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  569. /agno/{cli → integrations}/__init__.py +0 -0
  570. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  571. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  572. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  573. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  574. /agno/{app → os/interfaces}/slack/security.py +0 -0
  575. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  576. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  577. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  578. {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
  579. {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
  580. {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
agno/models/base.py CHANGED
@@ -5,7 +5,6 @@ from dataclasses import dataclass, field
5
5
  from types import AsyncGeneratorType, GeneratorType
6
6
  from typing import (
7
7
  Any,
8
- AsyncGenerator,
9
8
  AsyncIterator,
10
9
  Dict,
11
10
  Iterator,
@@ -22,12 +21,13 @@ from uuid import uuid4
22
21
  from pydantic import BaseModel
23
22
 
24
23
  from agno.exceptions import AgentRunException
25
- from agno.media import AudioResponse, ImageArtifact
26
- from agno.models.message import Citations, Message, MessageMetrics
24
+ from agno.media import Audio, AudioArtifact, AudioResponse, Image, ImageArtifact, Video, VideoArtifact
25
+ from agno.models.message import Citations, Message
26
+ from agno.models.metrics import Metrics
27
27
  from agno.models.response import ModelResponse, ModelResponseEvent, ToolExecution
28
- from agno.run.response import RunResponseContentEvent, RunResponseEvent
29
- from agno.run.team import RunResponseContentEvent as TeamRunResponseContentEvent
30
- from agno.run.team import TeamRunResponseEvent
28
+ from agno.run.agent import RunContentEvent, RunOutput, RunOutputEvent
29
+ from agno.run.team import RunContentEvent as TeamRunContentEvent
30
+ from agno.run.team import TeamRunOutputEvent
31
31
  from agno.tools.function import Function, FunctionCall, FunctionExecutionResult, UserInputField
32
32
  from agno.utils.log import log_debug, log_error, log_warning
33
33
  from agno.utils.timer import Timer
@@ -38,13 +38,14 @@ from agno.utils.tools import get_function_call_for_tool_call, get_function_call_
38
38
  class MessageData:
39
39
  response_role: Optional[Literal["system", "user", "assistant", "tool"]] = None
40
40
  response_content: Any = ""
41
- response_thinking: Any = ""
42
- response_redacted_thinking: Any = ""
41
+ response_reasoning_content: Any = ""
42
+ response_redacted_reasoning_content: Any = ""
43
43
  response_citations: Optional[Citations] = None
44
44
  response_tool_calls: List[Dict[str, Any]] = field(default_factory=list)
45
45
 
46
46
  response_audio: Optional[AudioResponse] = None
47
47
  response_image: Optional[ImageArtifact] = None
48
+ response_video: Optional[VideoArtifact] = None
48
49
 
49
50
  # Data from the provider that we might need on subsequent messages
50
51
  response_provider_data: Optional[Dict[str, Any]] = None
@@ -61,143 +62,17 @@ def _log_messages(messages: List[Message]) -> None:
61
62
  m.log(metrics=False)
62
63
 
63
64
 
64
- def _add_usage_metrics_to_assistant_message(assistant_message: Message, response_usage: Any) -> None:
65
- """
66
- Add usage metrics from the model provider to the assistant message.
67
-
68
- Args:
69
- assistant_message: Message to update with metrics
70
- response_usage: Usage data from model provider
71
- """
72
-
73
- # Standard token metrics
74
- if isinstance(response_usage, dict):
75
- if "input_tokens" in response_usage and response_usage.get("input_tokens") is not None:
76
- assistant_message.metrics.input_tokens = response_usage.get("input_tokens", 0)
77
- if "output_tokens" in response_usage and response_usage.get("output_tokens") is not None:
78
- assistant_message.metrics.output_tokens = response_usage.get("output_tokens", 0)
79
- if "prompt_tokens" in response_usage and response_usage.get("prompt_tokens") is not None:
80
- assistant_message.metrics.input_tokens = response_usage.get("prompt_tokens", 0)
81
- if "completion_tokens" in response_usage and response_usage.get("completion_tokens") is not None:
82
- assistant_message.metrics.output_tokens = response_usage.get("completion_tokens", 0)
83
- if "cached_tokens" in response_usage and response_usage.get("cached_tokens") is not None:
84
- assistant_message.metrics.cached_tokens = response_usage.get("cached_tokens", 0)
85
- if "cache_write_tokens" in response_usage and response_usage.get("cache_write_tokens") is not None:
86
- assistant_message.metrics.cache_write_tokens = response_usage.get("cache_write_tokens", 0)
87
- if "total_tokens" in response_usage and response_usage.get("total_tokens") is not None:
88
- assistant_message.metrics.total_tokens = response_usage.get("total_tokens", 0)
89
- else:
90
- assistant_message.metrics.total_tokens = (
91
- assistant_message.metrics.input_tokens + assistant_message.metrics.output_tokens
92
- )
93
- else:
94
- if hasattr(response_usage, "input_tokens") and response_usage.input_tokens:
95
- assistant_message.metrics.input_tokens = response_usage.input_tokens
96
- if hasattr(response_usage, "output_tokens") and response_usage.output_tokens:
97
- assistant_message.metrics.output_tokens = response_usage.output_tokens
98
- if hasattr(response_usage, "prompt_tokens") and response_usage.prompt_tokens is not None:
99
- assistant_message.metrics.input_tokens = response_usage.prompt_tokens
100
- assistant_message.metrics.prompt_tokens = response_usage.prompt_tokens
101
- if hasattr(response_usage, "completion_tokens") and response_usage.completion_tokens is not None:
102
- assistant_message.metrics.output_tokens = response_usage.completion_tokens
103
- assistant_message.metrics.completion_tokens = response_usage.completion_tokens
104
- if hasattr(response_usage, "total_tokens") and response_usage.total_tokens is not None:
105
- assistant_message.metrics.total_tokens = response_usage.total_tokens
106
- if hasattr(response_usage, "cached_tokens") and response_usage.cached_tokens is not None:
107
- assistant_message.metrics.cached_tokens = response_usage.cached_tokens
108
- if hasattr(response_usage, "cache_write_tokens") and response_usage.cache_write_tokens is not None:
109
- assistant_message.metrics.cache_write_tokens = response_usage.cache_write_tokens
110
-
111
- # If you didn't capture any total tokens
112
- if not assistant_message.metrics.total_tokens:
113
- if assistant_message.metrics.input_tokens is None:
114
- assistant_message.metrics.input_tokens = 0
115
- if assistant_message.metrics.output_tokens is None:
116
- assistant_message.metrics.output_tokens = 0
117
-
118
- assistant_message.metrics.total_tokens = (
119
- assistant_message.metrics.input_tokens + assistant_message.metrics.output_tokens
120
- )
121
-
122
- # Additional metrics (e.g., from Groq, Ollama)
123
- if isinstance(response_usage, dict) and "additional_metrics" in response_usage:
124
- assistant_message.metrics.additional_metrics = response_usage["additional_metrics"]
125
-
126
- # Token details (e.g., from OpenAI)
127
- if hasattr(response_usage, "prompt_tokens_details"):
128
- if isinstance(response_usage.prompt_tokens_details, dict):
129
- assistant_message.metrics.prompt_tokens_details = response_usage.prompt_tokens_details
130
- if (
131
- "audio_tokens" in response_usage.prompt_tokens_details
132
- and response_usage.prompt_tokens_details["audio_tokens"] is not None
133
- ):
134
- assistant_message.metrics.input_audio_tokens = response_usage.prompt_tokens_details["audio_tokens"]
135
- if (
136
- "cached_tokens" in response_usage.prompt_tokens_details
137
- and response_usage.prompt_tokens_details["cached_tokens"] is not None
138
- ):
139
- assistant_message.metrics.cached_tokens = response_usage.prompt_tokens_details["cached_tokens"]
140
- elif hasattr(response_usage.prompt_tokens_details, "model_dump"):
141
- assistant_message.metrics.prompt_tokens_details = response_usage.prompt_tokens_details.model_dump(
142
- exclude_none=True
143
- )
144
- if (
145
- hasattr(response_usage.prompt_tokens_details, "audio_tokens")
146
- and response_usage.prompt_tokens_details.audio_tokens is not None
147
- ):
148
- assistant_message.metrics.input_audio_tokens = response_usage.prompt_tokens_details.audio_tokens
149
- if (
150
- hasattr(response_usage.prompt_tokens_details, "cached_tokens")
151
- and response_usage.prompt_tokens_details.cached_tokens is not None
152
- ):
153
- assistant_message.metrics.cached_tokens = response_usage.prompt_tokens_details.cached_tokens
154
-
155
- if hasattr(response_usage, "completion_tokens_details"):
156
- if isinstance(response_usage.completion_tokens_details, dict):
157
- assistant_message.metrics.completion_tokens_details = response_usage.completion_tokens_details
158
- if (
159
- "audio_tokens" in response_usage.completion_tokens_details
160
- and response_usage.completion_tokens_details["audio_tokens"] is not None
161
- ):
162
- assistant_message.metrics.output_audio_tokens = response_usage.completion_tokens_details["audio_tokens"]
163
- if (
164
- "reasoning_tokens" in response_usage.completion_tokens_details
165
- and response_usage.completion_tokens_details["reasoning_tokens"] is not None
166
- ):
167
- assistant_message.metrics.reasoning_tokens = response_usage.completion_tokens_details[
168
- "reasoning_tokens"
169
- ]
170
- elif hasattr(response_usage.completion_tokens_details, "model_dump"):
171
- assistant_message.metrics.completion_tokens_details = response_usage.completion_tokens_details.model_dump(
172
- exclude_none=True
173
- )
174
- if (
175
- hasattr(response_usage.completion_tokens_details, "audio_tokens")
176
- and response_usage.completion_tokens_details.audio_tokens is not None
177
- ):
178
- assistant_message.metrics.output_audio_tokens = response_usage.completion_tokens_details.audio_tokens
179
- if (
180
- hasattr(response_usage.completion_tokens_details, "reasoning_tokens")
181
- and response_usage.completion_tokens_details.reasoning_tokens is not None
182
- ):
183
- assistant_message.metrics.reasoning_tokens = response_usage.completion_tokens_details.reasoning_tokens
184
-
185
- assistant_message.metrics.audio_tokens = (
186
- assistant_message.metrics.input_audio_tokens + assistant_message.metrics.output_audio_tokens
187
- )
188
-
189
-
190
- def _handle_agent_exception(a_exc: AgentRunException, additional_messages: Optional[List[Message]] = None) -> None:
65
+ def _handle_agent_exception(a_exc: AgentRunException, additional_input: Optional[List[Message]] = None) -> None:
191
66
  """Handle AgentRunException and collect additional messages."""
192
- if additional_messages is None:
193
- additional_messages = []
67
+ if additional_input is None:
68
+ additional_input = []
194
69
  if a_exc.user_message is not None:
195
70
  msg = (
196
71
  Message(role="user", content=a_exc.user_message)
197
72
  if isinstance(a_exc.user_message, str)
198
73
  else a_exc.user_message
199
74
  )
200
- additional_messages.append(msg)
75
+ additional_input.append(msg)
201
76
 
202
77
  if a_exc.agent_message is not None:
203
78
  msg = (
@@ -205,20 +80,20 @@ def _handle_agent_exception(a_exc: AgentRunException, additional_messages: Optio
205
80
  if isinstance(a_exc.agent_message, str)
206
81
  else a_exc.agent_message
207
82
  )
208
- additional_messages.append(msg)
83
+ additional_input.append(msg)
209
84
 
210
85
  if a_exc.messages:
211
86
  for m in a_exc.messages:
212
87
  if isinstance(m, Message):
213
- additional_messages.append(m)
88
+ additional_input.append(m)
214
89
  elif isinstance(m, dict):
215
90
  try:
216
- additional_messages.append(Message(**m))
91
+ additional_input.append(Message(**m))
217
92
  except Exception as e:
218
93
  log_warning(f"Failed to convert dict to Message: {e}")
219
94
 
220
95
  if a_exc.stop_execution:
221
- for m in additional_messages:
96
+ for m in additional_input:
222
97
  m.stop_after_tool_call = True
223
98
 
224
99
 
@@ -270,23 +145,23 @@ class Model(ABC):
270
145
  return self.provider or self.name or self.__class__.__name__
271
146
 
272
147
  @abstractmethod
273
- def invoke(self, *args, **kwargs) -> Any:
148
+ def invoke(self, *args, **kwargs) -> ModelResponse:
274
149
  pass
275
150
 
276
151
  @abstractmethod
277
- async def ainvoke(self, *args, **kwargs) -> Any:
152
+ async def ainvoke(self, *args, **kwargs) -> ModelResponse:
278
153
  pass
279
154
 
280
155
  @abstractmethod
281
- def invoke_stream(self, *args, **kwargs) -> Iterator[Any]:
156
+ def invoke_stream(self, *args, **kwargs) -> Iterator[ModelResponse]:
282
157
  pass
283
158
 
284
159
  @abstractmethod
285
- async def ainvoke_stream(self, *args, **kwargs) -> AsyncGenerator[Any, None]:
160
+ def ainvoke_stream(self, *args, **kwargs) -> AsyncIterator[ModelResponse]:
286
161
  pass
287
162
 
288
163
  @abstractmethod
289
- def parse_provider_response(self, response: Any, **kwargs) -> ModelResponse:
164
+ def _parse_provider_response(self, response: Any, **kwargs) -> ModelResponse:
290
165
  """
291
166
  Parse the raw response from the model provider into a ModelResponse.
292
167
 
@@ -299,7 +174,7 @@ class Model(ABC):
299
174
  pass
300
175
 
301
176
  @abstractmethod
302
- def parse_provider_response_delta(self, response: Any) -> ModelResponse:
177
+ def _parse_provider_response_delta(self, response: Any) -> ModelResponse:
303
178
  """
304
179
  Parse the streaming response from the model provider into ModelResponse objects.
305
180
 
@@ -319,6 +194,7 @@ class Model(ABC):
319
194
  functions: Optional[Dict[str, Function]] = None,
320
195
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
321
196
  tool_call_limit: Optional[int] = None,
197
+ run_response: Optional[RunOutput] = None,
322
198
  ) -> ModelResponse:
323
199
  """
324
200
  Generate a response from the model.
@@ -342,6 +218,7 @@ class Model(ABC):
342
218
  response_format=response_format,
343
219
  tools=tools,
344
220
  tool_choice=tool_choice or self._tool_choice,
221
+ run_response=run_response,
345
222
  )
346
223
 
347
224
  # Add assistant message to messages
@@ -369,6 +246,26 @@ class Model(ABC):
369
246
  function_call_limit=tool_call_limit,
370
247
  ):
371
248
  if isinstance(function_call_response, ModelResponse):
249
+ # The session state is updated by the function call
250
+ if function_call_response.updated_session_state is not None:
251
+ model_response.updated_session_state = function_call_response.updated_session_state
252
+
253
+ # Media artifacts are generated by the function call
254
+ if function_call_response.images is not None:
255
+ if model_response.images is None:
256
+ model_response.images = []
257
+ model_response.images.extend(function_call_response.images)
258
+
259
+ if function_call_response.audios is not None:
260
+ if model_response.audios is None:
261
+ model_response.audios = []
262
+ model_response.audios.extend(function_call_response.audios)
263
+
264
+ if function_call_response.videos is not None:
265
+ if model_response.videos is None:
266
+ model_response.videos = []
267
+ model_response.videos.extend(function_call_response.videos)
268
+
372
269
  if (
373
270
  function_call_response.event
374
271
  in [
@@ -395,6 +292,11 @@ class Model(ABC):
395
292
  self.format_function_call_results(
396
293
  messages=messages, function_call_results=function_call_results, **model_response.extra or {}
397
294
  )
295
+
296
+ if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
297
+ # Handle function call media
298
+ self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
299
+
398
300
  for function_call_result in function_call_results:
399
301
  function_call_result.log(metrics=True)
400
302
 
@@ -480,6 +382,26 @@ class Model(ABC):
480
382
  function_call_limit=tool_call_limit,
481
383
  ):
482
384
  if isinstance(function_call_response, ModelResponse):
385
+ # The session state is updated by the function call
386
+ if function_call_response.updated_session_state is not None:
387
+ model_response.updated_session_state = function_call_response.updated_session_state
388
+
389
+ # Media artifacts are generated by the function call
390
+ if function_call_response.images is not None:
391
+ if model_response.images is None:
392
+ model_response.images = []
393
+ model_response.images.extend(function_call_response.images)
394
+
395
+ if function_call_response.audios is not None:
396
+ if model_response.audios is None:
397
+ model_response.audios = []
398
+ model_response.audios.extend(function_call_response.audios)
399
+
400
+ if function_call_response.videos is not None:
401
+ if model_response.videos is None:
402
+ model_response.videos = []
403
+ model_response.videos.extend(function_call_response.videos)
404
+
483
405
  if (
484
406
  function_call_response.event
485
407
  in [
@@ -505,6 +427,11 @@ class Model(ABC):
505
427
  self.format_function_call_results(
506
428
  messages=messages, function_call_results=function_call_results, **model_response.extra or {}
507
429
  )
430
+
431
+ if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
432
+ # Handle function call media
433
+ self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
434
+
508
435
  for function_call_result in function_call_results:
509
436
  function_call_result.log(metrics=True)
510
437
 
@@ -541,6 +468,7 @@ class Model(ABC):
541
468
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
542
469
  tools: Optional[List[Dict[str, Any]]] = None,
543
470
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
471
+ run_response: Optional[RunOutput] = None,
544
472
  ) -> None:
545
473
  """
546
474
  Process a single model response and return the assistant message and whether to continue.
@@ -549,21 +477,14 @@ class Model(ABC):
549
477
  Tuple[Message, bool]: (assistant_message, should_continue)
550
478
  """
551
479
  # Generate response
552
- assistant_message.metrics.start_timer()
553
- response = self.invoke(
480
+ provider_response = self.invoke(
481
+ assistant_message=assistant_message,
554
482
  messages=messages,
555
483
  response_format=response_format,
556
484
  tools=tools,
557
485
  tool_choice=tool_choice or self._tool_choice,
486
+ run_response=run_response,
558
487
  )
559
- assistant_message.metrics.stop_timer()
560
-
561
- # Parse provider response
562
- provider_response: ModelResponse = self.parse_provider_response(response, response_format=response_format)
563
-
564
- # Add parsed data to model response
565
- if provider_response.parsed is not None:
566
- model_response.parsed = provider_response.parsed
567
488
 
568
489
  # Populate the assistant message
569
490
  self._populate_assistant_message(assistant_message=assistant_message, provider_response=provider_response)
@@ -574,16 +495,21 @@ class Model(ABC):
574
495
  model_response.content = assistant_message.get_content_string()
575
496
  else:
576
497
  model_response.content += assistant_message.get_content_string()
577
- if assistant_message.thinking is not None:
578
- model_response.thinking = assistant_message.thinking
579
- if assistant_message.redacted_thinking is not None:
580
- model_response.redacted_thinking = assistant_message.redacted_thinking
498
+ if assistant_message.reasoning_content is not None:
499
+ model_response.reasoning_content = assistant_message.reasoning_content
500
+ if assistant_message.redacted_reasoning_content is not None:
501
+ model_response.redacted_reasoning_content = assistant_message.redacted_reasoning_content
581
502
  if assistant_message.citations is not None:
582
503
  model_response.citations = assistant_message.citations
583
504
  if assistant_message.audio_output is not None:
584
- model_response.audio = assistant_message.audio_output
505
+ if isinstance(assistant_message.audio_output, AudioArtifact):
506
+ model_response.audios = [assistant_message.audio_output]
507
+ elif isinstance(assistant_message.audio_output, AudioResponse):
508
+ model_response.audio = assistant_message.audio_output
585
509
  if assistant_message.image_output is not None:
586
- model_response.image = assistant_message.image_output
510
+ model_response.images = [assistant_message.image_output]
511
+ if assistant_message.video_output is not None:
512
+ model_response.videos = [assistant_message.video_output]
587
513
  if provider_response.extra is not None:
588
514
  if model_response.extra is None:
589
515
  model_response.extra = {}
@@ -597,6 +523,7 @@ class Model(ABC):
597
523
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
598
524
  tools: Optional[List[Dict[str, Any]]] = None,
599
525
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
526
+ run_response: Optional[RunOutput] = None,
600
527
  ) -> None:
601
528
  """
602
529
  Process a single async model response and return the assistant message and whether to continue.
@@ -605,21 +532,14 @@ class Model(ABC):
605
532
  Tuple[Message, bool]: (assistant_message, should_continue)
606
533
  """
607
534
  # Generate response
608
- assistant_message.metrics.start_timer()
609
- response = await self.ainvoke(
535
+ provider_response = await self.ainvoke(
610
536
  messages=messages,
611
537
  response_format=response_format,
612
538
  tools=tools,
613
539
  tool_choice=tool_choice or self._tool_choice,
540
+ assistant_message=assistant_message,
541
+ run_response=run_response,
614
542
  )
615
- assistant_message.metrics.stop_timer()
616
-
617
- # Parse provider response
618
- provider_response: ModelResponse = self.parse_provider_response(response, response_format=response_format)
619
-
620
- # Add parsed data to model response
621
- if provider_response.parsed is not None:
622
- model_response.parsed = provider_response.parsed
623
543
 
624
544
  # Populate the assistant message
625
545
  self._populate_assistant_message(assistant_message=assistant_message, provider_response=provider_response)
@@ -630,16 +550,21 @@ class Model(ABC):
630
550
  model_response.content = assistant_message.get_content_string()
631
551
  else:
632
552
  model_response.content += assistant_message.get_content_string()
633
- if assistant_message.thinking is not None:
634
- model_response.thinking = assistant_message.thinking
635
- if assistant_message.redacted_thinking is not None:
636
- model_response.redacted_thinking = assistant_message.redacted_thinking
553
+ if assistant_message.reasoning_content is not None:
554
+ model_response.reasoning_content = assistant_message.reasoning_content
555
+ if assistant_message.redacted_reasoning_content is not None:
556
+ model_response.redacted_reasoning_content = assistant_message.redacted_reasoning_content
637
557
  if assistant_message.citations is not None:
638
558
  model_response.citations = assistant_message.citations
639
559
  if assistant_message.audio_output is not None:
640
- model_response.audio = assistant_message.audio_output
560
+ if isinstance(assistant_message.audio_output, AudioArtifact):
561
+ model_response.audios = [assistant_message.audio_output]
562
+ elif isinstance(assistant_message.audio_output, AudioResponse):
563
+ model_response.audio = assistant_message.audio_output
641
564
  if assistant_message.image_output is not None:
642
- model_response.image = assistant_message.image_output
565
+ model_response.images = [assistant_message.image_output]
566
+ if assistant_message.video_output is not None:
567
+ model_response.videos = [assistant_message.video_output]
643
568
  if provider_response.extra is not None:
644
569
  if model_response.extra is None:
645
570
  model_response.extra = {}
@@ -677,16 +602,22 @@ class Model(ABC):
677
602
  assistant_message.audio_output = provider_response.audio
678
603
 
679
604
  # Add image to assistant message
680
- if provider_response.image is not None:
681
- assistant_message.image_output = provider_response.image
605
+ if provider_response.images is not None:
606
+ if provider_response.images:
607
+ assistant_message.image_output = provider_response.images[-1] # Taking last (most recent) image
682
608
 
683
- # Add thinking content to assistant message
684
- if provider_response.thinking is not None:
685
- assistant_message.thinking = provider_response.thinking
609
+ # Add video to assistant message
610
+ if provider_response.videos is not None:
611
+ if provider_response.videos:
612
+ assistant_message.video_output = provider_response.videos[-1] # Taking last (most recent) video
613
+
614
+ if provider_response.audios is not None:
615
+ if provider_response.audios:
616
+ assistant_message.audio_output = provider_response.audios[-1] # Taking last (most recent) audio
686
617
 
687
618
  # Add redacted thinking content to assistant message
688
- if provider_response.redacted_thinking is not None:
689
- assistant_message.redacted_thinking = provider_response.redacted_thinking
619
+ if provider_response.redacted_reasoning_content is not None:
620
+ assistant_message.redacted_reasoning_content = provider_response.redacted_reasoning_content
690
621
 
691
622
  # Add reasoning content to assistant message
692
623
  if provider_response.reasoning_content is not None:
@@ -702,9 +633,7 @@ class Model(ABC):
702
633
 
703
634
  # Add usage metrics if provided
704
635
  if provider_response.response_usage is not None:
705
- _add_usage_metrics_to_assistant_message(
706
- assistant_message=assistant_message, response_usage=provider_response.response_usage
707
- )
636
+ assistant_message.metrics += provider_response.response_usage
708
637
 
709
638
  return assistant_message
710
639
 
@@ -716,22 +645,28 @@ class Model(ABC):
716
645
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
717
646
  tools: Optional[List[Dict[str, Any]]] = None,
718
647
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
648
+ run_response: Optional[RunOutput] = None,
719
649
  ) -> Iterator[ModelResponse]:
720
650
  """
721
651
  Process a streaming response from the model.
722
652
  """
723
- assistant_message.metrics.start_timer()
653
+
724
654
  for response_delta in self.invoke_stream(
725
655
  messages=messages,
656
+ assistant_message=assistant_message,
726
657
  response_format=response_format,
727
658
  tools=tools,
728
659
  tool_choice=tool_choice or self._tool_choice,
660
+ run_response=run_response,
729
661
  ):
730
- model_response_delta = self.parse_provider_response_delta(response_delta)
731
662
  yield from self._populate_stream_data_and_assistant_message(
732
- stream_data=stream_data, assistant_message=assistant_message, model_response_delta=model_response_delta
663
+ stream_data=stream_data,
664
+ assistant_message=assistant_message,
665
+ model_response_delta=response_delta,
733
666
  )
734
- assistant_message.metrics.stop_timer()
667
+
668
+ # Add final metrics to assistant message
669
+ self._populate_assistant_message(assistant_message=assistant_message, provider_response=response_delta)
735
670
 
736
671
  def response_stream(
737
672
  self,
@@ -742,7 +677,8 @@ class Model(ABC):
742
677
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
743
678
  tool_call_limit: Optional[int] = None,
744
679
  stream_model_response: bool = True,
745
- ) -> Iterator[Union[ModelResponse, RunResponseEvent, TeamRunResponseEvent]]:
680
+ run_response: Optional[RunOutput] = None,
681
+ ) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
746
682
  """
747
683
  Generate a streaming response from the model.
748
684
  """
@@ -766,15 +702,16 @@ class Model(ABC):
766
702
  response_format=response_format,
767
703
  tools=tools,
768
704
  tool_choice=tool_choice or self._tool_choice,
705
+ run_response=run_response,
769
706
  )
770
707
 
771
708
  # Populate assistant message from stream data
772
709
  if stream_data.response_content:
773
710
  assistant_message.content = stream_data.response_content
774
- if stream_data.response_thinking:
775
- assistant_message.thinking = stream_data.response_thinking
776
- if stream_data.response_redacted_thinking:
777
- assistant_message.redacted_thinking = stream_data.response_redacted_thinking
711
+ if stream_data.response_reasoning_content:
712
+ assistant_message.reasoning_content = stream_data.response_reasoning_content
713
+ if stream_data.response_redacted_reasoning_content:
714
+ assistant_message.redacted_reasoning_content = stream_data.response_redacted_reasoning_content
778
715
  if stream_data.response_provider_data:
779
716
  assistant_message.provider_data = stream_data.response_provider_data
780
717
  if stream_data.response_citations:
@@ -828,6 +765,10 @@ class Model(ABC):
828
765
  else:
829
766
  self.format_function_call_results(messages=messages, function_call_results=function_call_results)
830
767
 
768
+ # Handle function call media
769
+ if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
770
+ self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
771
+
831
772
  for function_call_result in function_call_results:
832
773
  function_call_result.log(metrics=True)
833
774
 
@@ -863,23 +804,28 @@ class Model(ABC):
863
804
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
864
805
  tools: Optional[List[Dict[str, Any]]] = None,
865
806
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
807
+ run_response: Optional[RunOutput] = None,
866
808
  ) -> AsyncIterator[ModelResponse]:
867
809
  """
868
810
  Process a streaming response from the model.
869
811
  """
870
- assistant_message.metrics.start_timer()
871
812
  async for response_delta in self.ainvoke_stream(
872
813
  messages=messages,
814
+ assistant_message=assistant_message,
873
815
  response_format=response_format,
874
816
  tools=tools,
875
817
  tool_choice=tool_choice or self._tool_choice,
818
+ run_response=run_response,
876
819
  ): # type: ignore
877
- model_response_delta = self.parse_provider_response_delta(response_delta)
878
820
  for model_response in self._populate_stream_data_and_assistant_message(
879
- stream_data=stream_data, assistant_message=assistant_message, model_response_delta=model_response_delta
821
+ stream_data=stream_data,
822
+ assistant_message=assistant_message,
823
+ model_response_delta=response_delta,
880
824
  ):
881
825
  yield model_response
882
- assistant_message.metrics.stop_timer()
826
+
827
+ # Populate the assistant message
828
+ self._populate_assistant_message(assistant_message=assistant_message, provider_response=model_response)
883
829
 
884
830
  async def aresponse_stream(
885
831
  self,
@@ -890,7 +836,8 @@ class Model(ABC):
890
836
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
891
837
  tool_call_limit: Optional[int] = None,
892
838
  stream_model_response: bool = True,
893
- ) -> AsyncIterator[Union[ModelResponse, RunResponseEvent, TeamRunResponseEvent]]:
839
+ run_response: Optional[RunOutput] = None,
840
+ ) -> AsyncIterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
894
841
  """
895
842
  Generate an asynchronous streaming response from the model.
896
843
  """
@@ -914,16 +861,17 @@ class Model(ABC):
914
861
  response_format=response_format,
915
862
  tools=tools,
916
863
  tool_choice=tool_choice or self._tool_choice,
864
+ run_response=run_response,
917
865
  ):
918
866
  yield response
919
867
 
920
868
  # Populate assistant message from stream data
921
869
  if stream_data.response_content:
922
870
  assistant_message.content = stream_data.response_content
923
- if stream_data.response_thinking:
924
- assistant_message.thinking = stream_data.response_thinking
925
- if stream_data.response_redacted_thinking:
926
- assistant_message.redacted_thinking = stream_data.response_redacted_thinking
871
+ if stream_data.response_reasoning_content:
872
+ assistant_message.reasoning_content = stream_data.response_reasoning_content
873
+ if stream_data.response_redacted_reasoning_content:
874
+ assistant_message.redacted_reasoning_content = stream_data.response_redacted_reasoning_content
927
875
  if stream_data.response_provider_data:
928
876
  assistant_message.provider_data = stream_data.response_provider_data
929
877
  if stream_data.response_audio:
@@ -940,6 +888,7 @@ class Model(ABC):
940
888
  response_format=response_format,
941
889
  tools=tools,
942
890
  tool_choice=tool_choice or self._tool_choice,
891
+ run_response=run_response,
943
892
  )
944
893
  yield model_response
945
894
 
@@ -975,6 +924,10 @@ class Model(ABC):
975
924
  else:
976
925
  self.format_function_call_results(messages=messages, function_call_results=function_call_results)
977
926
 
927
+ # Handle function call media
928
+ if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
929
+ self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
930
+
978
931
  for function_call_result in function_call_results:
979
932
  function_call_result.log(metrics=True)
980
933
 
@@ -1006,11 +959,6 @@ class Model(ABC):
1006
959
  self, stream_data: MessageData, assistant_message: Message, model_response_delta: ModelResponse
1007
960
  ) -> Iterator[ModelResponse]:
1008
961
  """Update the stream data and assistant message with the model response."""
1009
-
1010
- # Update metrics
1011
- if not assistant_message.metrics.time_to_first_token:
1012
- assistant_message.metrics.set_time_to_first_token()
1013
-
1014
962
  # Add role to assistant message
1015
963
  if model_response_delta.role is not None:
1016
964
  assistant_message.role = model_response_delta.role
@@ -1021,16 +969,12 @@ class Model(ABC):
1021
969
  stream_data.response_content += model_response_delta.content
1022
970
  should_yield = True
1023
971
 
1024
- if model_response_delta.thinking is not None:
1025
- stream_data.response_thinking += model_response_delta.thinking
1026
- should_yield = True
1027
-
1028
972
  if model_response_delta.reasoning_content is not None:
1029
- stream_data.response_thinking += model_response_delta.reasoning_content
973
+ stream_data.response_reasoning_content += model_response_delta.reasoning_content
1030
974
  should_yield = True
1031
975
 
1032
- if model_response_delta.redacted_thinking is not None:
1033
- stream_data.response_redacted_thinking += model_response_delta.redacted_thinking
976
+ if model_response_delta.redacted_reasoning_content is not None:
977
+ stream_data.response_redacted_reasoning_content += model_response_delta.redacted_reasoning_content
1034
978
  should_yield = True
1035
979
 
1036
980
  if model_response_delta.citations is not None:
@@ -1049,40 +993,45 @@ class Model(ABC):
1049
993
  stream_data.response_tool_calls.extend(model_response_delta.tool_calls)
1050
994
  should_yield = True
1051
995
 
1052
- if model_response_delta.audio is not None:
996
+ if model_response_delta.audio is not None and isinstance(model_response_delta.audio, AudioResponse):
1053
997
  if stream_data.response_audio is None:
1054
998
  stream_data.response_audio = AudioResponse(id=str(uuid4()), content="", transcript="")
1055
999
 
1000
+ from typing import cast
1001
+
1002
+ audio_response = cast(AudioResponse, model_response_delta.audio)
1003
+
1056
1004
  # Update the stream data with audio information
1057
- if model_response_delta.audio.id is not None:
1058
- stream_data.response_audio.id = model_response_delta.audio.id # type: ignore
1059
- if model_response_delta.audio.content is not None:
1060
- stream_data.response_audio.content += model_response_delta.audio.content # type: ignore
1061
- if model_response_delta.audio.transcript is not None:
1062
- stream_data.response_audio.transcript += model_response_delta.audio.transcript # type: ignore
1063
- if model_response_delta.audio.expires_at is not None:
1064
- stream_data.response_audio.expires_at = model_response_delta.audio.expires_at
1065
- if model_response_delta.audio.mime_type is not None:
1066
- stream_data.response_audio.mime_type = model_response_delta.audio.mime_type
1067
- stream_data.response_audio.sample_rate = model_response_delta.audio.sample_rate
1068
- stream_data.response_audio.channels = model_response_delta.audio.channels
1005
+ if audio_response.id is not None:
1006
+ stream_data.response_audio.id = audio_response.id # type: ignore
1007
+ if audio_response.content is not None:
1008
+ stream_data.response_audio.content += audio_response.content # type: ignore
1009
+ if audio_response.transcript is not None:
1010
+ stream_data.response_audio.transcript += audio_response.transcript # type: ignore
1011
+ if audio_response.expires_at is not None:
1012
+ stream_data.response_audio.expires_at = audio_response.expires_at
1013
+ if audio_response.mime_type is not None:
1014
+ stream_data.response_audio.mime_type = audio_response.mime_type
1015
+ stream_data.response_audio.sample_rate = audio_response.sample_rate
1016
+ stream_data.response_audio.channels = audio_response.channels
1069
1017
 
1070
1018
  should_yield = True
1071
1019
 
1072
- if model_response_delta.image:
1020
+ if model_response_delta.images:
1073
1021
  if stream_data.response_image is None:
1074
- stream_data.response_image = model_response_delta.image
1022
+ stream_data.response_image = model_response_delta.images[-1]
1023
+ should_yield = True
1024
+
1025
+ if model_response_delta.videos:
1026
+ if stream_data.response_video is None:
1027
+ stream_data.response_video = model_response_delta.videos[-1]
1028
+ should_yield = True
1075
1029
 
1076
1030
  if model_response_delta.extra is not None:
1077
1031
  if stream_data.extra is None:
1078
1032
  stream_data.extra = {}
1079
1033
  stream_data.extra.update(model_response_delta.extra)
1080
1034
 
1081
- if model_response_delta.response_usage is not None:
1082
- _add_usage_metrics_to_assistant_message(
1083
- assistant_message=assistant_message, response_usage=model_response_delta.response_usage
1084
- )
1085
-
1086
1035
  if should_yield:
1087
1036
  yield model_response_delta
1088
1037
 
@@ -1142,11 +1091,55 @@ class Model(ABC):
1142
1091
  success: bool,
1143
1092
  output: Optional[Union[List[Any], str]] = None,
1144
1093
  timer: Optional[Timer] = None,
1094
+ function_execution_result: Optional[FunctionExecutionResult] = None,
1145
1095
  ) -> Message:
1146
1096
  """Create a function call result message."""
1147
1097
  kwargs = {}
1148
1098
  if timer is not None:
1149
- kwargs["metrics"] = MessageMetrics(time=timer.elapsed)
1099
+ kwargs["metrics"] = Metrics(duration=timer.elapsed)
1100
+
1101
+ # Include media artifacts from function execution result in the tool message
1102
+ images = None
1103
+ videos = None
1104
+ audios = None
1105
+
1106
+ if success and function_execution_result:
1107
+ # Convert ImageArtifacts to Images for message compatibility
1108
+ if function_execution_result.images:
1109
+ from agno.media import Image
1110
+
1111
+ images = []
1112
+ for img_artifact in function_execution_result.images:
1113
+ if img_artifact.url:
1114
+ images.append(Image(url=img_artifact.url))
1115
+ elif img_artifact.content:
1116
+ images.append(Image(content=img_artifact.content))
1117
+
1118
+ # Convert VideoArtifacts to Videos for message compatibility
1119
+ if function_execution_result.videos:
1120
+ from agno.media import Video
1121
+
1122
+ videos = []
1123
+ for vid_artifact in function_execution_result.videos:
1124
+ if vid_artifact.url:
1125
+ videos.append(Video(url=vid_artifact.url))
1126
+ elif vid_artifact.content:
1127
+ videos.append(Video(content=vid_artifact.content))
1128
+
1129
+ # Convert AudioArtifacts to Audio for message compatibility
1130
+ if function_execution_result.audios:
1131
+ from agno.media import Audio
1132
+
1133
+ audios = []
1134
+ for aud_artifact in function_execution_result.audios:
1135
+ if aud_artifact.url:
1136
+ audios.append(Audio(url=aud_artifact.url))
1137
+ elif aud_artifact.base64_audio:
1138
+ import base64
1139
+
1140
+ audio_bytes = base64.b64decode(aud_artifact.base64_audio)
1141
+ audios.append(Audio(content=audio_bytes))
1142
+
1150
1143
  return Message(
1151
1144
  role=self.tool_message_role,
1152
1145
  content=output if success else function_call.error,
@@ -1155,7 +1148,10 @@ class Model(ABC):
1155
1148
  tool_args=function_call.arguments,
1156
1149
  tool_call_error=not success,
1157
1150
  stop_after_tool_call=function_call.function.stop_after_tool_call,
1158
- **kwargs,
1151
+ images=images,
1152
+ videos=videos,
1153
+ audio=audios,
1154
+ **kwargs, # type: ignore
1159
1155
  )
1160
1156
 
1161
1157
  def create_tool_call_limit_error_result(self, function_call: FunctionCall) -> Message:
@@ -1172,8 +1168,8 @@ class Model(ABC):
1172
1168
  self,
1173
1169
  function_call: FunctionCall,
1174
1170
  function_call_results: List[Message],
1175
- additional_messages: Optional[List[Message]] = None,
1176
- ) -> Iterator[Union[ModelResponse, RunResponseEvent, TeamRunResponseEvent]]:
1171
+ additional_input: Optional[List[Message]] = None,
1172
+ ) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
1177
1173
  # Start function call
1178
1174
  function_call_timer = Timer()
1179
1175
  function_call_timer.start()
@@ -1196,7 +1192,7 @@ class Model(ABC):
1196
1192
  function_execution_result = function_call.execute()
1197
1193
  except AgentRunException as a_exc:
1198
1194
  # Update additional messages from function call
1199
- _handle_agent_exception(a_exc, additional_messages)
1195
+ _handle_agent_exception(a_exc, additional_input)
1200
1196
  # Set function call success to False if an exception occurred
1201
1197
  except Exception as e:
1202
1198
  log_error(f"Error executing function {function_call.function.name}: {e}")
@@ -1210,14 +1206,14 @@ class Model(ABC):
1210
1206
  # Process function call output
1211
1207
  function_call_output: str = ""
1212
1208
 
1213
- if isinstance(function_call.result, (GeneratorType, collections.abc.Iterator)):
1214
- for item in function_call.result:
1209
+ if isinstance(function_execution_result.result, (GeneratorType, collections.abc.Iterator)):
1210
+ for item in function_execution_result.result:
1215
1211
  # This function yields agent/team run events
1216
- if isinstance(item, tuple(get_args(RunResponseEvent))) or isinstance(
1217
- item, tuple(get_args(TeamRunResponseEvent))
1212
+ if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
1213
+ item, tuple(get_args(TeamRunOutputEvent))
1218
1214
  ):
1219
1215
  # We only capture content events
1220
- if isinstance(item, RunResponseContentEvent) or isinstance(item, TeamRunResponseContentEvent):
1216
+ if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
1221
1217
  if item.content is not None and isinstance(item.content, BaseModel):
1222
1218
  function_call_output += item.content.model_dump_json()
1223
1219
  else:
@@ -1235,13 +1231,33 @@ class Model(ABC):
1235
1231
  if function_call.function.show_result:
1236
1232
  yield ModelResponse(content=str(item))
1237
1233
  else:
1238
- function_call_output = str(function_call.result)
1234
+ from agno.tools.function import ToolResult
1235
+
1236
+ if isinstance(function_execution_result.result, ToolResult):
1237
+ # Extract content and media from ToolResult
1238
+ tool_result = function_execution_result.result
1239
+ function_call_output = tool_result.content
1240
+
1241
+ # Transfer media from ToolResult to FunctionExecutionResult
1242
+ if tool_result.images:
1243
+ function_execution_result.images = tool_result.images
1244
+ if tool_result.videos:
1245
+ function_execution_result.videos = tool_result.videos
1246
+ if tool_result.audios:
1247
+ function_execution_result.audios = tool_result.audios
1248
+ else:
1249
+ function_call_output = str(function_execution_result.result) if function_execution_result.result else ""
1250
+
1239
1251
  if function_call.function.show_result:
1240
1252
  yield ModelResponse(content=function_call_output)
1241
1253
 
1242
1254
  # Create and yield function call result
1243
1255
  function_call_result = self.create_function_call_result(
1244
- function_call, success=function_call_success, output=function_call_output, timer=function_call_timer
1256
+ function_call,
1257
+ success=function_call_success,
1258
+ output=function_call_output,
1259
+ timer=function_call_timer,
1260
+ function_execution_result=function_execution_result,
1245
1261
  )
1246
1262
  yield ModelResponse(
1247
1263
  content=f"{function_call.get_call_str()} completed in {function_call_timer.elapsed:.4f}s.",
@@ -1257,6 +1273,11 @@ class Model(ABC):
1257
1273
  )
1258
1274
  ],
1259
1275
  event=ModelResponseEvent.tool_call_completed.value,
1276
+ updated_session_state=function_execution_result.updated_session_state,
1277
+ # Add media artifacts from function execution
1278
+ images=function_execution_result.images,
1279
+ videos=function_execution_result.videos,
1280
+ audios=function_execution_result.audios,
1260
1281
  )
1261
1282
 
1262
1283
  # Add function call to function call results
@@ -1266,13 +1287,13 @@ class Model(ABC):
1266
1287
  self,
1267
1288
  function_calls: List[FunctionCall],
1268
1289
  function_call_results: List[Message],
1269
- additional_messages: Optional[List[Message]] = None,
1290
+ additional_input: Optional[List[Message]] = None,
1270
1291
  current_function_call_count: int = 0,
1271
1292
  function_call_limit: Optional[int] = None,
1272
- ) -> Iterator[Union[ModelResponse, RunResponseEvent, TeamRunResponseEvent]]:
1293
+ ) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
1273
1294
  # Additional messages from function calls that will be added to the function call results
1274
- if additional_messages is None:
1275
- additional_messages = []
1295
+ if additional_input is None:
1296
+ additional_input = []
1276
1297
 
1277
1298
  for fc in function_calls:
1278
1299
  if function_call_limit is not None:
@@ -1358,17 +1379,17 @@ class Model(ABC):
1358
1379
  continue
1359
1380
 
1360
1381
  yield from self.run_function_call(
1361
- function_call=fc, function_call_results=function_call_results, additional_messages=additional_messages
1382
+ function_call=fc, function_call_results=function_call_results, additional_input=additional_input
1362
1383
  )
1363
1384
 
1364
1385
  # Add any additional messages at the end
1365
- if additional_messages:
1366
- function_call_results.extend(additional_messages)
1386
+ if additional_input:
1387
+ function_call_results.extend(additional_input)
1367
1388
 
1368
1389
  async def arun_function_call(
1369
1390
  self,
1370
1391
  function_call: FunctionCall,
1371
- ) -> Tuple[Union[bool, AgentRunException], Timer, FunctionCall]:
1392
+ ) -> Tuple[Union[bool, AgentRunException], Timer, FunctionCall, Optional[Dict[str, Any]]]:
1372
1393
  """Run a single function call and return its success status, timer, and the FunctionCall object."""
1373
1394
  from inspect import isasyncgenfunction, iscoroutine, iscoroutinefunction
1374
1395
 
@@ -1402,20 +1423,20 @@ class Model(ABC):
1402
1423
  raise e
1403
1424
 
1404
1425
  function_call_timer.stop()
1405
- return success, function_call_timer, function_call
1426
+ return success, function_call_timer, function_call, result.updated_session_state
1406
1427
 
1407
1428
  async def arun_function_calls(
1408
1429
  self,
1409
1430
  function_calls: List[FunctionCall],
1410
1431
  function_call_results: List[Message],
1411
- additional_messages: Optional[List[Message]] = None,
1432
+ additional_input: Optional[List[Message]] = None,
1412
1433
  current_function_call_count: int = 0,
1413
1434
  function_call_limit: Optional[int] = None,
1414
1435
  skip_pause_check: bool = False,
1415
- ) -> AsyncIterator[Union[ModelResponse, RunResponseEvent, TeamRunResponseEvent]]:
1436
+ ) -> AsyncIterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
1416
1437
  # Additional messages from function calls that will be added to the function call results
1417
- if additional_messages is None:
1418
- additional_messages = []
1438
+ if additional_input is None:
1439
+ additional_input = []
1419
1440
 
1420
1441
  function_calls_to_run = []
1421
1442
  for fc in function_calls:
@@ -1548,33 +1569,33 @@ class Model(ABC):
1548
1569
  raise result
1549
1570
 
1550
1571
  # Unpack result
1551
- function_call_success, function_call_timer, fc = result
1572
+ function_call_success, function_call_timer, function_call, updated_session_state = result
1552
1573
 
1553
1574
  # Handle AgentRunException
1554
1575
  if isinstance(function_call_success, AgentRunException):
1555
1576
  a_exc = function_call_success
1556
1577
  # Update additional messages from function call
1557
- _handle_agent_exception(a_exc, additional_messages)
1578
+ _handle_agent_exception(a_exc, additional_input)
1558
1579
  # Set function call success to False if an exception occurred
1559
1580
  function_call_success = False
1560
1581
 
1561
1582
  # Process function call output
1562
1583
  function_call_output: str = ""
1563
- if isinstance(fc.result, (GeneratorType, collections.abc.Iterator)):
1564
- for item in fc.result:
1584
+ if isinstance(function_call.result, (GeneratorType, collections.abc.Iterator)):
1585
+ for item in function_call.result:
1565
1586
  # This function yields agent/team run events
1566
- if isinstance(item, tuple(get_args(RunResponseEvent))) or isinstance(
1567
- item, tuple(get_args(TeamRunResponseEvent))
1587
+ if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
1588
+ item, tuple(get_args(TeamRunOutputEvent))
1568
1589
  ):
1569
1590
  # We only capture content events
1570
- if isinstance(item, RunResponseContentEvent) or isinstance(item, TeamRunResponseContentEvent):
1591
+ if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
1571
1592
  if item.content is not None and isinstance(item.content, BaseModel):
1572
1593
  function_call_output += item.content.model_dump_json()
1573
1594
  else:
1574
1595
  # Capture output
1575
1596
  function_call_output += item.content or ""
1576
1597
 
1577
- if fc.function.show_result:
1598
+ if function_call.function.show_result:
1578
1599
  yield ModelResponse(content=item.content)
1579
1600
  continue
1580
1601
 
@@ -1582,23 +1603,23 @@ class Model(ABC):
1582
1603
  yield item
1583
1604
  else:
1584
1605
  function_call_output += str(item)
1585
- if fc.function.show_result:
1606
+ if function_call.function.show_result:
1586
1607
  yield ModelResponse(content=str(item))
1587
- elif isinstance(fc.result, (AsyncGeneratorType, collections.abc.AsyncIterator)):
1588
- async for item in fc.result:
1608
+ elif isinstance(function_call.result, (AsyncGeneratorType, collections.abc.AsyncIterator)):
1609
+ async for item in function_call.result:
1589
1610
  # This function yields agent/team run events
1590
- if isinstance(item, tuple(get_args(RunResponseEvent))) or isinstance(
1591
- item, tuple(get_args(TeamRunResponseEvent))
1611
+ if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
1612
+ item, tuple(get_args(TeamRunOutputEvent))
1592
1613
  ):
1593
1614
  # We only capture content events
1594
- if isinstance(item, RunResponseContentEvent) or isinstance(item, TeamRunResponseContentEvent):
1615
+ if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
1595
1616
  if item.content is not None and isinstance(item.content, BaseModel):
1596
1617
  function_call_output += item.content.model_dump_json()
1597
1618
  else:
1598
1619
  # Capture output
1599
1620
  function_call_output += item.content or ""
1600
1621
 
1601
- if fc.function.show_result:
1622
+ if function_call.function.show_result:
1602
1623
  yield ModelResponse(content=item.content)
1603
1624
  continue
1604
1625
 
@@ -1606,19 +1627,19 @@ class Model(ABC):
1606
1627
  yield item
1607
1628
  else:
1608
1629
  function_call_output += str(item)
1609
- if fc.function.show_result:
1630
+ if function_call.function.show_result:
1610
1631
  yield ModelResponse(content=str(item))
1611
1632
  else:
1612
- function_call_output = str(fc.result)
1613
- if fc.function.show_result:
1633
+ function_call_output = str(function_call.result)
1634
+ if function_call.function.show_result:
1614
1635
  yield ModelResponse(content=function_call_output)
1615
1636
 
1616
1637
  # Create and yield function call result
1617
1638
  function_call_result = self.create_function_call_result(
1618
- fc, success=function_call_success, output=function_call_output, timer=function_call_timer
1639
+ function_call, success=function_call_success, output=function_call_output, timer=function_call_timer
1619
1640
  )
1620
1641
  yield ModelResponse(
1621
- content=f"{fc.get_call_str()} completed in {function_call_timer.elapsed:.4f}s.",
1642
+ content=f"{function_call.get_call_str()} completed in {function_call_timer.elapsed:.4f}s.",
1622
1643
  tool_executions=[
1623
1644
  ToolExecution(
1624
1645
  tool_call_id=function_call_result.tool_call_id,
@@ -1631,14 +1652,15 @@ class Model(ABC):
1631
1652
  )
1632
1653
  ],
1633
1654
  event=ModelResponseEvent.tool_call_completed.value,
1655
+ updated_session_state=updated_session_state,
1634
1656
  )
1635
1657
 
1636
1658
  # Add function call result to function call results
1637
1659
  function_call_results.append(function_call_result)
1638
1660
 
1639
1661
  # Add any additional messages at the end
1640
- if additional_messages:
1641
- function_call_results.extend(additional_messages)
1662
+ if additional_input:
1663
+ function_call_results.extend(additional_input)
1642
1664
 
1643
1665
  def _prepare_function_calls(
1644
1666
  self,
@@ -1669,6 +1691,44 @@ class Model(ABC):
1669
1691
  if len(function_call_results) > 0:
1670
1692
  messages.extend(function_call_results)
1671
1693
 
1694
+ def _handle_function_call_media(self, messages: List[Message], function_call_results: List[Message]) -> None:
1695
+ """
1696
+ Handle media artifacts from function calls by adding follow-up user messages for generated media if needed.
1697
+ """
1698
+ if not function_call_results:
1699
+ return
1700
+
1701
+ # Collect all media artifacts from function calls
1702
+ all_images: List[Image] = []
1703
+ all_videos: List[Video] = []
1704
+ all_audio: List[Audio] = []
1705
+
1706
+ for result_message in function_call_results:
1707
+ if result_message.images:
1708
+ all_images.extend(result_message.images)
1709
+ # Remove images from tool message to avoid errors on the LLMs
1710
+ result_message.images = None
1711
+
1712
+ if result_message.videos:
1713
+ all_videos.extend(result_message.videos)
1714
+ result_message.videos = None
1715
+
1716
+ if result_message.audio:
1717
+ all_audio.extend(result_message.audio)
1718
+ result_message.audio = None
1719
+
1720
+ # If we have media artifacts, add a follow-up "user" message instead of a "tool"
1721
+ # message with the media artifacts which throws error for some models
1722
+ if all_images or all_videos or all_audio:
1723
+ media_message = Message(
1724
+ role="user",
1725
+ content="Take note of the following content",
1726
+ images=all_images if all_images else None,
1727
+ videos=all_videos if all_videos else None,
1728
+ audio=all_audio if all_audio else None,
1729
+ )
1730
+ messages.append(media_message)
1731
+
1672
1732
  def get_system_message_for_model(self, tools: Optional[List[Any]] = None) -> Optional[str]:
1673
1733
  return self.system_prompt
1674
1734