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
@@ -0,0 +1,684 @@
1
+ import json
2
+ import time
3
+ from datetime import date, datetime, timedelta, timezone
4
+ from typing import Any, Callable, Dict, List, Optional, Union
5
+ from uuid import uuid4
6
+
7
+ from agno.db.base import SessionType
8
+ from agno.db.schemas.evals import EvalRunRecord
9
+ from agno.db.schemas.knowledge import KnowledgeRow
10
+ from agno.session import Session
11
+ from agno.utils.log import log_debug, log_error, log_info
12
+
13
+ # -- Serialization utils --
14
+
15
+
16
+ def serialize_to_dynamo_item(data: Dict[str, Any]) -> Dict[str, Any]:
17
+ """Serialize the given dict to a valid DynamoDB item
18
+
19
+ Args:
20
+ data: The dict to serialize
21
+
22
+ Returns:
23
+ A DynamoDB-ready dict with the serialized data
24
+
25
+ """
26
+ item = {}
27
+ for key, value in data.items():
28
+ if value is not None:
29
+ if isinstance(value, (int, float)):
30
+ item[key] = {"N": str(value)}
31
+ elif isinstance(value, str):
32
+ item[key] = {"S": value}
33
+ elif isinstance(value, bool):
34
+ item[key] = {"BOOL": str(value)}
35
+ elif isinstance(value, (dict, list)):
36
+ item[key] = {"S": json.dumps(value)}
37
+ else:
38
+ item[key] = {"S": str(value)}
39
+ return item
40
+
41
+
42
+ def deserialize_from_dynamodb_item(item: Dict[str, Any]) -> Dict[str, Any]:
43
+ data = {}
44
+ for key, value in item.items():
45
+ if "S" in value:
46
+ try:
47
+ data[key] = json.loads(value["S"])
48
+ except (json.JSONDecodeError, TypeError):
49
+ data[key] = value["S"]
50
+ elif "N" in value:
51
+ data[key] = float(value["N"]) if "." in value["N"] else int(value["N"])
52
+ elif "BOOL" in value:
53
+ data[key] = value["BOOL"]
54
+ elif "SS" in value:
55
+ data[key] = value["SS"]
56
+ elif "NS" in value:
57
+ data[key] = [float(n) if "." in n else int(n) for n in value["NS"]]
58
+ elif "M" in value:
59
+ data[key] = deserialize_from_dynamodb_item(value["M"])
60
+ elif "L" in value:
61
+ data[key] = [deserialize_from_dynamodb_item({"item": item})["item"] for item in value["L"]]
62
+ return data
63
+
64
+
65
+ def serialize_knowledge_row(knowledge: KnowledgeRow) -> Dict[str, Any]:
66
+ """Convert KnowledgeRow to DynamoDB item format."""
67
+ return serialize_to_dynamo_item(
68
+ {
69
+ "id": knowledge.id,
70
+ "name": knowledge.name,
71
+ "description": knowledge.description,
72
+ "type": getattr(knowledge, "type", None),
73
+ "status": getattr(knowledge, "status", None),
74
+ "status_message": getattr(knowledge, "status_message", None),
75
+ "metadata": getattr(knowledge, "metadata", None),
76
+ "size": getattr(knowledge, "size", None),
77
+ "linked_to": getattr(knowledge, "linked_to", None),
78
+ "access_count": getattr(knowledge, "access_count", None),
79
+ "created_at": int(knowledge.created_at) if knowledge.created_at else None,
80
+ "updated_at": int(knowledge.updated_at) if knowledge.updated_at else None,
81
+ }
82
+ )
83
+
84
+
85
+ def deserialize_knowledge_row(item: Dict[str, Any]) -> KnowledgeRow:
86
+ """Convert DynamoDB item to KnowledgeRow."""
87
+ data = deserialize_from_dynamodb_item(item)
88
+ return KnowledgeRow(
89
+ id=data["id"],
90
+ name=data["name"],
91
+ description=data["description"],
92
+ metadata=data.get("metadata"),
93
+ type=data.get("type"),
94
+ size=data.get("size"),
95
+ linked_to=data.get("linked_to"),
96
+ access_count=data.get("access_count"),
97
+ status=data.get("status"),
98
+ status_message=data.get("status_message"),
99
+ created_at=data.get("created_at"),
100
+ updated_at=data.get("updated_at"),
101
+ )
102
+
103
+
104
+ def serialize_eval_record(eval_record: EvalRunRecord) -> Dict[str, Any]:
105
+ """Convert EvalRunRecord to DynamoDB item format."""
106
+ return serialize_to_dynamo_item(
107
+ {
108
+ "run_id": eval_record.run_id,
109
+ "eval_type": eval_record.eval_type,
110
+ "eval_data": eval_record.eval_data,
111
+ "name": getattr(eval_record, "name", None),
112
+ "agent_id": getattr(eval_record, "agent_id", None),
113
+ "team_id": getattr(eval_record, "team_id", None),
114
+ "workflow_id": getattr(eval_record, "workflow_id", None),
115
+ "model_id": getattr(eval_record, "model_id", None),
116
+ "model_provider": getattr(eval_record, "model_provider", None),
117
+ "evaluated_component_name": getattr(eval_record, "evaluated_component_name", None),
118
+ }
119
+ )
120
+
121
+
122
+ def deserialize_eval_record(item: Dict[str, Any]) -> EvalRunRecord:
123
+ """Convert DynamoDB item to EvalRunRecord."""
124
+ data = deserialize_from_dynamodb_item(item)
125
+ # Convert timestamp fields back to datetime
126
+ if "created_at" in data and data["created_at"]:
127
+ data["created_at"] = datetime.fromtimestamp(data["created_at"], tz=timezone.utc)
128
+ if "updated_at" in data and data["updated_at"]:
129
+ data["updated_at"] = datetime.fromtimestamp(data["updated_at"], tz=timezone.utc)
130
+ return EvalRunRecord(run_id=data["run_id"], eval_type=data["eval_type"], eval_data=data["eval_data"])
131
+
132
+
133
+ # -- DB Utils --
134
+
135
+
136
+ def create_table_if_not_exists(dynamodb_client, table_name: str, schema: Dict[str, Any]) -> bool:
137
+ """Create DynamoDB table if it doesn't exist."""
138
+ try:
139
+ dynamodb_client.describe_table(TableName=table_name)
140
+ return True
141
+
142
+ except dynamodb_client.exceptions.ResourceNotFoundException:
143
+ log_info(f"Creating table {table_name}")
144
+ try:
145
+ dynamodb_client.create_table(**schema)
146
+ # Wait for table to be created
147
+ waiter = dynamodb_client.get_waiter("table_exists")
148
+ waiter.wait(TableName=table_name)
149
+
150
+ log_debug(f"Table {table_name} created successfully")
151
+
152
+ return True
153
+
154
+ except Exception as e:
155
+ log_error(f"Failed to create table {table_name}: {e}")
156
+ return False
157
+
158
+
159
+ def apply_pagination(
160
+ items: List[Dict[str, Any]], limit: Optional[int] = None, page: Optional[int] = None
161
+ ) -> List[Dict[str, Any]]:
162
+ """Apply pagination to a list of items."""
163
+ if limit is None:
164
+ return items
165
+
166
+ start_index = 0
167
+ if page is not None and page > 1:
168
+ start_index = (page - 1) * limit
169
+
170
+ return items[start_index : start_index + limit]
171
+
172
+
173
+ def apply_sorting(
174
+ items: List[Dict[str, Any]], sort_by: Optional[str] = None, sort_order: Optional[str] = None
175
+ ) -> List[Dict[str, Any]]:
176
+ """Apply sorting to a list of items."""
177
+ if sort_by is None:
178
+ sort_by = "created_at"
179
+
180
+ reverse = sort_order == "desc"
181
+
182
+ return sorted(items, key=lambda x: x.get(sort_by, ""), reverse=reverse)
183
+
184
+
185
+ # -- Session utils --
186
+
187
+
188
+ def prepare_session_data(session: "Session") -> Dict[str, Any]:
189
+ """Prepare session data for storage by serializing JSON fields and setting session type."""
190
+ from agno.session import AgentSession, TeamSession, WorkflowSession
191
+
192
+ serialized_session = session.to_dict()
193
+
194
+ # Handle JSON fields
195
+ json_fields = ["session_data", "memory", "tools", "functions", "additional_data"]
196
+ for field in json_fields:
197
+ if field in serialized_session and serialized_session[field] is not None:
198
+ if isinstance(serialized_session[field], (dict, list)):
199
+ serialized_session[field] = json.dumps(serialized_session[field])
200
+
201
+ # Set the session type
202
+ if isinstance(session, AgentSession):
203
+ serialized_session["session_type"] = SessionType.AGENT.value
204
+ elif isinstance(session, TeamSession):
205
+ serialized_session["session_type"] = SessionType.TEAM.value
206
+ elif isinstance(session, WorkflowSession):
207
+ serialized_session["session_type"] = SessionType.WORKFLOW.value
208
+
209
+ return serialized_session
210
+
211
+
212
+ def merge_with_existing_session(new_session: Dict[str, Any], existing_item: Dict[str, Any]) -> Dict[str, Any]:
213
+ """Merge new session data with existing session, preserving important fields."""
214
+ existing_session = deserialize_from_dynamodb_item(existing_item)
215
+
216
+ # Start with existing session as base
217
+ merged_session = existing_session.copy()
218
+
219
+ if "session_data" in new_session:
220
+ merged_session_data = merge_session_data(
221
+ existing_session.get("session_data", {}), new_session.get("session_data", {})
222
+ )
223
+ merged_session["session_data"] = json.dumps(merged_session_data)
224
+
225
+ for key, value in new_session.items():
226
+ if key != "created_at" and key != "session_data" and value is not None:
227
+ merged_session[key] = value
228
+
229
+ # Always preserve created_at and set updated_at
230
+ merged_session["created_at"] = existing_session.get("created_at")
231
+ merged_session["updated_at"] = int(time.time())
232
+
233
+ return merged_session
234
+
235
+
236
+ def merge_session_data(existing_data: Any, new_data: Any) -> Dict[str, Any]:
237
+ """Merge session_data fields, handling JSON string conversion."""
238
+
239
+ # Parse existing session_data
240
+ if isinstance(existing_data, str):
241
+ existing_data = json.loads(existing_data)
242
+ existing_data = existing_data or {}
243
+
244
+ # Parse new session_data
245
+ if isinstance(new_data, str):
246
+ new_data = json.loads(new_data)
247
+ new_data = new_data or {}
248
+
249
+ # Merge letting new data take precedence
250
+ return {**existing_data, **new_data}
251
+
252
+
253
+ def deserialize_session_result(
254
+ serialized_session: Dict[str, Any], original_session: "Session", deserialize: Optional[bool]
255
+ ) -> Optional[Union["Session", Dict[str, Any]]]:
256
+ """Deserialize the session result based on the deserialize flag and session type."""
257
+ from agno.session import AgentSession, TeamSession, WorkflowSession
258
+
259
+ if not deserialize:
260
+ return serialized_session
261
+
262
+ if isinstance(original_session, AgentSession):
263
+ return AgentSession.from_dict(serialized_session)
264
+ elif isinstance(original_session, TeamSession):
265
+ return TeamSession.from_dict(serialized_session)
266
+ elif isinstance(original_session, WorkflowSession):
267
+ return WorkflowSession.from_dict(serialized_session)
268
+
269
+ return None
270
+
271
+
272
+ def deserialize_session(session: Dict[str, Any]) -> Optional[Session]:
273
+ """Deserialize session data from DynamoDB format to Session object."""
274
+ try:
275
+ deserialized = session.copy()
276
+
277
+ # Handle JSON fields
278
+ json_fields = ["session_data", "memory", "tools", "functions", "additional_data"]
279
+ for field in json_fields:
280
+ if field in deserialized and deserialized[field] is not None:
281
+ if isinstance(deserialized[field], str):
282
+ try:
283
+ deserialized[field] = json.loads(deserialized[field])
284
+ except json.JSONDecodeError:
285
+ log_error(f"Failed to deserialize {field} field")
286
+ deserialized[field] = None
287
+
288
+ # Handle timestamp fields
289
+ for field in ["created_at", "updated_at"]:
290
+ if field in deserialized and deserialized[field] is not None:
291
+ if isinstance(deserialized[field], (int, float)):
292
+ deserialized[field] = datetime.fromtimestamp(deserialized[field], tz=timezone.utc)
293
+ elif isinstance(deserialized[field], str):
294
+ try:
295
+ deserialized[field] = datetime.fromisoformat(deserialized[field])
296
+ except ValueError:
297
+ deserialized[field] = datetime.fromtimestamp(float(deserialized[field]), tz=timezone.utc)
298
+
299
+ return Session.from_dict(deserialized) # type: ignore
300
+
301
+ except Exception as e:
302
+ log_error(f"Failed to deserialize session: {e}")
303
+ return None
304
+
305
+
306
+ # -- Metrics utils --
307
+
308
+
309
+ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
310
+ """Calculate metrics for the given single date.
311
+ Args:
312
+ date_to_process (date): The date to calculate metrics for.
313
+ sessions_data (dict): The sessions data to calculate metrics for.
314
+ Returns:
315
+ dict: The calculated metrics.
316
+ """
317
+ metrics = {
318
+ "users_count": 0,
319
+ "agent_sessions_count": 0,
320
+ "team_sessions_count": 0,
321
+ "workflow_sessions_count": 0,
322
+ "agent_runs_count": 0,
323
+ "team_runs_count": 0,
324
+ "workflow_runs_count": 0,
325
+ }
326
+ token_metrics = {
327
+ "input_tokens": 0,
328
+ "output_tokens": 0,
329
+ "total_tokens": 0,
330
+ "audio_total_tokens": 0,
331
+ "audio_input_tokens": 0,
332
+ "audio_output_tokens": 0,
333
+ "cache_read_tokens": 0,
334
+ "cache_write_tokens": 0,
335
+ "reasoning_tokens": 0,
336
+ }
337
+ model_counts: Dict[str, int] = {}
338
+ session_types = [
339
+ ("agent", "agent_sessions_count", "agent_runs_count"),
340
+ ("team", "team_sessions_count", "team_runs_count"),
341
+ ("workflow", "workflow_sessions_count", "workflow_runs_count"),
342
+ ]
343
+ all_user_ids = set()
344
+ for session_type, sessions_count_key, runs_count_key in session_types:
345
+ sessions = sessions_data.get(session_type, [])
346
+ metrics[sessions_count_key] = len(sessions)
347
+ for session in sessions:
348
+ if session.get("user_id"):
349
+ all_user_ids.add(session["user_id"])
350
+ metrics[runs_count_key] += len(session.get("runs", []))
351
+ if runs := session.get("runs", []):
352
+ for run in runs:
353
+ if model_id := run.get("model"):
354
+ model_provider = run.get("model_provider", "")
355
+ model_counts[f"{model_id}:{model_provider}"] = (
356
+ model_counts.get(f"{model_id}:{model_provider}", 0) + 1
357
+ )
358
+ session_metrics = session.get("session_data", {}).get("session_metrics", {})
359
+ for field in token_metrics:
360
+ token_metrics[field] += session_metrics.get(field, 0)
361
+ model_metrics = []
362
+ for model, count in model_counts.items():
363
+ model_id, model_provider = model.split(":")
364
+ model_metrics.append({"model_id": model_id, "model_provider": model_provider, "count": count})
365
+ metrics["users_count"] = len(all_user_ids)
366
+ current_time = int(time.time())
367
+ return {
368
+ "id": str(uuid4()),
369
+ "date": date_to_process,
370
+ "completed": date_to_process < datetime.now(timezone.utc).date(),
371
+ "token_metrics": token_metrics,
372
+ "model_metrics": model_metrics,
373
+ "created_at": current_time,
374
+ "updated_at": current_time,
375
+ "aggregation_period": "daily",
376
+ **metrics,
377
+ }
378
+
379
+
380
+ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
381
+ """Return the list of dates to calculate metrics for.
382
+ Args:
383
+ starting_date (date): The starting date to calculate metrics for.
384
+ Returns:
385
+ list[date]: The list of dates to calculate metrics for.
386
+ """
387
+ today = datetime.now(timezone.utc).date()
388
+ days_diff = (today - starting_date).days + 1
389
+ if days_diff <= 0:
390
+ return []
391
+ return [starting_date + timedelta(days=x) for x in range(days_diff)]
392
+
393
+
394
+ def fetch_all_sessions_data(
395
+ sessions: List[Dict[str, Any]], dates_to_process: list[date], start_timestamp: int
396
+ ) -> Optional[dict]:
397
+ """Return all session data for the given dates, for all session types.
398
+ Args:
399
+ sessions: List of session data dictionaries
400
+ dates_to_process (list[date]): The dates to fetch session data for.
401
+ start_timestamp: The start timestamp for the range
402
+ Returns:
403
+ dict: A dictionary with dates as keys and session data as values, for all session types.
404
+ Example:
405
+ {
406
+ "2000-01-01": {
407
+ "agent": [<session1>, <session2>, ...],
408
+ "team": [...],
409
+ "workflow": [...],
410
+ }
411
+ }
412
+ """
413
+ if not dates_to_process:
414
+ return None
415
+ all_sessions_data: Dict[str, Dict[str, List[Dict[str, Any]]]] = {
416
+ date_to_process.isoformat(): {"agent": [], "team": [], "workflow": []} for date_to_process in dates_to_process
417
+ }
418
+ for session in sessions:
419
+ session_date = (
420
+ datetime.fromtimestamp(session.get("created_at", start_timestamp), tz=timezone.utc).date().isoformat()
421
+ )
422
+ if session_date in all_sessions_data:
423
+ all_sessions_data[session_date][session["session_type"]].append(session)
424
+ return all_sessions_data
425
+
426
+
427
+ def fetch_all_sessions_data_by_type(
428
+ dynamodb_client,
429
+ table_name: str,
430
+ session_type: str,
431
+ user_id: Optional[str] = None,
432
+ component_id: Optional[str] = None,
433
+ session_name: Optional[str] = None,
434
+ ) -> List[Dict[str, Any]]:
435
+ """Fetch all sessions data from DynamoDB table using GSI for session_type."""
436
+ items = []
437
+
438
+ try:
439
+ # Build filter expression for additional filters
440
+ filter_expression = None
441
+ expression_attribute_names = {}
442
+ expression_attribute_values = {":session_type": {"S": session_type}}
443
+
444
+ if user_id:
445
+ filter_expression = "#user_id = :user_id"
446
+ expression_attribute_names["#user_id"] = "user_id"
447
+ expression_attribute_values[":user_id"] = {"S": user_id}
448
+
449
+ if component_id:
450
+ component_filter = "#component_id = :component_id"
451
+ expression_attribute_names["#component_id"] = "component_id"
452
+ expression_attribute_values[":component_id"] = {"S": component_id}
453
+
454
+ if filter_expression:
455
+ filter_expression += f" AND {component_filter}"
456
+ else:
457
+ filter_expression = component_filter
458
+
459
+ if session_name:
460
+ name_filter = "#session_name = :session_name"
461
+ expression_attribute_names["#session_name"] = "session_name"
462
+ expression_attribute_values[":session_name"] = {"S": session_name}
463
+
464
+ if filter_expression:
465
+ filter_expression += f" AND {name_filter}"
466
+ else:
467
+ filter_expression = name_filter
468
+
469
+ # Use GSI query for session_type (more efficient than scan)
470
+ query_kwargs = {
471
+ "TableName": table_name,
472
+ "IndexName": "session_type-created_at-index",
473
+ "KeyConditionExpression": "session_type = :session_type",
474
+ "ExpressionAttributeValues": expression_attribute_values,
475
+ }
476
+
477
+ if filter_expression:
478
+ query_kwargs["FilterExpression"] = filter_expression
479
+
480
+ if expression_attribute_names:
481
+ query_kwargs["ExpressionAttributeNames"] = expression_attribute_names
482
+
483
+ response = dynamodb_client.query(**query_kwargs)
484
+ items.extend(response.get("Items", []))
485
+
486
+ # Handle pagination
487
+ while "LastEvaluatedKey" in response:
488
+ query_kwargs["ExclusiveStartKey"] = response["LastEvaluatedKey"]
489
+ response = dynamodb_client.query(**query_kwargs)
490
+ items.extend(response.get("Items", []))
491
+
492
+ except Exception as e:
493
+ log_error(f"Failed to fetch sessions: {e}")
494
+
495
+ return items
496
+
497
+
498
+ def bulk_upsert_metrics(dynamodb_client, table_name: str, metrics_data: List[Dict[str, Any]]) -> None:
499
+ """Bulk upsert metrics data to DynamoDB."""
500
+ try:
501
+ # DynamoDB batch write has a limit of 25 items
502
+ batch_size = 25
503
+
504
+ for i in range(0, len(metrics_data), batch_size):
505
+ batch = metrics_data[i : i + batch_size]
506
+
507
+ request_items: Dict[str, List[Dict[str, Any]]] = {table_name: []}
508
+
509
+ for metric in batch:
510
+ request_items[table_name].append({"PutRequest": {"Item": metric}})
511
+
512
+ dynamodb_client.batch_write_item(RequestItems=request_items)
513
+
514
+ except Exception as e:
515
+ log_error(f"Failed to bulk upsert metrics: {e}")
516
+
517
+
518
+ # -- Query utils --
519
+
520
+
521
+ def build_query_filter_expression(filters: Dict[str, Any]) -> tuple[Optional[str], Dict[str, str], Dict[str, Any]]:
522
+ """Build DynamoDB query filter expression from filters dictionary.
523
+
524
+ Args:
525
+ filters: Dictionary of filter key-value pairs
526
+
527
+ Returns:
528
+ Tuple of (filter_expression, expression_attribute_names, expression_attribute_values)
529
+ """
530
+ filter_expressions = []
531
+ expression_attribute_names = {}
532
+ expression_attribute_values = {}
533
+
534
+ for field, value in filters.items():
535
+ if value is not None:
536
+ filter_expressions.append(f"#{field} = :{field}")
537
+ expression_attribute_names[f"#{field}"] = field
538
+ expression_attribute_values[f":{field}"] = {"S": value}
539
+
540
+ filter_expression = " AND ".join(filter_expressions) if filter_expressions else None
541
+ return filter_expression, expression_attribute_names, expression_attribute_values
542
+
543
+
544
+ def build_topic_filter_expression(topics: List[str]) -> tuple[str, Dict[str, Any]]:
545
+ """Build DynamoDB filter expression for topics.
546
+
547
+ Args:
548
+ topics: List of topics to filter by
549
+
550
+ Returns:
551
+ Tuple of (filter_expression, expression_attribute_values)
552
+ """
553
+ topic_filters = []
554
+ expression_attribute_values = {}
555
+
556
+ for i, topic in enumerate(topics):
557
+ topic_key = f":topic_{i}"
558
+ topic_filters.append(f"contains(topics, {topic_key})")
559
+ expression_attribute_values[topic_key] = {"S": topic}
560
+
561
+ filter_expression = f"({' OR '.join(topic_filters)})"
562
+ return filter_expression, expression_attribute_values
563
+
564
+
565
+ def execute_query_with_pagination(
566
+ dynamodb_client,
567
+ table_name: str,
568
+ index_name: str,
569
+ key_condition_expression: str,
570
+ expression_attribute_names: Dict[str, str],
571
+ expression_attribute_values: Dict[str, Any],
572
+ filter_expression: Optional[str] = None,
573
+ sort_by: Optional[str] = None,
574
+ sort_order: Optional[str] = None,
575
+ limit: Optional[int] = None,
576
+ page: Optional[int] = None,
577
+ ) -> List[Dict[str, Any]]:
578
+ """Execute DynamoDB query with pagination support.
579
+
580
+ Args:
581
+ dynamodb_client: DynamoDB client
582
+ table_name: Table name
583
+ index_name: Index name for query
584
+ key_condition_expression: Key condition expression
585
+ expression_attribute_names: Expression attribute names
586
+ expression_attribute_values: Expression attribute values
587
+ filter_expression: Optional filter expression
588
+ sort_by: Field to sort by
589
+ sort_order: Sort order (asc/desc)
590
+ limit: Limit for pagination
591
+ page: Page number
592
+
593
+ Returns:
594
+ List of DynamoDB items
595
+ """
596
+ query_kwargs = {
597
+ "TableName": table_name,
598
+ "IndexName": index_name,
599
+ "KeyConditionExpression": key_condition_expression,
600
+ "ExpressionAttributeValues": expression_attribute_values,
601
+ }
602
+
603
+ if expression_attribute_names:
604
+ query_kwargs["ExpressionAttributeNames"] = expression_attribute_names
605
+
606
+ if filter_expression:
607
+ query_kwargs["FilterExpression"] = filter_expression
608
+
609
+ # Apply sorting at query level if sorting by created_at
610
+ if sort_by == "created_at":
611
+ query_kwargs["ScanIndexForward"] = sort_order != "desc" # type: ignore
612
+
613
+ # Apply limit at DynamoDB level if no pagination
614
+ if limit and not page:
615
+ query_kwargs["Limit"] = limit # type: ignore
616
+
617
+ items = []
618
+ response = dynamodb_client.query(**query_kwargs)
619
+ items.extend(response.get("Items", []))
620
+
621
+ # Handle pagination
622
+ while "LastEvaluatedKey" in response:
623
+ query_kwargs["ExclusiveStartKey"] = response["LastEvaluatedKey"]
624
+ response = dynamodb_client.query(**query_kwargs)
625
+ items.extend(response.get("Items", []))
626
+
627
+ return items
628
+
629
+
630
+ def process_query_results(
631
+ items: List[Dict[str, Any]],
632
+ sort_by: Optional[str] = None,
633
+ sort_order: Optional[str] = None,
634
+ limit: Optional[int] = None,
635
+ page: Optional[int] = None,
636
+ deserialize_func: Optional[Callable] = None,
637
+ deserialize: bool = True,
638
+ ) -> Union[List[Any], tuple[List[Any], int]]:
639
+ """Process query results with sorting, pagination, and deserialization.
640
+
641
+ Args:
642
+ items: List of DynamoDB items
643
+ sort_by: Field to sort by
644
+ sort_order: Sort order (asc/desc)
645
+ limit: Limit for pagination
646
+ page: Page number
647
+ deserialize_func: Function to deserialize items
648
+ deserialize: Whether to deserialize items
649
+
650
+ Returns:
651
+ List of processed items or tuple of (items, total_count)
652
+ """
653
+ # Convert DynamoDB items to data
654
+ processed_data = []
655
+ for item in items:
656
+ data = deserialize_from_dynamodb_item(item)
657
+ if data:
658
+ processed_data.append(data)
659
+
660
+ # Apply in-memory sorting for fields not handled by DynamoDB
661
+ if sort_by and sort_by != "created_at":
662
+ processed_data = apply_sorting(processed_data, sort_by, sort_order)
663
+
664
+ # Get total count before pagination
665
+ total_count = len(processed_data)
666
+
667
+ # Apply pagination
668
+ if page:
669
+ processed_data = apply_pagination(processed_data, limit, page)
670
+
671
+ if not deserialize or not deserialize_func:
672
+ return processed_data, total_count
673
+
674
+ # Deserialize items
675
+ deserialized_items = []
676
+ for data in processed_data:
677
+ try:
678
+ item = deserialize_func(data)
679
+ if item:
680
+ deserialized_items.append(item)
681
+ except Exception as e:
682
+ log_error(f"Failed to deserialize item: {e}")
683
+
684
+ return deserialized_items
@@ -0,0 +1,3 @@
1
+ from agno.db.firestore.firestore import FirestoreDb
2
+
3
+ __all__ = ["FirestoreDb"]