agno 1.8.1__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 (590) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +3143 -4170
  4. agno/api/agent.py +11 -67
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +8 -19
  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 +11 -66
  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 +1743 -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 +1432 -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 +882 -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 +1045 -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 +1416 -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 +297 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1710 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +280 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1367 -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 +1712 -0
  67. agno/db/singlestore/utils.py +326 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1676 -0
  71. agno/db/sqlite/utils.py +268 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +154 -48
  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 +15 -11
  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 +1551 -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 +47 -65
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/{document → knowledge}/reader/json_reader.py +30 -9
  118. agno/{document → knowledge}/reader/markdown_reader.py +58 -9
  119. agno/{document → knowledge}/reader/pdf_reader.py +71 -126
  120. agno/knowledge/reader/reader_factory.py +268 -0
  121. agno/knowledge/reader/s3_reader.py +101 -0
  122. agno/{document → knowledge}/reader/text_reader.py +31 -10
  123. agno/knowledge/reader/url_reader.py +128 -0
  124. agno/knowledge/reader/web_search_reader.py +366 -0
  125. agno/{document → knowledge}/reader/website_reader.py +37 -10
  126. agno/knowledge/reader/wikipedia_reader.py +59 -0
  127. agno/knowledge/reader/youtube_reader.py +78 -0
  128. agno/knowledge/remote_content/remote_content.py +88 -0
  129. agno/{reranker → knowledge/reranker}/base.py +1 -1
  130. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  131. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  132. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  133. agno/knowledge/types.py +30 -0
  134. agno/knowledge/utils.py +169 -0
  135. agno/media.py +269 -268
  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 +131 -131
  141. agno/models/aws/bedrock.py +110 -182
  142. agno/models/aws/claude.py +64 -18
  143. agno/models/azure/ai_foundry.py +73 -23
  144. agno/models/base.py +346 -290
  145. agno/models/cerebras/cerebras.py +84 -27
  146. agno/models/cohere/chat.py +106 -98
  147. agno/models/google/gemini.py +105 -46
  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 +46 -151
  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 +85 -47
  158. agno/models/openai/chat.py +154 -37
  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 +15 -9
  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 +497 -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 +77 -33
  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 +32 -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 +29 -0
  184. agno/os/mcp.py +235 -0
  185. agno/os/router.py +1400 -0
  186. agno/os/routers/__init__.py +3 -0
  187. agno/os/routers/evals/__init__.py +3 -0
  188. agno/os/routers/evals/evals.py +393 -0
  189. agno/os/routers/evals/schemas.py +142 -0
  190. agno/os/routers/evals/utils.py +161 -0
  191. agno/os/routers/knowledge/__init__.py +3 -0
  192. agno/os/routers/knowledge/knowledge.py +850 -0
  193. agno/os/routers/knowledge/schemas.py +118 -0
  194. agno/os/routers/memory/__init__.py +3 -0
  195. agno/os/routers/memory/memory.py +410 -0
  196. agno/os/routers/memory/schemas.py +58 -0
  197. agno/os/routers/metrics/__init__.py +3 -0
  198. agno/os/routers/metrics/metrics.py +178 -0
  199. agno/os/routers/metrics/schemas.py +47 -0
  200. agno/os/routers/session/__init__.py +3 -0
  201. agno/os/routers/session/session.py +536 -0
  202. agno/os/schema.py +945 -0
  203. agno/{app/playground → os}/settings.py +7 -15
  204. agno/os/utils.py +270 -0
  205. agno/reasoning/azure_ai_foundry.py +4 -4
  206. agno/reasoning/deepseek.py +4 -4
  207. agno/reasoning/default.py +6 -11
  208. agno/reasoning/groq.py +4 -4
  209. agno/reasoning/helpers.py +4 -6
  210. agno/reasoning/ollama.py +4 -4
  211. agno/reasoning/openai.py +4 -4
  212. agno/run/agent.py +633 -0
  213. agno/run/base.py +53 -77
  214. agno/run/cancel.py +81 -0
  215. agno/run/team.py +243 -96
  216. agno/run/workflow.py +550 -12
  217. agno/session/__init__.py +10 -0
  218. agno/session/agent.py +244 -0
  219. agno/session/summary.py +225 -0
  220. agno/session/team.py +262 -0
  221. agno/{storage/session/v2 → session}/workflow.py +47 -24
  222. agno/team/__init__.py +15 -16
  223. agno/team/team.py +3260 -4824
  224. agno/tools/agentql.py +14 -5
  225. agno/tools/airflow.py +9 -4
  226. agno/tools/api.py +7 -3
  227. agno/tools/apify.py +2 -46
  228. agno/tools/arxiv.py +8 -3
  229. agno/tools/aws_lambda.py +7 -5
  230. agno/tools/aws_ses.py +7 -1
  231. agno/tools/baidusearch.py +4 -1
  232. agno/tools/bitbucket.py +4 -4
  233. agno/tools/brandfetch.py +14 -11
  234. agno/tools/bravesearch.py +4 -1
  235. agno/tools/brightdata.py +43 -23
  236. agno/tools/browserbase.py +13 -4
  237. agno/tools/calcom.py +12 -10
  238. agno/tools/calculator.py +10 -27
  239. agno/tools/cartesia.py +20 -17
  240. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  241. agno/tools/confluence.py +8 -8
  242. agno/tools/crawl4ai.py +7 -1
  243. agno/tools/csv_toolkit.py +9 -8
  244. agno/tools/dalle.py +22 -12
  245. agno/tools/daytona.py +13 -16
  246. agno/tools/decorator.py +6 -3
  247. agno/tools/desi_vocal.py +17 -8
  248. agno/tools/discord.py +11 -8
  249. agno/tools/docker.py +30 -42
  250. agno/tools/duckdb.py +34 -53
  251. agno/tools/duckduckgo.py +8 -7
  252. agno/tools/e2b.py +62 -62
  253. agno/tools/eleven_labs.py +36 -29
  254. agno/tools/email.py +4 -1
  255. agno/tools/evm.py +7 -1
  256. agno/tools/exa.py +19 -14
  257. agno/tools/fal.py +30 -30
  258. agno/tools/file.py +9 -8
  259. agno/tools/financial_datasets.py +25 -44
  260. agno/tools/firecrawl.py +22 -22
  261. agno/tools/function.py +127 -18
  262. agno/tools/giphy.py +23 -11
  263. agno/tools/github.py +48 -126
  264. agno/tools/gmail.py +45 -61
  265. agno/tools/google_bigquery.py +7 -6
  266. agno/tools/google_maps.py +11 -26
  267. agno/tools/googlesearch.py +7 -2
  268. agno/tools/googlesheets.py +21 -17
  269. agno/tools/hackernews.py +9 -5
  270. agno/tools/jina.py +5 -4
  271. agno/tools/jira.py +18 -9
  272. agno/tools/knowledge.py +31 -32
  273. agno/tools/linear.py +19 -34
  274. agno/tools/linkup.py +5 -1
  275. agno/tools/local_file_system.py +8 -5
  276. agno/tools/lumalab.py +32 -20
  277. agno/tools/mcp.py +1 -2
  278. agno/tools/mem0.py +18 -12
  279. agno/tools/memori.py +14 -10
  280. agno/tools/mlx_transcribe.py +3 -2
  281. agno/tools/models/azure_openai.py +33 -15
  282. agno/tools/models/gemini.py +59 -32
  283. agno/tools/models/groq.py +30 -23
  284. agno/tools/models/nebius.py +28 -12
  285. agno/tools/models_labs.py +40 -16
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +10 -8
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +58 -32
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +63 -47
  293. agno/tools/openweather.py +14 -12
  294. agno/tools/pandas.py +11 -3
  295. agno/tools/postgres.py +4 -12
  296. agno/tools/pubmed.py +4 -1
  297. agno/tools/python.py +9 -22
  298. agno/tools/reasoning.py +35 -27
  299. agno/tools/reddit.py +11 -26
  300. agno/tools/replicate.py +55 -42
  301. agno/tools/resend.py +4 -1
  302. agno/tools/scrapegraph.py +15 -14
  303. agno/tools/searxng.py +10 -23
  304. agno/tools/serpapi.py +6 -3
  305. agno/tools/serper.py +13 -4
  306. agno/tools/shell.py +9 -2
  307. agno/tools/slack.py +12 -11
  308. agno/tools/sleep.py +3 -2
  309. agno/tools/spider.py +24 -4
  310. agno/tools/sql.py +7 -6
  311. agno/tools/tavily.py +6 -4
  312. agno/tools/telegram.py +12 -4
  313. agno/tools/todoist.py +11 -31
  314. agno/tools/toolkit.py +1 -1
  315. agno/tools/trafilatura.py +22 -6
  316. agno/tools/trello.py +9 -22
  317. agno/tools/twilio.py +10 -3
  318. agno/tools/user_control_flow.py +6 -1
  319. agno/tools/valyu.py +34 -5
  320. agno/tools/visualization.py +19 -28
  321. agno/tools/webbrowser.py +4 -3
  322. agno/tools/webex.py +11 -7
  323. agno/tools/website.py +15 -46
  324. agno/tools/webtools.py +12 -4
  325. agno/tools/whatsapp.py +5 -9
  326. agno/tools/wikipedia.py +20 -13
  327. agno/tools/x.py +14 -13
  328. agno/tools/yfinance.py +13 -40
  329. agno/tools/youtube.py +26 -20
  330. agno/tools/zendesk.py +7 -2
  331. agno/tools/zep.py +10 -7
  332. agno/tools/zoom.py +10 -9
  333. agno/utils/common.py +1 -19
  334. agno/utils/events.py +100 -123
  335. agno/utils/gemini.py +32 -2
  336. agno/utils/knowledge.py +29 -0
  337. agno/utils/log.py +54 -4
  338. agno/utils/mcp.py +68 -10
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/aws_claude.py +1 -1
  342. agno/utils/models/claude.py +47 -4
  343. agno/utils/models/cohere.py +1 -1
  344. agno/utils/models/mistral.py +8 -7
  345. agno/utils/models/schema_utils.py +3 -3
  346. agno/utils/models/watsonx.py +1 -1
  347. agno/utils/openai.py +1 -1
  348. agno/utils/pprint.py +33 -32
  349. agno/utils/print_response/agent.py +779 -0
  350. agno/utils/print_response/team.py +1669 -0
  351. agno/utils/print_response/workflow.py +1451 -0
  352. agno/utils/prompts.py +14 -14
  353. agno/utils/reasoning.py +87 -0
  354. agno/utils/response.py +42 -42
  355. agno/utils/streamlit.py +481 -0
  356. agno/utils/string.py +8 -22
  357. agno/utils/team.py +50 -0
  358. agno/utils/timer.py +2 -2
  359. agno/vectordb/base.py +33 -21
  360. agno/vectordb/cassandra/cassandra.py +287 -23
  361. agno/vectordb/chroma/chromadb.py +482 -59
  362. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  363. agno/vectordb/couchbase/couchbase.py +309 -29
  364. agno/vectordb/lancedb/lance_db.py +360 -21
  365. agno/vectordb/langchaindb/__init__.py +5 -0
  366. agno/vectordb/langchaindb/langchaindb.py +145 -0
  367. agno/vectordb/lightrag/__init__.py +5 -0
  368. agno/vectordb/lightrag/lightrag.py +374 -0
  369. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  370. agno/vectordb/milvus/milvus.py +242 -32
  371. agno/vectordb/mongodb/mongodb.py +200 -24
  372. agno/vectordb/pgvector/pgvector.py +319 -37
  373. agno/vectordb/pineconedb/pineconedb.py +221 -27
  374. agno/vectordb/qdrant/qdrant.py +334 -14
  375. agno/vectordb/singlestore/singlestore.py +286 -29
  376. agno/vectordb/surrealdb/surrealdb.py +187 -7
  377. agno/vectordb/upstashdb/upstashdb.py +342 -26
  378. agno/vectordb/weaviate/weaviate.py +227 -165
  379. agno/workflow/__init__.py +17 -13
  380. agno/workflow/{v2/condition.py → condition.py} +135 -32
  381. agno/workflow/{v2/loop.py → loop.py} +115 -28
  382. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  383. agno/workflow/{v2/router.py → router.py} +133 -32
  384. agno/workflow/{v2/step.py → step.py} +207 -49
  385. agno/workflow/{v2/steps.py → steps.py} +147 -66
  386. agno/workflow/types.py +482 -0
  387. agno/workflow/workflow.py +2410 -696
  388. agno-2.0.0.dist-info/METADATA +494 -0
  389. agno-2.0.0.dist-info/RECORD +515 -0
  390. agno-2.0.0.dist-info/licenses/LICENSE +201 -0
  391. agno/agent/metrics.py +0 -107
  392. agno/api/app.py +0 -35
  393. agno/api/playground.py +0 -92
  394. agno/api/schemas/app.py +0 -12
  395. agno/api/schemas/playground.py +0 -22
  396. agno/api/schemas/user.py +0 -35
  397. agno/api/schemas/workspace.py +0 -46
  398. agno/api/user.py +0 -160
  399. agno/api/workflows.py +0 -33
  400. agno/api/workspace.py +0 -175
  401. agno/app/agui/__init__.py +0 -3
  402. agno/app/agui/app.py +0 -17
  403. agno/app/agui/sync_router.py +0 -120
  404. agno/app/base.py +0 -186
  405. agno/app/discord/__init__.py +0 -3
  406. agno/app/fastapi/__init__.py +0 -3
  407. agno/app/fastapi/app.py +0 -107
  408. agno/app/fastapi/async_router.py +0 -457
  409. agno/app/fastapi/sync_router.py +0 -448
  410. agno/app/playground/app.py +0 -228
  411. agno/app/playground/async_router.py +0 -1050
  412. agno/app/playground/deploy.py +0 -249
  413. agno/app/playground/operator.py +0 -183
  414. agno/app/playground/schemas.py +0 -220
  415. agno/app/playground/serve.py +0 -55
  416. agno/app/playground/sync_router.py +0 -1042
  417. agno/app/playground/utils.py +0 -46
  418. agno/app/settings.py +0 -15
  419. agno/app/slack/__init__.py +0 -3
  420. agno/app/slack/app.py +0 -19
  421. agno/app/slack/sync_router.py +0 -92
  422. agno/app/utils.py +0 -54
  423. agno/app/whatsapp/__init__.py +0 -3
  424. agno/app/whatsapp/app.py +0 -15
  425. agno/app/whatsapp/sync_router.py +0 -197
  426. agno/cli/auth_server.py +0 -249
  427. agno/cli/config.py +0 -274
  428. agno/cli/console.py +0 -88
  429. agno/cli/credentials.py +0 -23
  430. agno/cli/entrypoint.py +0 -571
  431. agno/cli/operator.py +0 -357
  432. agno/cli/settings.py +0 -96
  433. agno/cli/ws/ws_cli.py +0 -817
  434. agno/constants.py +0 -13
  435. agno/document/__init__.py +0 -5
  436. agno/document/chunking/semantic.py +0 -45
  437. agno/document/chunking/strategy.py +0 -31
  438. agno/document/reader/__init__.py +0 -5
  439. agno/document/reader/base.py +0 -47
  440. agno/document/reader/docx_reader.py +0 -60
  441. agno/document/reader/gcs/pdf_reader.py +0 -44
  442. agno/document/reader/s3/pdf_reader.py +0 -59
  443. agno/document/reader/s3/text_reader.py +0 -63
  444. agno/document/reader/url_reader.py +0 -59
  445. agno/document/reader/youtube_reader.py +0 -58
  446. agno/embedder/__init__.py +0 -5
  447. agno/embedder/langdb.py +0 -80
  448. agno/embedder/mistral.py +0 -82
  449. agno/embedder/openai.py +0 -78
  450. agno/file/__init__.py +0 -5
  451. agno/file/file.py +0 -16
  452. agno/file/local/csv.py +0 -32
  453. agno/file/local/txt.py +0 -19
  454. agno/infra/app.py +0 -240
  455. agno/infra/base.py +0 -144
  456. agno/infra/context.py +0 -20
  457. agno/infra/db_app.py +0 -52
  458. agno/infra/resource.py +0 -205
  459. agno/infra/resources.py +0 -55
  460. agno/knowledge/agent.py +0 -702
  461. agno/knowledge/arxiv.py +0 -33
  462. agno/knowledge/combined.py +0 -36
  463. agno/knowledge/csv.py +0 -144
  464. agno/knowledge/csv_url.py +0 -124
  465. agno/knowledge/document.py +0 -223
  466. agno/knowledge/docx.py +0 -137
  467. agno/knowledge/firecrawl.py +0 -34
  468. agno/knowledge/gcs/__init__.py +0 -0
  469. agno/knowledge/gcs/base.py +0 -39
  470. agno/knowledge/gcs/pdf.py +0 -125
  471. agno/knowledge/json.py +0 -137
  472. agno/knowledge/langchain.py +0 -71
  473. agno/knowledge/light_rag.py +0 -273
  474. agno/knowledge/llamaindex.py +0 -66
  475. agno/knowledge/markdown.py +0 -154
  476. agno/knowledge/pdf.py +0 -164
  477. agno/knowledge/pdf_bytes.py +0 -42
  478. agno/knowledge/pdf_url.py +0 -148
  479. agno/knowledge/s3/__init__.py +0 -0
  480. agno/knowledge/s3/base.py +0 -64
  481. agno/knowledge/s3/pdf.py +0 -33
  482. agno/knowledge/s3/text.py +0 -34
  483. agno/knowledge/text.py +0 -141
  484. agno/knowledge/url.py +0 -46
  485. agno/knowledge/website.py +0 -179
  486. agno/knowledge/wikipedia.py +0 -32
  487. agno/knowledge/youtube.py +0 -35
  488. agno/memory/agent.py +0 -423
  489. agno/memory/classifier.py +0 -104
  490. agno/memory/db/__init__.py +0 -5
  491. agno/memory/db/base.py +0 -42
  492. agno/memory/db/mongodb.py +0 -189
  493. agno/memory/db/postgres.py +0 -203
  494. agno/memory/db/sqlite.py +0 -193
  495. agno/memory/memory.py +0 -22
  496. agno/memory/row.py +0 -36
  497. agno/memory/summarizer.py +0 -201
  498. agno/memory/summary.py +0 -19
  499. agno/memory/team.py +0 -415
  500. agno/memory/v2/__init__.py +0 -2
  501. agno/memory/v2/db/__init__.py +0 -1
  502. agno/memory/v2/db/base.py +0 -42
  503. agno/memory/v2/db/firestore.py +0 -339
  504. agno/memory/v2/db/mongodb.py +0 -196
  505. agno/memory/v2/db/postgres.py +0 -214
  506. agno/memory/v2/db/redis.py +0 -187
  507. agno/memory/v2/db/schema.py +0 -54
  508. agno/memory/v2/db/sqlite.py +0 -209
  509. agno/memory/v2/manager.py +0 -437
  510. agno/memory/v2/memory.py +0 -1097
  511. agno/memory/v2/schema.py +0 -55
  512. agno/memory/v2/summarizer.py +0 -215
  513. agno/memory/workflow.py +0 -38
  514. agno/models/ollama/tools.py +0 -430
  515. agno/models/qwen/__init__.py +0 -5
  516. agno/playground/__init__.py +0 -10
  517. agno/playground/deploy.py +0 -3
  518. agno/playground/playground.py +0 -3
  519. agno/playground/serve.py +0 -3
  520. agno/playground/settings.py +0 -3
  521. agno/reranker/__init__.py +0 -0
  522. agno/run/response.py +0 -467
  523. agno/run/v2/__init__.py +0 -0
  524. agno/run/v2/workflow.py +0 -567
  525. agno/storage/__init__.py +0 -0
  526. agno/storage/agent/__init__.py +0 -0
  527. agno/storage/agent/dynamodb.py +0 -1
  528. agno/storage/agent/json.py +0 -1
  529. agno/storage/agent/mongodb.py +0 -1
  530. agno/storage/agent/postgres.py +0 -1
  531. agno/storage/agent/singlestore.py +0 -1
  532. agno/storage/agent/sqlite.py +0 -1
  533. agno/storage/agent/yaml.py +0 -1
  534. agno/storage/base.py +0 -60
  535. agno/storage/dynamodb.py +0 -673
  536. agno/storage/firestore.py +0 -297
  537. agno/storage/gcs_json.py +0 -261
  538. agno/storage/in_memory.py +0 -234
  539. agno/storage/json.py +0 -237
  540. agno/storage/mongodb.py +0 -328
  541. agno/storage/mysql.py +0 -685
  542. agno/storage/postgres.py +0 -682
  543. agno/storage/redis.py +0 -336
  544. agno/storage/session/__init__.py +0 -16
  545. agno/storage/session/agent.py +0 -64
  546. agno/storage/session/team.py +0 -63
  547. agno/storage/session/v2/__init__.py +0 -5
  548. agno/storage/session/workflow.py +0 -61
  549. agno/storage/singlestore.py +0 -606
  550. agno/storage/sqlite.py +0 -646
  551. agno/storage/workflow/__init__.py +0 -0
  552. agno/storage/workflow/mongodb.py +0 -1
  553. agno/storage/workflow/postgres.py +0 -1
  554. agno/storage/workflow/sqlite.py +0 -1
  555. agno/storage/yaml.py +0 -241
  556. agno/tools/thinking.py +0 -73
  557. agno/utils/defaults.py +0 -57
  558. agno/utils/filesystem.py +0 -39
  559. agno/utils/git.py +0 -52
  560. agno/utils/json_io.py +0 -30
  561. agno/utils/load_env.py +0 -19
  562. agno/utils/py_io.py +0 -19
  563. agno/utils/pyproject.py +0 -18
  564. agno/utils/resource_filter.py +0 -31
  565. agno/workflow/v2/__init__.py +0 -21
  566. agno/workflow/v2/types.py +0 -357
  567. agno/workflow/v2/workflow.py +0 -3312
  568. agno/workspace/__init__.py +0 -0
  569. agno/workspace/config.py +0 -325
  570. agno/workspace/enums.py +0 -6
  571. agno/workspace/helpers.py +0 -52
  572. agno/workspace/operator.py +0 -757
  573. agno/workspace/settings.py +0 -158
  574. agno-1.8.1.dist-info/METADATA +0 -982
  575. agno-1.8.1.dist-info/RECORD +0 -566
  576. agno-1.8.1.dist-info/entry_points.txt +0 -3
  577. agno-1.8.1.dist-info/licenses/LICENSE +0 -375
  578. /agno/{app → db/migrations}/__init__.py +0 -0
  579. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  580. /agno/{cli → integrations}/__init__.py +0 -0
  581. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  582. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  583. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  584. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  585. /agno/{app → os/interfaces}/slack/security.py +0 -0
  586. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  587. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  588. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  589. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
  590. {agno-1.8.1.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