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

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