agno 1.8.2__py3-none-any.whl → 2.0.0__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 (589) hide show
  1. agno/agent/__init__.py +19 -27
  2. agno/agent/agent.py +3143 -4170
  3. agno/api/agent.py +11 -67
  4. agno/api/api.py +5 -46
  5. agno/api/evals.py +8 -19
  6. agno/api/os.py +17 -0
  7. agno/api/routes.py +6 -41
  8. agno/api/schemas/__init__.py +9 -0
  9. agno/api/schemas/agent.py +5 -21
  10. agno/api/schemas/evals.py +7 -16
  11. agno/api/schemas/os.py +14 -0
  12. agno/api/schemas/team.py +5 -21
  13. agno/api/schemas/utils.py +21 -0
  14. agno/api/schemas/workflows.py +11 -7
  15. agno/api/settings.py +53 -0
  16. agno/api/team.py +11 -66
  17. agno/api/workflow.py +28 -0
  18. agno/cloud/aws/base.py +214 -0
  19. agno/cloud/aws/s3/__init__.py +2 -0
  20. agno/cloud/aws/s3/api_client.py +43 -0
  21. agno/cloud/aws/s3/bucket.py +195 -0
  22. agno/cloud/aws/s3/object.py +57 -0
  23. agno/db/__init__.py +24 -0
  24. agno/db/base.py +245 -0
  25. agno/db/dynamo/__init__.py +3 -0
  26. agno/db/dynamo/dynamo.py +1743 -0
  27. agno/db/dynamo/schemas.py +278 -0
  28. agno/db/dynamo/utils.py +684 -0
  29. agno/db/firestore/__init__.py +3 -0
  30. agno/db/firestore/firestore.py +1432 -0
  31. agno/db/firestore/schemas.py +130 -0
  32. agno/db/firestore/utils.py +278 -0
  33. agno/db/gcs_json/__init__.py +3 -0
  34. agno/db/gcs_json/gcs_json_db.py +1001 -0
  35. agno/db/gcs_json/utils.py +194 -0
  36. agno/db/in_memory/__init__.py +3 -0
  37. agno/db/in_memory/in_memory_db.py +882 -0
  38. agno/db/in_memory/utils.py +172 -0
  39. agno/db/json/__init__.py +3 -0
  40. agno/db/json/json_db.py +1045 -0
  41. agno/db/json/utils.py +196 -0
  42. agno/db/migrations/v1_to_v2.py +162 -0
  43. agno/db/mongo/__init__.py +3 -0
  44. agno/db/mongo/mongo.py +1416 -0
  45. agno/db/mongo/schemas.py +77 -0
  46. agno/db/mongo/utils.py +204 -0
  47. agno/db/mysql/__init__.py +3 -0
  48. agno/db/mysql/mysql.py +1719 -0
  49. agno/db/mysql/schemas.py +124 -0
  50. agno/db/mysql/utils.py +297 -0
  51. agno/db/postgres/__init__.py +3 -0
  52. agno/db/postgres/postgres.py +1710 -0
  53. agno/db/postgres/schemas.py +124 -0
  54. agno/db/postgres/utils.py +280 -0
  55. agno/db/redis/__init__.py +3 -0
  56. agno/db/redis/redis.py +1367 -0
  57. agno/db/redis/schemas.py +109 -0
  58. agno/db/redis/utils.py +288 -0
  59. agno/db/schemas/__init__.py +3 -0
  60. agno/db/schemas/evals.py +33 -0
  61. agno/db/schemas/knowledge.py +40 -0
  62. agno/db/schemas/memory.py +46 -0
  63. agno/db/singlestore/__init__.py +3 -0
  64. agno/db/singlestore/schemas.py +116 -0
  65. agno/db/singlestore/singlestore.py +1712 -0
  66. agno/db/singlestore/utils.py +326 -0
  67. agno/db/sqlite/__init__.py +3 -0
  68. agno/db/sqlite/schemas.py +119 -0
  69. agno/db/sqlite/sqlite.py +1676 -0
  70. agno/db/sqlite/utils.py +268 -0
  71. agno/db/utils.py +88 -0
  72. agno/eval/__init__.py +14 -0
  73. agno/eval/accuracy.py +154 -48
  74. agno/eval/performance.py +88 -23
  75. agno/eval/reliability.py +73 -20
  76. agno/eval/utils.py +23 -13
  77. agno/integrations/discord/__init__.py +3 -0
  78. agno/{app → integrations}/discord/client.py +10 -10
  79. agno/knowledge/__init__.py +2 -2
  80. agno/{document → knowledge}/chunking/agentic.py +2 -2
  81. agno/{document → knowledge}/chunking/document.py +2 -2
  82. agno/{document → knowledge}/chunking/fixed.py +3 -3
  83. agno/{document → knowledge}/chunking/markdown.py +2 -2
  84. agno/{document → knowledge}/chunking/recursive.py +2 -2
  85. agno/{document → knowledge}/chunking/row.py +2 -2
  86. agno/knowledge/chunking/semantic.py +59 -0
  87. agno/knowledge/chunking/strategy.py +121 -0
  88. agno/knowledge/content.py +74 -0
  89. agno/knowledge/document/__init__.py +5 -0
  90. agno/{document → knowledge/document}/base.py +12 -2
  91. agno/knowledge/embedder/__init__.py +5 -0
  92. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  93. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  94. agno/{embedder → knowledge/embedder}/base.py +6 -0
  95. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  96. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  97. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  98. agno/{embedder → knowledge/embedder}/google.py +74 -1
  99. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  100. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  101. agno/knowledge/embedder/langdb.py +22 -0
  102. agno/knowledge/embedder/mistral.py +139 -0
  103. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  104. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  105. agno/knowledge/embedder/openai.py +223 -0
  106. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  107. agno/{embedder → knowledge/embedder}/together.py +1 -1
  108. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  109. agno/knowledge/knowledge.py +1551 -0
  110. agno/knowledge/reader/__init__.py +7 -0
  111. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  112. agno/knowledge/reader/base.py +88 -0
  113. agno/{document → knowledge}/reader/csv_reader.py +47 -65
  114. agno/knowledge/reader/docx_reader.py +83 -0
  115. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  116. agno/{document → knowledge}/reader/json_reader.py +30 -9
  117. agno/{document → knowledge}/reader/markdown_reader.py +58 -9
  118. agno/{document → knowledge}/reader/pdf_reader.py +71 -126
  119. agno/knowledge/reader/reader_factory.py +268 -0
  120. agno/knowledge/reader/s3_reader.py +101 -0
  121. agno/{document → knowledge}/reader/text_reader.py +31 -10
  122. agno/knowledge/reader/url_reader.py +128 -0
  123. agno/knowledge/reader/web_search_reader.py +366 -0
  124. agno/{document → knowledge}/reader/website_reader.py +37 -10
  125. agno/knowledge/reader/wikipedia_reader.py +59 -0
  126. agno/knowledge/reader/youtube_reader.py +78 -0
  127. agno/knowledge/remote_content/remote_content.py +88 -0
  128. agno/{reranker → knowledge/reranker}/base.py +1 -1
  129. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  130. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  131. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  132. agno/knowledge/types.py +30 -0
  133. agno/knowledge/utils.py +169 -0
  134. agno/media.py +269 -268
  135. agno/memory/__init__.py +2 -10
  136. agno/memory/manager.py +1003 -148
  137. agno/models/aimlapi/__init__.py +2 -2
  138. agno/models/aimlapi/aimlapi.py +6 -6
  139. agno/models/anthropic/claude.py +128 -72
  140. agno/models/aws/bedrock.py +107 -175
  141. agno/models/aws/claude.py +64 -18
  142. agno/models/azure/ai_foundry.py +73 -23
  143. agno/models/base.py +346 -290
  144. agno/models/cerebras/cerebras.py +84 -27
  145. agno/models/cohere/chat.py +106 -98
  146. agno/models/google/gemini.py +105 -46
  147. agno/models/groq/groq.py +97 -35
  148. agno/models/huggingface/huggingface.py +92 -27
  149. agno/models/ibm/watsonx.py +72 -13
  150. agno/models/litellm/chat.py +85 -13
  151. agno/models/message.py +46 -151
  152. agno/models/meta/llama.py +85 -49
  153. agno/models/metrics.py +120 -0
  154. agno/models/mistral/mistral.py +90 -21
  155. agno/models/ollama/__init__.py +0 -2
  156. agno/models/ollama/chat.py +85 -47
  157. agno/models/openai/chat.py +154 -37
  158. agno/models/openai/responses.py +178 -105
  159. agno/models/perplexity/perplexity.py +26 -2
  160. agno/models/portkey/portkey.py +0 -7
  161. agno/models/response.py +15 -9
  162. agno/models/utils.py +20 -0
  163. agno/models/vercel/__init__.py +2 -2
  164. agno/models/vercel/v0.py +1 -1
  165. agno/models/vllm/__init__.py +2 -2
  166. agno/models/vllm/vllm.py +3 -3
  167. agno/models/xai/xai.py +10 -10
  168. agno/os/__init__.py +3 -0
  169. agno/os/app.py +497 -0
  170. agno/os/auth.py +47 -0
  171. agno/os/config.py +103 -0
  172. agno/os/interfaces/agui/__init__.py +3 -0
  173. agno/os/interfaces/agui/agui.py +31 -0
  174. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  175. agno/{app → os/interfaces}/agui/utils.py +65 -28
  176. agno/os/interfaces/base.py +21 -0
  177. agno/os/interfaces/slack/__init__.py +3 -0
  178. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  179. agno/os/interfaces/slack/slack.py +32 -0
  180. agno/os/interfaces/whatsapp/__init__.py +3 -0
  181. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  182. agno/os/interfaces/whatsapp/whatsapp.py +29 -0
  183. agno/os/mcp.py +235 -0
  184. agno/os/router.py +1400 -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 +393 -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 +850 -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 +410 -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 +178 -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 +536 -0
  201. agno/os/schema.py +945 -0
  202. agno/{app/playground → os}/settings.py +7 -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/agent.py +633 -0
  212. agno/run/base.py +53 -77
  213. agno/run/cancel.py +81 -0
  214. agno/run/team.py +243 -96
  215. agno/run/workflow.py +550 -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 +3260 -4824
  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 +43 -23
  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 +20 -17
  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 +22 -12
  244. agno/tools/daytona.py +13 -16
  245. agno/tools/decorator.py +6 -3
  246. agno/tools/desi_vocal.py +17 -8
  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 +62 -62
  252. agno/tools/eleven_labs.py +36 -29
  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 +30 -30
  257. agno/tools/file.py +9 -8
  258. agno/tools/financial_datasets.py +25 -44
  259. agno/tools/firecrawl.py +17 -18
  260. agno/tools/function.py +127 -18
  261. agno/tools/giphy.py +23 -11
  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 +32 -20
  276. agno/tools/mcp.py +1 -2
  277. agno/tools/mem0.py +18 -12
  278. agno/tools/memori.py +14 -10
  279. agno/tools/mlx_transcribe.py +3 -2
  280. agno/tools/models/azure_openai.py +33 -15
  281. agno/tools/models/gemini.py +59 -32
  282. agno/tools/models/groq.py +30 -23
  283. agno/tools/models/nebius.py +28 -12
  284. agno/tools/models_labs.py +40 -16
  285. agno/tools/moviepy_video.py +7 -6
  286. agno/tools/neo4j.py +10 -8
  287. agno/tools/newspaper.py +7 -2
  288. agno/tools/newspaper4k.py +8 -3
  289. agno/tools/openai.py +58 -32
  290. agno/tools/openbb.py +12 -11
  291. agno/tools/opencv.py +63 -47
  292. agno/tools/openweather.py +14 -12
  293. agno/tools/pandas.py +11 -3
  294. agno/tools/postgres.py +4 -12
  295. agno/tools/pubmed.py +4 -1
  296. agno/tools/python.py +9 -22
  297. agno/tools/reasoning.py +35 -27
  298. agno/tools/reddit.py +11 -26
  299. agno/tools/replicate.py +55 -42
  300. agno/tools/resend.py +4 -1
  301. agno/tools/scrapegraph.py +15 -14
  302. agno/tools/searxng.py +10 -23
  303. agno/tools/serpapi.py +6 -3
  304. agno/tools/serper.py +13 -4
  305. agno/tools/shell.py +9 -2
  306. agno/tools/slack.py +12 -11
  307. agno/tools/sleep.py +3 -2
  308. agno/tools/spider.py +24 -4
  309. agno/tools/sql.py +7 -6
  310. agno/tools/tavily.py +6 -4
  311. agno/tools/telegram.py +12 -4
  312. agno/tools/todoist.py +11 -31
  313. agno/tools/toolkit.py +1 -1
  314. agno/tools/trafilatura.py +22 -6
  315. agno/tools/trello.py +9 -22
  316. agno/tools/twilio.py +10 -3
  317. agno/tools/user_control_flow.py +6 -1
  318. agno/tools/valyu.py +34 -5
  319. agno/tools/visualization.py +19 -28
  320. agno/tools/webbrowser.py +4 -3
  321. agno/tools/webex.py +11 -7
  322. agno/tools/website.py +15 -46
  323. agno/tools/webtools.py +12 -4
  324. agno/tools/whatsapp.py +5 -9
  325. agno/tools/wikipedia.py +20 -13
  326. agno/tools/x.py +14 -13
  327. agno/tools/yfinance.py +13 -40
  328. agno/tools/youtube.py +26 -20
  329. agno/tools/zendesk.py +7 -2
  330. agno/tools/zep.py +10 -7
  331. agno/tools/zoom.py +10 -9
  332. agno/utils/common.py +1 -19
  333. agno/utils/events.py +100 -123
  334. agno/utils/gemini.py +1 -1
  335. agno/utils/knowledge.py +29 -0
  336. agno/utils/log.py +54 -4
  337. agno/utils/mcp.py +68 -10
  338. agno/utils/media.py +39 -0
  339. agno/utils/message.py +12 -1
  340. agno/utils/models/aws_claude.py +1 -1
  341. agno/utils/models/claude.py +6 -12
  342. agno/utils/models/cohere.py +1 -1
  343. agno/utils/models/mistral.py +8 -7
  344. agno/utils/models/schema_utils.py +3 -3
  345. agno/utils/models/watsonx.py +1 -1
  346. agno/utils/openai.py +1 -1
  347. agno/utils/pprint.py +33 -32
  348. agno/utils/print_response/agent.py +779 -0
  349. agno/utils/print_response/team.py +1669 -0
  350. agno/utils/print_response/workflow.py +1451 -0
  351. agno/utils/prompts.py +14 -14
  352. agno/utils/reasoning.py +87 -0
  353. agno/utils/response.py +42 -42
  354. agno/utils/streamlit.py +481 -0
  355. agno/utils/string.py +8 -22
  356. agno/utils/team.py +50 -0
  357. agno/utils/timer.py +2 -2
  358. agno/vectordb/base.py +33 -21
  359. agno/vectordb/cassandra/cassandra.py +287 -23
  360. agno/vectordb/chroma/chromadb.py +482 -59
  361. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  362. agno/vectordb/couchbase/couchbase.py +309 -29
  363. agno/vectordb/lancedb/lance_db.py +360 -21
  364. agno/vectordb/langchaindb/__init__.py +5 -0
  365. agno/vectordb/langchaindb/langchaindb.py +145 -0
  366. agno/vectordb/lightrag/__init__.py +5 -0
  367. agno/vectordb/lightrag/lightrag.py +374 -0
  368. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  369. agno/vectordb/milvus/milvus.py +242 -32
  370. agno/vectordb/mongodb/mongodb.py +200 -24
  371. agno/vectordb/pgvector/pgvector.py +319 -37
  372. agno/vectordb/pineconedb/pineconedb.py +221 -27
  373. agno/vectordb/qdrant/qdrant.py +334 -14
  374. agno/vectordb/singlestore/singlestore.py +286 -29
  375. agno/vectordb/surrealdb/surrealdb.py +187 -7
  376. agno/vectordb/upstashdb/upstashdb.py +342 -26
  377. agno/vectordb/weaviate/weaviate.py +227 -165
  378. agno/workflow/__init__.py +17 -13
  379. agno/workflow/{v2/condition.py → condition.py} +135 -32
  380. agno/workflow/{v2/loop.py → loop.py} +115 -28
  381. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  382. agno/workflow/{v2/router.py → router.py} +133 -32
  383. agno/workflow/{v2/step.py → step.py} +207 -49
  384. agno/workflow/{v2/steps.py → steps.py} +147 -66
  385. agno/workflow/types.py +482 -0
  386. agno/workflow/workflow.py +2410 -696
  387. agno-2.0.0.dist-info/METADATA +494 -0
  388. agno-2.0.0.dist-info/RECORD +515 -0
  389. agno-2.0.0.dist-info/licenses/LICENSE +201 -0
  390. agno/agent/metrics.py +0 -110
  391. agno/api/app.py +0 -35
  392. agno/api/playground.py +0 -92
  393. agno/api/schemas/app.py +0 -12
  394. agno/api/schemas/playground.py +0 -22
  395. agno/api/schemas/user.py +0 -35
  396. agno/api/schemas/workspace.py +0 -46
  397. agno/api/user.py +0 -160
  398. agno/api/workflows.py +0 -33
  399. agno/api/workspace.py +0 -175
  400. agno/app/agui/__init__.py +0 -3
  401. agno/app/agui/app.py +0 -17
  402. agno/app/agui/sync_router.py +0 -120
  403. agno/app/base.py +0 -186
  404. agno/app/discord/__init__.py +0 -3
  405. agno/app/fastapi/__init__.py +0 -3
  406. agno/app/fastapi/app.py +0 -107
  407. agno/app/fastapi/async_router.py +0 -457
  408. agno/app/fastapi/sync_router.py +0 -448
  409. agno/app/playground/app.py +0 -228
  410. agno/app/playground/async_router.py +0 -1053
  411. agno/app/playground/deploy.py +0 -249
  412. agno/app/playground/operator.py +0 -183
  413. agno/app/playground/schemas.py +0 -223
  414. agno/app/playground/serve.py +0 -55
  415. agno/app/playground/sync_router.py +0 -1045
  416. agno/app/playground/utils.py +0 -46
  417. agno/app/settings.py +0 -15
  418. agno/app/slack/__init__.py +0 -3
  419. agno/app/slack/app.py +0 -19
  420. agno/app/slack/sync_router.py +0 -92
  421. agno/app/utils.py +0 -54
  422. agno/app/whatsapp/__init__.py +0 -3
  423. agno/app/whatsapp/app.py +0 -15
  424. agno/app/whatsapp/sync_router.py +0 -197
  425. agno/cli/auth_server.py +0 -249
  426. agno/cli/config.py +0 -274
  427. agno/cli/console.py +0 -88
  428. agno/cli/credentials.py +0 -23
  429. agno/cli/entrypoint.py +0 -571
  430. agno/cli/operator.py +0 -357
  431. agno/cli/settings.py +0 -96
  432. agno/cli/ws/ws_cli.py +0 -817
  433. agno/constants.py +0 -13
  434. agno/document/__init__.py +0 -5
  435. agno/document/chunking/semantic.py +0 -45
  436. agno/document/chunking/strategy.py +0 -31
  437. agno/document/reader/__init__.py +0 -5
  438. agno/document/reader/base.py +0 -47
  439. agno/document/reader/docx_reader.py +0 -60
  440. agno/document/reader/gcs/pdf_reader.py +0 -44
  441. agno/document/reader/s3/pdf_reader.py +0 -59
  442. agno/document/reader/s3/text_reader.py +0 -63
  443. agno/document/reader/url_reader.py +0 -59
  444. agno/document/reader/youtube_reader.py +0 -58
  445. agno/embedder/__init__.py +0 -5
  446. agno/embedder/langdb.py +0 -80
  447. agno/embedder/mistral.py +0 -82
  448. agno/embedder/openai.py +0 -78
  449. agno/file/__init__.py +0 -5
  450. agno/file/file.py +0 -16
  451. agno/file/local/csv.py +0 -32
  452. agno/file/local/txt.py +0 -19
  453. agno/infra/app.py +0 -240
  454. agno/infra/base.py +0 -144
  455. agno/infra/context.py +0 -20
  456. agno/infra/db_app.py +0 -52
  457. agno/infra/resource.py +0 -205
  458. agno/infra/resources.py +0 -55
  459. agno/knowledge/agent.py +0 -702
  460. agno/knowledge/arxiv.py +0 -33
  461. agno/knowledge/combined.py +0 -36
  462. agno/knowledge/csv.py +0 -144
  463. agno/knowledge/csv_url.py +0 -124
  464. agno/knowledge/document.py +0 -223
  465. agno/knowledge/docx.py +0 -137
  466. agno/knowledge/firecrawl.py +0 -34
  467. agno/knowledge/gcs/__init__.py +0 -0
  468. agno/knowledge/gcs/base.py +0 -39
  469. agno/knowledge/gcs/pdf.py +0 -125
  470. agno/knowledge/json.py +0 -137
  471. agno/knowledge/langchain.py +0 -71
  472. agno/knowledge/light_rag.py +0 -273
  473. agno/knowledge/llamaindex.py +0 -66
  474. agno/knowledge/markdown.py +0 -154
  475. agno/knowledge/pdf.py +0 -164
  476. agno/knowledge/pdf_bytes.py +0 -42
  477. agno/knowledge/pdf_url.py +0 -148
  478. agno/knowledge/s3/__init__.py +0 -0
  479. agno/knowledge/s3/base.py +0 -64
  480. agno/knowledge/s3/pdf.py +0 -33
  481. agno/knowledge/s3/text.py +0 -34
  482. agno/knowledge/text.py +0 -141
  483. agno/knowledge/url.py +0 -46
  484. agno/knowledge/website.py +0 -179
  485. agno/knowledge/wikipedia.py +0 -32
  486. agno/knowledge/youtube.py +0 -35
  487. agno/memory/agent.py +0 -423
  488. agno/memory/classifier.py +0 -104
  489. agno/memory/db/__init__.py +0 -5
  490. agno/memory/db/base.py +0 -42
  491. agno/memory/db/mongodb.py +0 -189
  492. agno/memory/db/postgres.py +0 -203
  493. agno/memory/db/sqlite.py +0 -193
  494. agno/memory/memory.py +0 -22
  495. agno/memory/row.py +0 -36
  496. agno/memory/summarizer.py +0 -201
  497. agno/memory/summary.py +0 -19
  498. agno/memory/team.py +0 -415
  499. agno/memory/v2/__init__.py +0 -2
  500. agno/memory/v2/db/__init__.py +0 -1
  501. agno/memory/v2/db/base.py +0 -42
  502. agno/memory/v2/db/firestore.py +0 -339
  503. agno/memory/v2/db/mongodb.py +0 -196
  504. agno/memory/v2/db/postgres.py +0 -214
  505. agno/memory/v2/db/redis.py +0 -187
  506. agno/memory/v2/db/schema.py +0 -54
  507. agno/memory/v2/db/sqlite.py +0 -209
  508. agno/memory/v2/manager.py +0 -437
  509. agno/memory/v2/memory.py +0 -1097
  510. agno/memory/v2/schema.py +0 -55
  511. agno/memory/v2/summarizer.py +0 -215
  512. agno/memory/workflow.py +0 -38
  513. agno/models/ollama/tools.py +0 -430
  514. agno/models/qwen/__init__.py +0 -5
  515. agno/playground/__init__.py +0 -10
  516. agno/playground/deploy.py +0 -3
  517. agno/playground/playground.py +0 -3
  518. agno/playground/serve.py +0 -3
  519. agno/playground/settings.py +0 -3
  520. agno/reranker/__init__.py +0 -0
  521. agno/run/response.py +0 -467
  522. agno/run/v2/__init__.py +0 -0
  523. agno/run/v2/workflow.py +0 -567
  524. agno/storage/__init__.py +0 -0
  525. agno/storage/agent/__init__.py +0 -0
  526. agno/storage/agent/dynamodb.py +0 -1
  527. agno/storage/agent/json.py +0 -1
  528. agno/storage/agent/mongodb.py +0 -1
  529. agno/storage/agent/postgres.py +0 -1
  530. agno/storage/agent/singlestore.py +0 -1
  531. agno/storage/agent/sqlite.py +0 -1
  532. agno/storage/agent/yaml.py +0 -1
  533. agno/storage/base.py +0 -60
  534. agno/storage/dynamodb.py +0 -673
  535. agno/storage/firestore.py +0 -297
  536. agno/storage/gcs_json.py +0 -261
  537. agno/storage/in_memory.py +0 -234
  538. agno/storage/json.py +0 -237
  539. agno/storage/mongodb.py +0 -328
  540. agno/storage/mysql.py +0 -685
  541. agno/storage/postgres.py +0 -682
  542. agno/storage/redis.py +0 -336
  543. agno/storage/session/__init__.py +0 -16
  544. agno/storage/session/agent.py +0 -64
  545. agno/storage/session/team.py +0 -63
  546. agno/storage/session/v2/__init__.py +0 -5
  547. agno/storage/session/workflow.py +0 -61
  548. agno/storage/singlestore.py +0 -606
  549. agno/storage/sqlite.py +0 -646
  550. agno/storage/workflow/__init__.py +0 -0
  551. agno/storage/workflow/mongodb.py +0 -1
  552. agno/storage/workflow/postgres.py +0 -1
  553. agno/storage/workflow/sqlite.py +0 -1
  554. agno/storage/yaml.py +0 -241
  555. agno/tools/thinking.py +0 -73
  556. agno/utils/defaults.py +0 -57
  557. agno/utils/filesystem.py +0 -39
  558. agno/utils/git.py +0 -52
  559. agno/utils/json_io.py +0 -30
  560. agno/utils/load_env.py +0 -19
  561. agno/utils/py_io.py +0 -19
  562. agno/utils/pyproject.py +0 -18
  563. agno/utils/resource_filter.py +0 -31
  564. agno/workflow/v2/__init__.py +0 -21
  565. agno/workflow/v2/types.py +0 -357
  566. agno/workflow/v2/workflow.py +0 -3313
  567. agno/workspace/__init__.py +0 -0
  568. agno/workspace/config.py +0 -325
  569. agno/workspace/enums.py +0 -6
  570. agno/workspace/helpers.py +0 -52
  571. agno/workspace/operator.py +0 -757
  572. agno/workspace/settings.py +0 -158
  573. agno-1.8.2.dist-info/METADATA +0 -982
  574. agno-1.8.2.dist-info/RECORD +0 -566
  575. agno-1.8.2.dist-info/entry_points.txt +0 -3
  576. agno-1.8.2.dist-info/licenses/LICENSE +0 -375
  577. /agno/{app → db/migrations}/__init__.py +0 -0
  578. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  579. /agno/{cli → integrations}/__init__.py +0 -0
  580. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  581. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  582. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  583. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  584. /agno/{app → os/interfaces}/slack/security.py +0 -0
  585. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  586. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  587. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  588. {agno-1.8.2.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
  589. {agno-1.8.2.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
agno/models/message.py CHANGED
@@ -1,13 +1,12 @@
1
1
  import json
2
- from dataclasses import asdict, dataclass
3
2
  from time import time
4
3
  from typing import Any, Dict, List, Optional, Sequence, Union
5
4
 
6
5
  from pydantic import BaseModel, ConfigDict, Field
7
6
 
8
- from agno.media import Audio, AudioResponse, File, Image, ImageArtifact, Video
7
+ from agno.media import Audio, File, Image, Video
8
+ from agno.models.metrics import Metrics
9
9
  from agno.utils.log import log_debug, log_error, log_info, log_warning
10
- from agno.utils.timer import Timer
11
10
 
12
11
 
13
12
  class MessageReferences(BaseModel):
@@ -49,118 +48,6 @@ class Citations(BaseModel):
49
48
  documents: Optional[List[DocumentCitation]] = None
50
49
 
51
50
 
52
- @dataclass
53
- class MessageMetrics:
54
- input_tokens: int = 0
55
- output_tokens: int = 0
56
- total_tokens: int = 0
57
-
58
- audio_tokens: int = 0
59
- input_audio_tokens: int = 0
60
- output_audio_tokens: int = 0
61
- cached_tokens: int = 0
62
- cache_write_tokens: int = 0
63
- reasoning_tokens: int = 0
64
- prompt_tokens: int = 0
65
- completion_tokens: int = 0
66
- prompt_tokens_details: Optional[dict] = None
67
- completion_tokens_details: Optional[dict] = None
68
-
69
- additional_metrics: Optional[dict] = None
70
-
71
- time: Optional[float] = None
72
- time_to_first_token: Optional[float] = None
73
-
74
- timer: Optional[Timer] = None
75
-
76
- def to_dict(self) -> Dict[str, Any]:
77
- metrics_dict = asdict(self)
78
- metrics_dict.pop("timer")
79
- metrics_dict = {
80
- k: v
81
- for k, v in metrics_dict.items()
82
- if v is not None and (not isinstance(v, (int, float)) or v != 0) and (not isinstance(v, dict) or len(v) > 0)
83
- }
84
- return metrics_dict
85
-
86
- def start_timer(self):
87
- if self.timer is None:
88
- self.timer = Timer()
89
- self.timer.start()
90
-
91
- def stop_timer(self, set_time: bool = True):
92
- if self.timer is not None:
93
- self.timer.stop()
94
- if set_time:
95
- self.time = self.timer.elapsed
96
-
97
- def set_time_to_first_token(self):
98
- if self.timer is not None:
99
- self.time_to_first_token = self.timer.elapsed
100
-
101
- def __add__(self, other: "MessageMetrics") -> "MessageMetrics":
102
- # Create new instance with summed basic metrics
103
- result = MessageMetrics(
104
- input_tokens=self.input_tokens + other.input_tokens,
105
- output_tokens=self.output_tokens + other.output_tokens,
106
- total_tokens=self.total_tokens + other.total_tokens,
107
- prompt_tokens=self.prompt_tokens + other.prompt_tokens,
108
- completion_tokens=self.completion_tokens + other.completion_tokens,
109
- audio_tokens=self.audio_tokens + other.audio_tokens,
110
- input_audio_tokens=self.input_audio_tokens + other.input_audio_tokens,
111
- output_audio_tokens=self.output_audio_tokens + other.output_audio_tokens,
112
- cached_tokens=self.cached_tokens + other.cached_tokens,
113
- cache_write_tokens=self.cache_write_tokens + other.cache_write_tokens,
114
- reasoning_tokens=self.reasoning_tokens + other.reasoning_tokens,
115
- )
116
-
117
- # Handle prompt_tokens_details
118
- if self.prompt_tokens_details or other.prompt_tokens_details:
119
- result.prompt_tokens_details = {}
120
- # Merge from self
121
- if self.prompt_tokens_details:
122
- result.prompt_tokens_details.update(self.prompt_tokens_details)
123
- # Add values from other
124
- if other.prompt_tokens_details:
125
- for key, value in other.prompt_tokens_details.items():
126
- result.prompt_tokens_details[key] = result.prompt_tokens_details.get(key, 0) + value
127
-
128
- # Handle completion_tokens_details similarly
129
- if self.completion_tokens_details or other.completion_tokens_details:
130
- result.completion_tokens_details = {}
131
- if self.completion_tokens_details:
132
- result.completion_tokens_details.update(self.completion_tokens_details)
133
- if other.completion_tokens_details:
134
- for key, value in other.completion_tokens_details.items():
135
- result.completion_tokens_details[key] = result.completion_tokens_details.get(key, 0) + value
136
-
137
- # Handle additional metrics
138
- if self.additional_metrics or other.additional_metrics:
139
- result.additional_metrics = {}
140
- if self.additional_metrics:
141
- result.additional_metrics.update(self.additional_metrics)
142
- if other.additional_metrics:
143
- result.additional_metrics.update(other.additional_metrics)
144
-
145
- # Sum times if both exist
146
- if self.time is not None and other.time is not None:
147
- result.time = self.time + other.time
148
- elif self.time is not None:
149
- result.time = self.time
150
- elif other.time is not None:
151
- result.time = other.time
152
-
153
- # Handle time_to_first_token (take the first non-None value)
154
- result.time_to_first_token = self.time_to_first_token or other.time_to_first_token
155
-
156
- return result
157
-
158
- def __radd__(self, other: "MessageMetrics") -> "MessageMetrics":
159
- if other == 0: # Handle sum() starting value
160
- return self
161
- return self + other
162
-
163
-
164
51
  class Message(BaseModel):
165
52
  """Message sent to the Model"""
166
53
 
@@ -184,12 +71,12 @@ class Message(BaseModel):
184
71
  files: Optional[Sequence[File]] = None
185
72
 
186
73
  # Output from the models
187
- audio_output: Optional[AudioResponse] = None
188
- image_output: Optional[ImageArtifact] = None
74
+ audio_output: Optional[Audio] = None
75
+ image_output: Optional[Image] = None
76
+ video_output: Optional[Video] = None
189
77
 
190
78
  # The thinking content from the model
191
- thinking: Optional[str] = None
192
- redacted_thinking: Optional[str] = None
79
+ redacted_reasoning_content: Optional[str] = None
193
80
 
194
81
  # Data from the provider we might need on subsequent messages
195
82
  provider_data: Optional[Dict[str, Any]] = None
@@ -213,7 +100,7 @@ class Message(BaseModel):
213
100
  # This flag is enabled when a message is fetched from the agent's memory.
214
101
  from_history: bool = False
215
102
  # Metrics for the message.
216
- metrics: MessageMetrics = Field(default_factory=MessageMetrics)
103
+ metrics: Metrics = Field(default_factory=Metrics)
217
104
  # The references added to the message for RAG
218
105
  references: Optional[MessageReferences] = None
219
106
  # The Unix timestamp the message was created.
@@ -232,6 +119,10 @@ class Message(BaseModel):
232
119
  return json.dumps(self.content)
233
120
  return ""
234
121
 
122
+ @classmethod
123
+ def from_dict(cls, data: Dict[str, Any]) -> "Message":
124
+ return cls(**data)
125
+
235
126
  def to_dict(self) -> Dict[str, Any]:
236
127
  """Returns the message as a dictionary."""
237
128
  message_dict = {
@@ -246,8 +137,7 @@ class Message(BaseModel):
246
137
  "tool_args": self.tool_args,
247
138
  "tool_call_error": self.tool_call_error,
248
139
  "tool_calls": self.tool_calls,
249
- "thinking": self.thinking,
250
- "redacted_thinking": self.redacted_thinking,
140
+ "redacted_reasoning_content": self.redacted_reasoning_content,
251
141
  "provider_data": self.provider_data,
252
142
  }
253
143
  # Filter out None and empty collections
@@ -316,8 +206,8 @@ class Message(BaseModel):
316
206
  _logger(f"Name: {self.name}")
317
207
  if self.tool_call_id:
318
208
  _logger(f"Tool call Id: {self.tool_call_id}")
319
- if self.thinking:
320
- _logger(f"<thinking>\n{self.thinking}\n</thinking>")
209
+ if self.reasoning_content:
210
+ _logger(f"<reasoning>\n{self.reasoning_content}\n</reasoning>")
321
211
  if self.content:
322
212
  if isinstance(self.content, str) or isinstance(self.content, list):
323
213
  _logger(self.content)
@@ -338,12 +228,13 @@ class Message(BaseModel):
338
228
  if isinstance(tool_call_arguments, dict)
339
229
  else json.loads(tool_call_arguments)
340
230
  )
341
- # Ensure tool_call_args is a dictionary before calling .items()
342
- if isinstance(tool_call_args, dict):
343
- arguments = ", ".join(f"{k}: {v}" for k, v in tool_call_args.items())
344
- tool_calls_list.append(f" Arguments: '{arguments}'")
345
- else:
346
- tool_calls_list.append(f" Arguments: '{tool_call_args}'")
231
+ if tool_call_args:
232
+ # Ensure tool_call_args is a dictionary before calling .items()
233
+ if isinstance(tool_call_args, dict):
234
+ arguments = ", ".join(f"{k}: {v}" for k, v in tool_call_args.items())
235
+ tool_calls_list.append(f" Arguments: '{arguments}'")
236
+ else:
237
+ tool_calls_list.append(f" Arguments: '{tool_call_args}'")
347
238
  except json.JSONDecodeError:
348
239
  tool_calls_list.append(" Arguments: 'Invalid JSON format'")
349
240
  tool_calls_str = "\n".join(tool_calls_list)
@@ -359,42 +250,46 @@ class Message(BaseModel):
359
250
  _logger(f"Files added: {len(self.files)}")
360
251
 
361
252
  metrics_header = " TOOL METRICS " if self.role == "tool" else " METRICS "
362
- if metrics and self.metrics is not None and self.metrics != MessageMetrics():
253
+ if metrics and self.metrics is not None and self.metrics != Metrics():
363
254
  _logger(metrics_header, center=True, symbol="*")
364
255
 
365
- # Combine token metrics into a single line
256
+ # Token metrics
366
257
  token_metrics = []
367
- if self.metrics.input_tokens:
258
+ if self.metrics.input_tokens and self.metrics.input_tokens > 0:
368
259
  token_metrics.append(f"input={self.metrics.input_tokens}")
369
- if self.metrics.output_tokens:
260
+ if self.metrics.output_tokens and self.metrics.output_tokens > 0:
370
261
  token_metrics.append(f"output={self.metrics.output_tokens}")
371
- if self.metrics.total_tokens:
262
+ if self.metrics.total_tokens and self.metrics.total_tokens > 0:
372
263
  token_metrics.append(f"total={self.metrics.total_tokens}")
373
- if self.metrics.cached_tokens:
374
- token_metrics.append(f"cached={self.metrics.cached_tokens}")
375
- if self.metrics.cache_write_tokens:
264
+ if self.metrics.cache_read_tokens and self.metrics.cache_read_tokens > 0:
265
+ token_metrics.append(f"cached={self.metrics.cache_read_tokens}")
266
+ if self.metrics.cache_write_tokens and self.metrics.cache_write_tokens > 0:
376
267
  token_metrics.append(f"cache_write_tokens={self.metrics.cache_write_tokens}")
377
- if self.metrics.reasoning_tokens:
268
+ if self.metrics.reasoning_tokens and self.metrics.reasoning_tokens > 0:
378
269
  token_metrics.append(f"reasoning={self.metrics.reasoning_tokens}")
379
- if self.metrics.audio_tokens:
380
- token_metrics.append(f"audio={self.metrics.audio_tokens}")
270
+ if self.metrics.audio_total_tokens and self.metrics.audio_total_tokens > 0:
271
+ token_metrics.append(f"audio={self.metrics.audio_total_tokens}")
381
272
  if token_metrics:
382
273
  _logger(f"* Tokens: {', '.join(token_metrics)}")
383
- if self.metrics.prompt_tokens_details:
384
- _logger(f"* Prompt tokens details: {self.metrics.prompt_tokens_details}")
385
- if self.metrics.completion_tokens_details:
386
- _logger(f"* Completion tokens details: {self.metrics.completion_tokens_details}")
387
- if self.metrics.time is not None:
388
- _logger(f"* Time: {self.metrics.time:.4f}s")
389
- if self.metrics.output_tokens and self.metrics.time:
390
- _logger(f"* Tokens per second: {self.metrics.output_tokens / self.metrics.time:.4f} tokens/s")
391
- if self.metrics.time_to_first_token is not None:
274
+
275
+ # Time related metrics
276
+ if self.metrics.duration is not None and self.metrics.duration > 0:
277
+ _logger(f"* Duration: {self.metrics.duration:.4f}s")
278
+ if self.metrics.output_tokens and self.metrics.duration and self.metrics.duration > 0:
279
+ _logger(
280
+ f"* Tokens per second: {self.metrics.output_tokens / self.metrics.duration:.4f} tokens/s"
281
+ )
282
+ if self.metrics.time_to_first_token is not None and self.metrics.time_to_first_token > 0:
392
283
  _logger(f"* Time to first token: {self.metrics.time_to_first_token:.4f}s")
284
+
285
+ # Non-generic metrics
286
+ if self.metrics.provider_metrics:
287
+ _logger(f"* Provider metrics: {self.metrics.provider_metrics}")
393
288
  if self.metrics.additional_metrics:
394
289
  _logger(f"* Additional metrics: {self.metrics.additional_metrics}")
290
+
395
291
  _logger(metrics_header, center=True, symbol="*")
396
292
 
397
293
  def content_is_valid(self) -> bool:
398
294
  """Check if the message content is valid."""
399
-
400
295
  return self.content is not None and len(self.content) > 0
agno/models/meta/llama.py CHANGED
@@ -9,18 +9,21 @@ from pydantic import BaseModel
9
9
  from agno.exceptions import ModelProviderError
10
10
  from agno.models.base import Model
11
11
  from agno.models.message import Message
12
+ from agno.models.metrics import Metrics
12
13
  from agno.models.response import ModelResponse
14
+ from agno.run.agent import RunOutput
13
15
  from agno.utils.log import log_debug, log_error, log_warning
14
16
  from agno.utils.models.llama import format_message
15
17
 
16
18
  try:
17
19
  from llama_api_client import AsyncLlamaAPIClient, LlamaAPIClient
18
- from llama_api_client.types.create_chat_completion_response import CreateChatCompletionResponse
20
+ from llama_api_client.types.create_chat_completion_response import CreateChatCompletionResponse, Metric
19
21
  from llama_api_client.types.create_chat_completion_response_stream_chunk import (
20
22
  CreateChatCompletionResponseStreamChunk,
21
23
  EventDeltaTextDelta,
22
24
  EventDeltaToolCallDelta,
23
25
  EventDeltaToolCallDeltaFunction,
26
+ EventMetric,
24
27
  )
25
28
  from llama_api_client.types.message_text_content_item import MessageTextContentItem
26
29
  except ImportError:
@@ -192,54 +195,84 @@ class Llama(Model):
192
195
  def invoke(
193
196
  self,
194
197
  messages: List[Message],
198
+ assistant_message: Message,
195
199
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
196
200
  tools: Optional[List[Dict[str, Any]]] = None,
197
201
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
198
- ) -> CreateChatCompletionResponse:
202
+ run_response: Optional[RunOutput] = None,
203
+ ) -> ModelResponse:
199
204
  """
200
205
  Send a chat completion request to the Llama API.
201
206
  """
202
- return self.get_client().chat.completions.create(
207
+ assistant_message.metrics.start_timer()
208
+
209
+ provider_response = self.get_client().chat.completions.create(
203
210
  model=self.id,
204
211
  messages=[format_message(m, tool_calls=bool(tools)) for m in messages], # type: ignore
205
212
  **self.get_request_params(tools=tools, response_format=response_format),
206
213
  )
207
214
 
215
+ assistant_message.metrics.stop_timer()
216
+
217
+ model_response = self._parse_provider_response(provider_response, response_format=response_format)
218
+ return model_response
219
+
208
220
  async def ainvoke(
209
221
  self,
210
222
  messages: List[Message],
223
+ assistant_message: Message,
211
224
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
212
225
  tools: Optional[List[Dict[str, Any]]] = None,
213
226
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
214
- ) -> CreateChatCompletionResponse:
227
+ run_response: Optional[RunOutput] = None,
228
+ ) -> ModelResponse:
215
229
  """
216
230
  Sends an asynchronous chat completion request to the Llama API.
217
231
  """
232
+ if run_response and run_response.metrics:
233
+ run_response.metrics.set_time_to_first_token()
218
234
 
219
- return await self.get_async_client().chat.completions.create(
235
+ assistant_message.metrics.start_timer()
236
+
237
+ provider_response = await self.get_async_client().chat.completions.create(
220
238
  model=self.id,
221
239
  messages=[format_message(m, tool_calls=bool(tools)) for m in messages], # type: ignore
222
240
  **self.get_request_params(tools=tools, response_format=response_format),
223
241
  )
224
242
 
243
+ assistant_message.metrics.stop_timer()
244
+
245
+ model_response = self._parse_provider_response(provider_response, response_format=response_format)
246
+ return model_response
247
+
225
248
  def invoke_stream(
226
249
  self,
227
250
  messages: List[Message],
251
+ assistant_message: Message,
228
252
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
229
253
  tools: Optional[List[Dict[str, Any]]] = None,
230
254
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
231
- ) -> Iterator[CreateChatCompletionResponseStreamChunk]:
255
+ run_response: Optional[RunOutput] = None,
256
+ ) -> Iterator[ModelResponse]:
232
257
  """
233
258
  Send a streaming chat completion request to the Llama API.
234
259
  """
260
+ if run_response and run_response.metrics:
261
+ run_response.metrics.set_time_to_first_token()
235
262
 
236
263
  try:
237
- yield from self.get_client().chat.completions.create(
264
+ assistant_message.metrics.start_timer()
265
+
266
+ for chunk in self.get_client().chat.completions.create(
238
267
  model=self.id,
239
268
  messages=[format_message(m, tool_calls=bool(tools)) for m in messages], # type: ignore
240
269
  stream=True,
241
270
  **self.get_request_params(tools=tools, response_format=response_format),
242
- ) # type: ignore
271
+ ):
272
+ yield self._parse_provider_response_delta(chunk) # type: ignore
273
+
274
+ assistant_message.metrics.stop_timer()
275
+
243
276
  except Exception as e:
244
277
  log_error(f"Error from Llama API: {e}")
245
278
  raise ModelProviderError(message=str(e), model_name=self.name, model_id=self.id) from e
@@ -247,29 +280,36 @@ class Llama(Model):
247
280
  async def ainvoke_stream(
248
281
  self,
249
282
  messages: List[Message],
283
+ assistant_message: Message,
250
284
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
251
285
  tools: Optional[List[Dict[str, Any]]] = None,
252
286
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
253
- ) -> AsyncIterator[CreateChatCompletionResponseStreamChunk]:
287
+ run_response: Optional[RunOutput] = None,
288
+ ) -> AsyncIterator[ModelResponse]:
254
289
  """
255
290
  Sends an asynchronous streaming chat completion request to the Llama API.
256
291
  """
292
+ if run_response and run_response.metrics:
293
+ run_response.metrics.set_time_to_first_token()
294
+
295
+ assistant_message.metrics.start_timer()
257
296
 
258
297
  try:
259
- async_stream = await self.get_async_client().chat.completions.create(
298
+ async for chunk in await self.get_async_client().chat.completions.create(
260
299
  model=self.id,
261
300
  messages=[format_message(m, tool_calls=bool(tools)) for m in messages], # type: ignore
262
301
  stream=True,
263
302
  **self.get_request_params(tools=tools, response_format=response_format),
264
- )
265
- async for chunk in async_stream: # type: ignore
266
- yield chunk # type: ignore
303
+ ):
304
+ yield self._parse_provider_response_delta(chunk) # type: ignore
305
+
306
+ assistant_message.metrics.stop_timer()
307
+
267
308
  except Exception as e:
268
309
  log_error(f"Error from Llama API: {e}")
269
310
  raise ModelProviderError(message=str(e), model_name=self.name, model_id=self.id) from e
270
311
 
271
- @staticmethod
272
- def parse_tool_calls(tool_calls_data: List[EventDeltaToolCallDeltaFunction]) -> List[Dict[str, Any]]:
312
+ def parse_tool_calls(self, tool_calls_data: List[EventDeltaToolCallDeltaFunction]) -> List[Dict[str, Any]]:
273
313
  """
274
314
  Parse the tool calls from the Llama API.
275
315
 
@@ -321,7 +361,7 @@ class Llama(Model):
321
361
 
322
362
  return tool_calls
323
363
 
324
- def parse_provider_response(self, response: CreateChatCompletionResponse, **kwargs) -> ModelResponse:
364
+ def _parse_provider_response(self, response: CreateChatCompletionResponse, **kwargs) -> ModelResponse:
325
365
  """
326
366
  Parse the Llama response into a ModelResponse.
327
367
 
@@ -371,26 +411,12 @@ class Llama(Model):
371
411
 
372
412
  # Add metrics from the metrics list
373
413
  if hasattr(response, "metrics") and response.metrics is not None:
374
- usage_data = {}
375
- metric_map = {
376
- "num_prompt_tokens": "input_tokens",
377
- "num_completion_tokens": "output_tokens",
378
- "num_total_tokens": "total_tokens",
379
- }
380
-
381
- for metric in response.metrics:
382
- key = metric_map.get(metric.metric)
383
- if key:
384
- value = int(metric.value)
385
- usage_data[key] = value
386
-
387
- if usage_data:
388
- model_response.response_usage = usage_data
414
+ model_response.response_usage = self._get_metrics(response.metrics)
389
415
 
390
416
  return model_response
391
417
 
392
- def parse_provider_response_delta(
393
- self, response_delta: CreateChatCompletionResponseStreamChunk, **kwargs
418
+ def _parse_provider_response_delta(
419
+ self, response: CreateChatCompletionResponseStreamChunk, **kwargs
394
420
  ) -> ModelResponse:
395
421
  """
396
422
  Parse the Llama streaming response into a ModelResponse.
@@ -403,25 +429,12 @@ class Llama(Model):
403
429
  """
404
430
  model_response = ModelResponse()
405
431
 
406
- if response_delta is not None:
407
- delta = response_delta.event
432
+ if response is not None:
433
+ delta = response.event
408
434
 
409
435
  # Capture metrics event
410
436
  if delta.event_type == "metrics" and delta.metrics is not None:
411
- usage_data = {}
412
- metric_map = {
413
- "num_prompt_tokens": "input_tokens",
414
- "num_completion_tokens": "output_tokens",
415
- "num_total_tokens": "total_tokens",
416
- }
417
-
418
- for metric in delta.metrics:
419
- key = metric_map.get(metric.metric)
420
- if key:
421
- usage_data[key] = int(metric.value)
422
-
423
- if usage_data:
424
- model_response.response_usage = usage_data
437
+ model_response.response_usage = self._get_metrics(delta.metrics)
425
438
 
426
439
  if isinstance(delta.delta, EventDeltaTextDelta):
427
440
  model_response.content = delta.delta.text
@@ -431,3 +444,26 @@ class Llama(Model):
431
444
  model_response.tool_calls = delta.delta # type: ignore
432
445
 
433
446
  return model_response
447
+
448
+ def _get_metrics(self, response_usage: Union[List[Metric], List[EventMetric]]) -> Metrics:
449
+ """
450
+ Parse the given Llama usage into an Agno Metrics object.
451
+
452
+ Args:
453
+ response_usage: Usage data from Llama
454
+
455
+ Returns:
456
+ Metrics: Parsed metrics data
457
+ """
458
+ metrics = Metrics()
459
+
460
+ for metric in response_usage:
461
+ metrics_field = metric.metric
462
+ if metrics_field == "num_prompt_tokens":
463
+ metrics.input_tokens = int(metric.value)
464
+ elif metrics_field == "num_completion_tokens":
465
+ metrics.output_tokens = int(metric.value)
466
+
467
+ metrics.total_tokens = metrics.input_tokens + metrics.output_tokens
468
+
469
+ return metrics
agno/models/metrics.py ADDED
@@ -0,0 +1,120 @@
1
+ from dataclasses import asdict, dataclass
2
+ from typing import Any, Dict, Optional
3
+
4
+ from agno.utils.timer import Timer
5
+
6
+
7
+ @dataclass
8
+ class Metrics:
9
+ """All relevant metrics for a session, run or message."""
10
+
11
+ # Main token consumption values
12
+ input_tokens: int = 0
13
+ output_tokens: int = 0
14
+ total_tokens: int = 0
15
+
16
+ # Audio token usage
17
+ audio_input_tokens: int = 0
18
+ audio_output_tokens: int = 0
19
+ audio_total_tokens: int = 0
20
+
21
+ # Cache token usage
22
+ cache_read_tokens: int = 0
23
+ cache_write_tokens: int = 0
24
+
25
+ # Tokens employed in reasoning
26
+ reasoning_tokens: int = 0
27
+
28
+ # Time metrics
29
+ # Internal timer utility for tracking execution time
30
+ timer: Optional[Timer] = None
31
+ # Time from run start to first token generation, in seconds
32
+ time_to_first_token: Optional[float] = None
33
+ # Total run time, in seconds
34
+ duration: Optional[float] = None
35
+
36
+ # Provider-specific metrics
37
+ provider_metrics: Optional[dict] = None
38
+
39
+ # Any additional metrics
40
+ additional_metrics: Optional[dict] = None
41
+
42
+ def to_dict(self) -> Dict[str, Any]:
43
+ metrics_dict = asdict(self)
44
+ # Remove the timer util if present
45
+ metrics_dict.pop("timer", None)
46
+ metrics_dict = {
47
+ k: v
48
+ for k, v in metrics_dict.items()
49
+ if v is not None and (not isinstance(v, (int, float)) or v != 0) and (not isinstance(v, dict) or len(v) > 0)
50
+ }
51
+ return metrics_dict
52
+
53
+ def __add__(self, other: "Metrics") -> "Metrics":
54
+ # Create new instance of the same type as self
55
+ result_class = type(self)
56
+ result = result_class(
57
+ input_tokens=self.input_tokens + other.input_tokens,
58
+ output_tokens=self.output_tokens + other.output_tokens,
59
+ total_tokens=self.total_tokens + other.total_tokens,
60
+ audio_total_tokens=self.audio_total_tokens + other.audio_total_tokens,
61
+ audio_input_tokens=self.audio_input_tokens + other.audio_input_tokens,
62
+ audio_output_tokens=self.audio_output_tokens + other.audio_output_tokens,
63
+ cache_read_tokens=self.cache_read_tokens + other.cache_read_tokens,
64
+ cache_write_tokens=self.cache_write_tokens + other.cache_write_tokens,
65
+ reasoning_tokens=self.reasoning_tokens + other.reasoning_tokens,
66
+ )
67
+
68
+ # Handle provider_metrics
69
+ if self.provider_metrics or other.provider_metrics:
70
+ result.provider_metrics = {}
71
+ if self.provider_metrics:
72
+ result.provider_metrics.update(self.provider_metrics)
73
+ if other.provider_metrics:
74
+ result.provider_metrics.update(other.provider_metrics)
75
+
76
+ # Handle additional metrics
77
+ if self.additional_metrics or other.additional_metrics:
78
+ result.additional_metrics = {}
79
+ if self.additional_metrics:
80
+ result.additional_metrics.update(self.additional_metrics)
81
+ if other.additional_metrics:
82
+ result.additional_metrics.update(other.additional_metrics)
83
+
84
+ # Sum durations if both exist
85
+ if self.duration is not None and other.duration is not None:
86
+ result.duration = self.duration + other.duration
87
+ elif self.duration is not None:
88
+ result.duration = self.duration
89
+ elif other.duration is not None:
90
+ result.duration = other.duration
91
+
92
+ # Sum time to first token if both exist
93
+ if self.time_to_first_token is not None and other.time_to_first_token is not None:
94
+ result.time_to_first_token = self.time_to_first_token + other.time_to_first_token
95
+ elif self.time_to_first_token is not None:
96
+ result.time_to_first_token = self.time_to_first_token
97
+ elif other.time_to_first_token is not None:
98
+ result.time_to_first_token = other.time_to_first_token
99
+
100
+ return result
101
+
102
+ def __radd__(self, other: "Metrics") -> "Metrics":
103
+ if other == 0: # Handle sum() starting value
104
+ return self
105
+ return self + other
106
+
107
+ def start_timer(self):
108
+ if self.timer is None:
109
+ self.timer = Timer()
110
+ self.timer.start()
111
+
112
+ def stop_timer(self, set_duration: bool = True):
113
+ if self.timer is not None:
114
+ self.timer.stop()
115
+ if set_duration:
116
+ self.duration = self.timer.elapsed
117
+
118
+ def set_time_to_first_token(self):
119
+ if self.timer is not None:
120
+ self.time_to_first_token = self.timer.elapsed