agno 1.8.0__py3-none-any.whl → 2.0.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (583) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +2781 -4126
  4. agno/api/agent.py +9 -65
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +6 -17
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -41
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +5 -21
  11. agno/api/schemas/evals.py +7 -16
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +5 -21
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +11 -7
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +9 -64
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/db/__init__.py +24 -0
  25. agno/db/base.py +245 -0
  26. agno/db/dynamo/__init__.py +3 -0
  27. agno/db/dynamo/dynamo.py +1749 -0
  28. agno/db/dynamo/schemas.py +278 -0
  29. agno/db/dynamo/utils.py +684 -0
  30. agno/db/firestore/__init__.py +3 -0
  31. agno/db/firestore/firestore.py +1438 -0
  32. agno/db/firestore/schemas.py +130 -0
  33. agno/db/firestore/utils.py +278 -0
  34. agno/db/gcs_json/__init__.py +3 -0
  35. agno/db/gcs_json/gcs_json_db.py +1001 -0
  36. agno/db/gcs_json/utils.py +194 -0
  37. agno/db/in_memory/__init__.py +3 -0
  38. agno/db/in_memory/in_memory_db.py +888 -0
  39. agno/db/in_memory/utils.py +172 -0
  40. agno/db/json/__init__.py +3 -0
  41. agno/db/json/json_db.py +1051 -0
  42. agno/db/json/utils.py +196 -0
  43. agno/db/migrations/v1_to_v2.py +162 -0
  44. agno/db/mongo/__init__.py +3 -0
  45. agno/db/mongo/mongo.py +1417 -0
  46. agno/db/mongo/schemas.py +77 -0
  47. agno/db/mongo/utils.py +204 -0
  48. agno/db/mysql/__init__.py +3 -0
  49. agno/db/mysql/mysql.py +1719 -0
  50. agno/db/mysql/schemas.py +124 -0
  51. agno/db/mysql/utils.py +298 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1720 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +281 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1371 -0
  58. agno/db/redis/schemas.py +109 -0
  59. agno/db/redis/utils.py +288 -0
  60. agno/db/schemas/__init__.py +3 -0
  61. agno/db/schemas/evals.py +33 -0
  62. agno/db/schemas/knowledge.py +40 -0
  63. agno/db/schemas/memory.py +46 -0
  64. agno/db/singlestore/__init__.py +3 -0
  65. agno/db/singlestore/schemas.py +116 -0
  66. agno/db/singlestore/singlestore.py +1722 -0
  67. agno/db/singlestore/utils.py +327 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1680 -0
  71. agno/db/sqlite/utils.py +269 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +142 -43
  75. agno/eval/performance.py +88 -23
  76. agno/eval/reliability.py +73 -20
  77. agno/eval/utils.py +23 -13
  78. agno/integrations/discord/__init__.py +3 -0
  79. agno/{app → integrations}/discord/client.py +10 -10
  80. agno/knowledge/__init__.py +2 -2
  81. agno/{document → knowledge}/chunking/agentic.py +2 -2
  82. agno/{document → knowledge}/chunking/document.py +2 -2
  83. agno/{document → knowledge}/chunking/fixed.py +3 -3
  84. agno/{document → knowledge}/chunking/markdown.py +2 -2
  85. agno/{document → knowledge}/chunking/recursive.py +2 -2
  86. agno/{document → knowledge}/chunking/row.py +2 -2
  87. agno/knowledge/chunking/semantic.py +59 -0
  88. agno/knowledge/chunking/strategy.py +121 -0
  89. agno/knowledge/content.py +74 -0
  90. agno/knowledge/document/__init__.py +5 -0
  91. agno/{document → knowledge/document}/base.py +12 -2
  92. agno/knowledge/embedder/__init__.py +5 -0
  93. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  94. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  95. agno/{embedder → knowledge/embedder}/base.py +6 -0
  96. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  97. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  98. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  99. agno/{embedder → knowledge/embedder}/google.py +74 -1
  100. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  101. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  102. agno/knowledge/embedder/langdb.py +22 -0
  103. agno/knowledge/embedder/mistral.py +139 -0
  104. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  105. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  106. agno/knowledge/embedder/openai.py +223 -0
  107. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  108. agno/{embedder → knowledge/embedder}/together.py +1 -1
  109. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  110. agno/knowledge/knowledge.py +1515 -0
  111. agno/knowledge/reader/__init__.py +7 -0
  112. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  113. agno/knowledge/reader/base.py +88 -0
  114. agno/{document → knowledge}/reader/csv_reader.py +68 -15
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/knowledge/reader/gcs_reader.py +67 -0
  118. agno/{document → knowledge}/reader/json_reader.py +30 -9
  119. agno/{document → knowledge}/reader/markdown_reader.py +36 -9
  120. agno/{document → knowledge}/reader/pdf_reader.py +79 -21
  121. agno/knowledge/reader/reader_factory.py +275 -0
  122. agno/knowledge/reader/s3_reader.py +171 -0
  123. agno/{document → knowledge}/reader/text_reader.py +31 -10
  124. agno/knowledge/reader/url_reader.py +84 -0
  125. agno/knowledge/reader/web_search_reader.py +389 -0
  126. agno/{document → knowledge}/reader/website_reader.py +37 -10
  127. agno/knowledge/reader/wikipedia_reader.py +59 -0
  128. agno/knowledge/reader/youtube_reader.py +78 -0
  129. agno/knowledge/remote_content/remote_content.py +88 -0
  130. agno/{reranker → knowledge/reranker}/base.py +1 -1
  131. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  132. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  133. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  134. agno/knowledge/types.py +30 -0
  135. agno/knowledge/utils.py +169 -0
  136. agno/media.py +2 -2
  137. agno/memory/__init__.py +2 -10
  138. agno/memory/manager.py +1003 -148
  139. agno/models/aimlapi/__init__.py +2 -2
  140. agno/models/aimlapi/aimlapi.py +6 -6
  141. agno/models/anthropic/claude.py +129 -82
  142. agno/models/aws/bedrock.py +107 -175
  143. agno/models/aws/claude.py +64 -18
  144. agno/models/azure/ai_foundry.py +73 -23
  145. agno/models/base.py +347 -287
  146. agno/models/cerebras/cerebras.py +84 -27
  147. agno/models/cohere/chat.py +106 -98
  148. agno/models/dashscope/dashscope.py +14 -5
  149. agno/models/google/gemini.py +123 -53
  150. agno/models/groq/groq.py +97 -35
  151. agno/models/huggingface/huggingface.py +92 -27
  152. agno/models/ibm/watsonx.py +72 -13
  153. agno/models/litellm/chat.py +85 -13
  154. agno/models/message.py +38 -144
  155. agno/models/meta/llama.py +85 -49
  156. agno/models/metrics.py +120 -0
  157. agno/models/mistral/mistral.py +90 -21
  158. agno/models/ollama/__init__.py +0 -2
  159. agno/models/ollama/chat.py +84 -46
  160. agno/models/openai/chat.py +135 -27
  161. agno/models/openai/responses.py +233 -115
  162. agno/models/perplexity/perplexity.py +26 -2
  163. agno/models/portkey/portkey.py +0 -7
  164. agno/models/response.py +14 -8
  165. agno/models/utils.py +20 -0
  166. agno/models/vercel/__init__.py +2 -2
  167. agno/models/vercel/v0.py +1 -1
  168. agno/models/vllm/__init__.py +2 -2
  169. agno/models/vllm/vllm.py +3 -3
  170. agno/models/xai/xai.py +10 -10
  171. agno/os/__init__.py +3 -0
  172. agno/os/app.py +393 -0
  173. agno/os/auth.py +47 -0
  174. agno/os/config.py +103 -0
  175. agno/os/interfaces/agui/__init__.py +3 -0
  176. agno/os/interfaces/agui/agui.py +31 -0
  177. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  178. agno/{app → os/interfaces}/agui/utils.py +65 -28
  179. agno/os/interfaces/base.py +21 -0
  180. agno/os/interfaces/slack/__init__.py +3 -0
  181. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  182. agno/os/interfaces/slack/slack.py +33 -0
  183. agno/os/interfaces/whatsapp/__init__.py +3 -0
  184. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  185. agno/os/interfaces/whatsapp/whatsapp.py +30 -0
  186. agno/os/router.py +843 -0
  187. agno/os/routers/__init__.py +3 -0
  188. agno/os/routers/evals/__init__.py +3 -0
  189. agno/os/routers/evals/evals.py +204 -0
  190. agno/os/routers/evals/schemas.py +142 -0
  191. agno/os/routers/evals/utils.py +161 -0
  192. agno/os/routers/knowledge/__init__.py +3 -0
  193. agno/os/routers/knowledge/knowledge.py +413 -0
  194. agno/os/routers/knowledge/schemas.py +118 -0
  195. agno/os/routers/memory/__init__.py +3 -0
  196. agno/os/routers/memory/memory.py +179 -0
  197. agno/os/routers/memory/schemas.py +58 -0
  198. agno/os/routers/metrics/__init__.py +3 -0
  199. agno/os/routers/metrics/metrics.py +58 -0
  200. agno/os/routers/metrics/schemas.py +47 -0
  201. agno/os/routers/session/__init__.py +3 -0
  202. agno/os/routers/session/session.py +163 -0
  203. agno/os/schema.py +892 -0
  204. agno/{app/playground → os}/settings.py +8 -15
  205. agno/os/utils.py +270 -0
  206. agno/reasoning/azure_ai_foundry.py +4 -4
  207. agno/reasoning/deepseek.py +4 -4
  208. agno/reasoning/default.py +6 -11
  209. agno/reasoning/groq.py +4 -4
  210. agno/reasoning/helpers.py +4 -6
  211. agno/reasoning/ollama.py +4 -4
  212. agno/reasoning/openai.py +4 -4
  213. agno/run/{response.py → agent.py} +144 -72
  214. agno/run/base.py +44 -58
  215. agno/run/cancel.py +83 -0
  216. agno/run/team.py +133 -77
  217. agno/run/workflow.py +537 -12
  218. agno/session/__init__.py +10 -0
  219. agno/session/agent.py +244 -0
  220. agno/session/summary.py +225 -0
  221. agno/session/team.py +262 -0
  222. agno/{storage/session/v2 → session}/workflow.py +47 -24
  223. agno/team/__init__.py +15 -16
  224. agno/team/team.py +2967 -4243
  225. agno/tools/agentql.py +14 -5
  226. agno/tools/airflow.py +9 -4
  227. agno/tools/api.py +7 -3
  228. agno/tools/apify.py +2 -46
  229. agno/tools/arxiv.py +8 -3
  230. agno/tools/aws_lambda.py +7 -5
  231. agno/tools/aws_ses.py +7 -1
  232. agno/tools/baidusearch.py +4 -1
  233. agno/tools/bitbucket.py +4 -4
  234. agno/tools/brandfetch.py +14 -11
  235. agno/tools/bravesearch.py +4 -1
  236. agno/tools/brightdata.py +42 -22
  237. agno/tools/browserbase.py +13 -4
  238. agno/tools/calcom.py +12 -10
  239. agno/tools/calculator.py +10 -27
  240. agno/tools/cartesia.py +18 -13
  241. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  242. agno/tools/confluence.py +71 -18
  243. agno/tools/crawl4ai.py +7 -1
  244. agno/tools/csv_toolkit.py +9 -8
  245. agno/tools/dalle.py +18 -11
  246. agno/tools/daytona.py +13 -16
  247. agno/tools/decorator.py +6 -3
  248. agno/tools/desi_vocal.py +16 -7
  249. agno/tools/discord.py +11 -8
  250. agno/tools/docker.py +30 -42
  251. agno/tools/duckdb.py +34 -53
  252. agno/tools/duckduckgo.py +8 -7
  253. agno/tools/e2b.py +62 -62
  254. agno/tools/eleven_labs.py +35 -28
  255. agno/tools/email.py +4 -1
  256. agno/tools/evm.py +7 -1
  257. agno/tools/exa.py +19 -14
  258. agno/tools/fal.py +29 -29
  259. agno/tools/file.py +9 -8
  260. agno/tools/financial_datasets.py +25 -44
  261. agno/tools/firecrawl.py +22 -22
  262. agno/tools/function.py +68 -17
  263. agno/tools/giphy.py +22 -10
  264. agno/tools/github.py +48 -126
  265. agno/tools/gmail.py +46 -62
  266. agno/tools/google_bigquery.py +7 -6
  267. agno/tools/google_maps.py +11 -26
  268. agno/tools/googlesearch.py +7 -2
  269. agno/tools/googlesheets.py +21 -17
  270. agno/tools/hackernews.py +9 -5
  271. agno/tools/jina.py +5 -4
  272. agno/tools/jira.py +18 -9
  273. agno/tools/knowledge.py +31 -32
  274. agno/tools/linear.py +18 -33
  275. agno/tools/linkup.py +5 -1
  276. agno/tools/local_file_system.py +8 -5
  277. agno/tools/lumalab.py +31 -19
  278. agno/tools/mem0.py +18 -12
  279. agno/tools/memori.py +14 -10
  280. agno/tools/mlx_transcribe.py +3 -2
  281. agno/tools/models/azure_openai.py +32 -14
  282. agno/tools/models/gemini.py +58 -31
  283. agno/tools/models/groq.py +29 -20
  284. agno/tools/models/nebius.py +27 -11
  285. agno/tools/models_labs.py +39 -15
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +134 -0
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +57 -26
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +62 -46
  293. agno/tools/openweather.py +14 -12
  294. agno/tools/pandas.py +11 -3
  295. agno/tools/postgres.py +4 -12
  296. agno/tools/pubmed.py +4 -1
  297. agno/tools/python.py +9 -22
  298. agno/tools/reasoning.py +35 -27
  299. agno/tools/reddit.py +11 -26
  300. agno/tools/replicate.py +54 -41
  301. agno/tools/resend.py +4 -1
  302. agno/tools/scrapegraph.py +15 -14
  303. agno/tools/searxng.py +10 -23
  304. agno/tools/serpapi.py +6 -3
  305. agno/tools/serper.py +13 -4
  306. agno/tools/shell.py +9 -2
  307. agno/tools/slack.py +12 -11
  308. agno/tools/sleep.py +3 -2
  309. agno/tools/spider.py +24 -4
  310. agno/tools/sql.py +7 -6
  311. agno/tools/tavily.py +6 -4
  312. agno/tools/telegram.py +12 -4
  313. agno/tools/todoist.py +11 -31
  314. agno/tools/toolkit.py +1 -1
  315. agno/tools/trafilatura.py +22 -6
  316. agno/tools/trello.py +9 -22
  317. agno/tools/twilio.py +10 -3
  318. agno/tools/user_control_flow.py +6 -1
  319. agno/tools/valyu.py +34 -5
  320. agno/tools/visualization.py +19 -28
  321. agno/tools/webbrowser.py +4 -3
  322. agno/tools/webex.py +11 -7
  323. agno/tools/website.py +15 -46
  324. agno/tools/webtools.py +12 -4
  325. agno/tools/whatsapp.py +5 -9
  326. agno/tools/wikipedia.py +20 -13
  327. agno/tools/x.py +14 -13
  328. agno/tools/yfinance.py +13 -40
  329. agno/tools/youtube.py +26 -20
  330. agno/tools/zendesk.py +7 -2
  331. agno/tools/zep.py +10 -7
  332. agno/tools/zoom.py +10 -9
  333. agno/utils/common.py +1 -19
  334. agno/utils/events.py +95 -118
  335. agno/utils/knowledge.py +29 -0
  336. agno/utils/location.py +2 -2
  337. agno/utils/log.py +2 -2
  338. agno/utils/mcp.py +11 -5
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/claude.py +6 -4
  342. agno/utils/models/mistral.py +8 -7
  343. agno/utils/models/schema_utils.py +3 -3
  344. agno/utils/pprint.py +33 -32
  345. agno/utils/print_response/agent.py +779 -0
  346. agno/utils/print_response/team.py +1565 -0
  347. agno/utils/print_response/workflow.py +1451 -0
  348. agno/utils/prompts.py +14 -14
  349. agno/utils/reasoning.py +87 -0
  350. agno/utils/response.py +42 -42
  351. agno/utils/string.py +8 -22
  352. agno/utils/team.py +50 -0
  353. agno/utils/timer.py +2 -2
  354. agno/vectordb/base.py +33 -21
  355. agno/vectordb/cassandra/cassandra.py +287 -23
  356. agno/vectordb/chroma/chromadb.py +482 -59
  357. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  358. agno/vectordb/couchbase/couchbase.py +309 -29
  359. agno/vectordb/lancedb/lance_db.py +360 -21
  360. agno/vectordb/langchaindb/__init__.py +5 -0
  361. agno/vectordb/langchaindb/langchaindb.py +145 -0
  362. agno/vectordb/lightrag/__init__.py +5 -0
  363. agno/vectordb/lightrag/lightrag.py +374 -0
  364. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  365. agno/vectordb/milvus/milvus.py +242 -32
  366. agno/vectordb/mongodb/mongodb.py +200 -24
  367. agno/vectordb/pgvector/pgvector.py +319 -37
  368. agno/vectordb/pineconedb/pineconedb.py +221 -27
  369. agno/vectordb/qdrant/qdrant.py +356 -14
  370. agno/vectordb/singlestore/singlestore.py +286 -29
  371. agno/vectordb/surrealdb/surrealdb.py +187 -7
  372. agno/vectordb/upstashdb/upstashdb.py +342 -26
  373. agno/vectordb/weaviate/weaviate.py +227 -165
  374. agno/workflow/__init__.py +17 -13
  375. agno/workflow/{v2/condition.py → condition.py} +135 -32
  376. agno/workflow/{v2/loop.py → loop.py} +115 -28
  377. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  378. agno/workflow/{v2/router.py → router.py} +133 -32
  379. agno/workflow/{v2/step.py → step.py} +200 -42
  380. agno/workflow/{v2/steps.py → steps.py} +147 -66
  381. agno/workflow/types.py +482 -0
  382. agno/workflow/workflow.py +2394 -696
  383. agno-2.0.0a1.dist-info/METADATA +355 -0
  384. agno-2.0.0a1.dist-info/RECORD +514 -0
  385. agno/agent/metrics.py +0 -107
  386. agno/api/app.py +0 -35
  387. agno/api/playground.py +0 -92
  388. agno/api/schemas/app.py +0 -12
  389. agno/api/schemas/playground.py +0 -22
  390. agno/api/schemas/user.py +0 -35
  391. agno/api/schemas/workspace.py +0 -46
  392. agno/api/user.py +0 -160
  393. agno/api/workflows.py +0 -33
  394. agno/api/workspace.py +0 -175
  395. agno/app/agui/__init__.py +0 -3
  396. agno/app/agui/app.py +0 -17
  397. agno/app/agui/sync_router.py +0 -120
  398. agno/app/base.py +0 -186
  399. agno/app/discord/__init__.py +0 -3
  400. agno/app/fastapi/__init__.py +0 -3
  401. agno/app/fastapi/app.py +0 -107
  402. agno/app/fastapi/async_router.py +0 -457
  403. agno/app/fastapi/sync_router.py +0 -448
  404. agno/app/playground/app.py +0 -228
  405. agno/app/playground/async_router.py +0 -1050
  406. agno/app/playground/deploy.py +0 -249
  407. agno/app/playground/operator.py +0 -183
  408. agno/app/playground/schemas.py +0 -220
  409. agno/app/playground/serve.py +0 -55
  410. agno/app/playground/sync_router.py +0 -1042
  411. agno/app/playground/utils.py +0 -46
  412. agno/app/settings.py +0 -15
  413. agno/app/slack/__init__.py +0 -3
  414. agno/app/slack/app.py +0 -19
  415. agno/app/slack/sync_router.py +0 -92
  416. agno/app/utils.py +0 -54
  417. agno/app/whatsapp/__init__.py +0 -3
  418. agno/app/whatsapp/app.py +0 -15
  419. agno/app/whatsapp/sync_router.py +0 -197
  420. agno/cli/auth_server.py +0 -249
  421. agno/cli/config.py +0 -274
  422. agno/cli/console.py +0 -88
  423. agno/cli/credentials.py +0 -23
  424. agno/cli/entrypoint.py +0 -571
  425. agno/cli/operator.py +0 -357
  426. agno/cli/settings.py +0 -96
  427. agno/cli/ws/ws_cli.py +0 -817
  428. agno/constants.py +0 -13
  429. agno/document/__init__.py +0 -5
  430. agno/document/chunking/semantic.py +0 -45
  431. agno/document/chunking/strategy.py +0 -31
  432. agno/document/reader/__init__.py +0 -5
  433. agno/document/reader/base.py +0 -47
  434. agno/document/reader/docx_reader.py +0 -60
  435. agno/document/reader/gcs/pdf_reader.py +0 -44
  436. agno/document/reader/s3/pdf_reader.py +0 -59
  437. agno/document/reader/s3/text_reader.py +0 -63
  438. agno/document/reader/url_reader.py +0 -59
  439. agno/document/reader/youtube_reader.py +0 -58
  440. agno/embedder/__init__.py +0 -5
  441. agno/embedder/langdb.py +0 -80
  442. agno/embedder/mistral.py +0 -82
  443. agno/embedder/openai.py +0 -78
  444. agno/file/__init__.py +0 -5
  445. agno/file/file.py +0 -16
  446. agno/file/local/csv.py +0 -32
  447. agno/file/local/txt.py +0 -19
  448. agno/infra/app.py +0 -240
  449. agno/infra/base.py +0 -144
  450. agno/infra/context.py +0 -20
  451. agno/infra/db_app.py +0 -52
  452. agno/infra/resource.py +0 -205
  453. agno/infra/resources.py +0 -55
  454. agno/knowledge/agent.py +0 -698
  455. agno/knowledge/arxiv.py +0 -33
  456. agno/knowledge/combined.py +0 -36
  457. agno/knowledge/csv.py +0 -144
  458. agno/knowledge/csv_url.py +0 -124
  459. agno/knowledge/document.py +0 -223
  460. agno/knowledge/docx.py +0 -137
  461. agno/knowledge/firecrawl.py +0 -34
  462. agno/knowledge/gcs/__init__.py +0 -0
  463. agno/knowledge/gcs/base.py +0 -39
  464. agno/knowledge/gcs/pdf.py +0 -125
  465. agno/knowledge/json.py +0 -137
  466. agno/knowledge/langchain.py +0 -71
  467. agno/knowledge/light_rag.py +0 -273
  468. agno/knowledge/llamaindex.py +0 -66
  469. agno/knowledge/markdown.py +0 -154
  470. agno/knowledge/pdf.py +0 -164
  471. agno/knowledge/pdf_bytes.py +0 -42
  472. agno/knowledge/pdf_url.py +0 -148
  473. agno/knowledge/s3/__init__.py +0 -0
  474. agno/knowledge/s3/base.py +0 -64
  475. agno/knowledge/s3/pdf.py +0 -33
  476. agno/knowledge/s3/text.py +0 -34
  477. agno/knowledge/text.py +0 -141
  478. agno/knowledge/url.py +0 -46
  479. agno/knowledge/website.py +0 -179
  480. agno/knowledge/wikipedia.py +0 -32
  481. agno/knowledge/youtube.py +0 -35
  482. agno/memory/agent.py +0 -423
  483. agno/memory/classifier.py +0 -104
  484. agno/memory/db/__init__.py +0 -5
  485. agno/memory/db/base.py +0 -42
  486. agno/memory/db/mongodb.py +0 -189
  487. agno/memory/db/postgres.py +0 -203
  488. agno/memory/db/sqlite.py +0 -193
  489. agno/memory/memory.py +0 -22
  490. agno/memory/row.py +0 -36
  491. agno/memory/summarizer.py +0 -201
  492. agno/memory/summary.py +0 -19
  493. agno/memory/team.py +0 -415
  494. agno/memory/v2/__init__.py +0 -2
  495. agno/memory/v2/db/__init__.py +0 -1
  496. agno/memory/v2/db/base.py +0 -42
  497. agno/memory/v2/db/firestore.py +0 -339
  498. agno/memory/v2/db/mongodb.py +0 -196
  499. agno/memory/v2/db/postgres.py +0 -214
  500. agno/memory/v2/db/redis.py +0 -187
  501. agno/memory/v2/db/schema.py +0 -54
  502. agno/memory/v2/db/sqlite.py +0 -209
  503. agno/memory/v2/manager.py +0 -437
  504. agno/memory/v2/memory.py +0 -1097
  505. agno/memory/v2/schema.py +0 -55
  506. agno/memory/v2/summarizer.py +0 -215
  507. agno/memory/workflow.py +0 -38
  508. agno/models/ollama/tools.py +0 -430
  509. agno/models/qwen/__init__.py +0 -5
  510. agno/playground/__init__.py +0 -10
  511. agno/playground/deploy.py +0 -3
  512. agno/playground/playground.py +0 -3
  513. agno/playground/serve.py +0 -3
  514. agno/playground/settings.py +0 -3
  515. agno/reranker/__init__.py +0 -0
  516. agno/run/v2/__init__.py +0 -0
  517. agno/run/v2/workflow.py +0 -567
  518. agno/storage/__init__.py +0 -0
  519. agno/storage/agent/__init__.py +0 -0
  520. agno/storage/agent/dynamodb.py +0 -1
  521. agno/storage/agent/json.py +0 -1
  522. agno/storage/agent/mongodb.py +0 -1
  523. agno/storage/agent/postgres.py +0 -1
  524. agno/storage/agent/singlestore.py +0 -1
  525. agno/storage/agent/sqlite.py +0 -1
  526. agno/storage/agent/yaml.py +0 -1
  527. agno/storage/base.py +0 -60
  528. agno/storage/dynamodb.py +0 -673
  529. agno/storage/firestore.py +0 -297
  530. agno/storage/gcs_json.py +0 -261
  531. agno/storage/in_memory.py +0 -234
  532. agno/storage/json.py +0 -237
  533. agno/storage/mongodb.py +0 -328
  534. agno/storage/mysql.py +0 -685
  535. agno/storage/postgres.py +0 -682
  536. agno/storage/redis.py +0 -336
  537. agno/storage/session/__init__.py +0 -16
  538. agno/storage/session/agent.py +0 -64
  539. agno/storage/session/team.py +0 -63
  540. agno/storage/session/v2/__init__.py +0 -5
  541. agno/storage/session/workflow.py +0 -61
  542. agno/storage/singlestore.py +0 -606
  543. agno/storage/sqlite.py +0 -646
  544. agno/storage/workflow/__init__.py +0 -0
  545. agno/storage/workflow/mongodb.py +0 -1
  546. agno/storage/workflow/postgres.py +0 -1
  547. agno/storage/workflow/sqlite.py +0 -1
  548. agno/storage/yaml.py +0 -241
  549. agno/tools/thinking.py +0 -73
  550. agno/utils/defaults.py +0 -57
  551. agno/utils/filesystem.py +0 -39
  552. agno/utils/git.py +0 -52
  553. agno/utils/json_io.py +0 -30
  554. agno/utils/load_env.py +0 -19
  555. agno/utils/py_io.py +0 -19
  556. agno/utils/pyproject.py +0 -18
  557. agno/utils/resource_filter.py +0 -31
  558. agno/workflow/v2/__init__.py +0 -21
  559. agno/workflow/v2/types.py +0 -357
  560. agno/workflow/v2/workflow.py +0 -3312
  561. agno/workspace/__init__.py +0 -0
  562. agno/workspace/config.py +0 -325
  563. agno/workspace/enums.py +0 -6
  564. agno/workspace/helpers.py +0 -52
  565. agno/workspace/operator.py +0 -757
  566. agno/workspace/settings.py +0 -158
  567. agno-1.8.0.dist-info/METADATA +0 -979
  568. agno-1.8.0.dist-info/RECORD +0 -565
  569. agno-1.8.0.dist-info/entry_points.txt +0 -3
  570. /agno/{app → db/migrations}/__init__.py +0 -0
  571. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  572. /agno/{cli → integrations}/__init__.py +0 -0
  573. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  574. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  575. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  576. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  577. /agno/{app → os/interfaces}/slack/security.py +0 -0
  578. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  579. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  580. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  581. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
  582. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
  583. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
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, AudioArtifact, AudioResponse, File, Image, ImageArtifact, Video, VideoArtifact
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
74
+ audio_output: Optional[Union[AudioResponse, AudioArtifact]] = None
188
75
  image_output: Optional[ImageArtifact] = None
76
+ video_output: Optional[VideoArtifact] = 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)
@@ -359,42 +249,46 @@ class Message(BaseModel):
359
249
  _logger(f"Files added: {len(self.files)}")
360
250
 
361
251
  metrics_header = " TOOL METRICS " if self.role == "tool" else " METRICS "
362
- if metrics and self.metrics is not None and self.metrics != MessageMetrics():
252
+ if metrics and self.metrics is not None and self.metrics != Metrics():
363
253
  _logger(metrics_header, center=True, symbol="*")
364
254
 
365
- # Combine token metrics into a single line
255
+ # Token metrics
366
256
  token_metrics = []
367
- if self.metrics.input_tokens:
257
+ if self.metrics.input_tokens and self.metrics.input_tokens > 0:
368
258
  token_metrics.append(f"input={self.metrics.input_tokens}")
369
- if self.metrics.output_tokens:
259
+ if self.metrics.output_tokens and self.metrics.output_tokens > 0:
370
260
  token_metrics.append(f"output={self.metrics.output_tokens}")
371
- if self.metrics.total_tokens:
261
+ if self.metrics.total_tokens and self.metrics.total_tokens > 0:
372
262
  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:
263
+ if self.metrics.cache_read_tokens and self.metrics.cache_read_tokens > 0:
264
+ token_metrics.append(f"cached={self.metrics.cache_read_tokens}")
265
+ if self.metrics.cache_write_tokens and self.metrics.cache_write_tokens > 0:
376
266
  token_metrics.append(f"cache_write_tokens={self.metrics.cache_write_tokens}")
377
- if self.metrics.reasoning_tokens:
267
+ if self.metrics.reasoning_tokens and self.metrics.reasoning_tokens > 0:
378
268
  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}")
269
+ if self.metrics.audio_total_tokens and self.metrics.audio_total_tokens > 0:
270
+ token_metrics.append(f"audio={self.metrics.audio_total_tokens}")
381
271
  if token_metrics:
382
272
  _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:
273
+
274
+ # Time related metrics
275
+ if self.metrics.duration is not None and self.metrics.duration > 0:
276
+ _logger(f"* Duration: {self.metrics.duration:.4f}s")
277
+ if self.metrics.output_tokens and self.metrics.duration and self.metrics.duration > 0:
278
+ _logger(
279
+ f"* Tokens per second: {self.metrics.output_tokens / self.metrics.duration:.4f} tokens/s"
280
+ )
281
+ if self.metrics.time_to_first_token is not None and self.metrics.time_to_first_token > 0:
392
282
  _logger(f"* Time to first token: {self.metrics.time_to_first_token:.4f}s")
283
+
284
+ # Non-generic metrics
285
+ if self.metrics.provider_metrics:
286
+ _logger(f"* Provider metrics: {self.metrics.provider_metrics}")
393
287
  if self.metrics.additional_metrics:
394
288
  _logger(f"* Additional metrics: {self.metrics.additional_metrics}")
289
+
395
290
  _logger(metrics_header, center=True, symbol="*")
396
291
 
397
292
  def content_is_valid(self) -> bool:
398
293
  """Check if the message content is valid."""
399
-
400
294
  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