agno 0.1.2__py3-none-any.whl → 2.3.13__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 (723) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +44 -5
  3. agno/agent/agent.py +10531 -2975
  4. agno/api/agent.py +14 -53
  5. agno/api/api.py +7 -46
  6. agno/api/evals.py +22 -0
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -25
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +6 -9
  11. agno/api/schemas/evals.py +16 -0
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +10 -10
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +16 -0
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +22 -26
  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/compression/__init__.py +3 -0
  25. agno/compression/manager.py +247 -0
  26. agno/culture/__init__.py +3 -0
  27. agno/culture/manager.py +956 -0
  28. agno/db/__init__.py +24 -0
  29. agno/db/async_postgres/__init__.py +3 -0
  30. agno/db/base.py +946 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2781 -0
  33. agno/db/dynamo/schemas.py +442 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +2379 -0
  37. agno/db/firestore/schemas.py +181 -0
  38. agno/db/firestore/utils.py +376 -0
  39. agno/db/gcs_json/__init__.py +3 -0
  40. agno/db/gcs_json/gcs_json_db.py +1791 -0
  41. agno/db/gcs_json/utils.py +228 -0
  42. agno/db/in_memory/__init__.py +3 -0
  43. agno/db/in_memory/in_memory_db.py +1312 -0
  44. agno/db/in_memory/utils.py +230 -0
  45. agno/db/json/__init__.py +3 -0
  46. agno/db/json/json_db.py +1777 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/manager.py +199 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/migrations/versions/v2_3_0.py +938 -0
  51. agno/db/mongo/__init__.py +17 -0
  52. agno/db/mongo/async_mongo.py +2760 -0
  53. agno/db/mongo/mongo.py +2597 -0
  54. agno/db/mongo/schemas.py +119 -0
  55. agno/db/mongo/utils.py +276 -0
  56. agno/db/mysql/__init__.py +4 -0
  57. agno/db/mysql/async_mysql.py +2912 -0
  58. agno/db/mysql/mysql.py +2923 -0
  59. agno/db/mysql/schemas.py +186 -0
  60. agno/db/mysql/utils.py +488 -0
  61. agno/db/postgres/__init__.py +4 -0
  62. agno/db/postgres/async_postgres.py +2579 -0
  63. agno/db/postgres/postgres.py +2870 -0
  64. agno/db/postgres/schemas.py +187 -0
  65. agno/db/postgres/utils.py +442 -0
  66. agno/db/redis/__init__.py +3 -0
  67. agno/db/redis/redis.py +2141 -0
  68. agno/db/redis/schemas.py +159 -0
  69. agno/db/redis/utils.py +346 -0
  70. agno/db/schemas/__init__.py +4 -0
  71. agno/db/schemas/culture.py +120 -0
  72. agno/db/schemas/evals.py +34 -0
  73. agno/db/schemas/knowledge.py +40 -0
  74. agno/db/schemas/memory.py +61 -0
  75. agno/db/singlestore/__init__.py +3 -0
  76. agno/db/singlestore/schemas.py +179 -0
  77. agno/db/singlestore/singlestore.py +2877 -0
  78. agno/db/singlestore/utils.py +384 -0
  79. agno/db/sqlite/__init__.py +4 -0
  80. agno/db/sqlite/async_sqlite.py +2911 -0
  81. agno/db/sqlite/schemas.py +181 -0
  82. agno/db/sqlite/sqlite.py +2908 -0
  83. agno/db/sqlite/utils.py +429 -0
  84. agno/db/surrealdb/__init__.py +3 -0
  85. agno/db/surrealdb/metrics.py +292 -0
  86. agno/db/surrealdb/models.py +334 -0
  87. agno/db/surrealdb/queries.py +71 -0
  88. agno/db/surrealdb/surrealdb.py +1908 -0
  89. agno/db/surrealdb/utils.py +147 -0
  90. agno/db/utils.py +118 -0
  91. agno/eval/__init__.py +24 -0
  92. agno/eval/accuracy.py +666 -276
  93. agno/eval/agent_as_judge.py +861 -0
  94. agno/eval/base.py +29 -0
  95. agno/eval/performance.py +779 -0
  96. agno/eval/reliability.py +241 -62
  97. agno/eval/utils.py +120 -0
  98. agno/exceptions.py +143 -1
  99. agno/filters.py +354 -0
  100. agno/guardrails/__init__.py +6 -0
  101. agno/guardrails/base.py +19 -0
  102. agno/guardrails/openai.py +144 -0
  103. agno/guardrails/pii.py +94 -0
  104. agno/guardrails/prompt_injection.py +52 -0
  105. agno/hooks/__init__.py +3 -0
  106. agno/hooks/decorator.py +164 -0
  107. agno/integrations/discord/__init__.py +3 -0
  108. agno/integrations/discord/client.py +203 -0
  109. agno/knowledge/__init__.py +5 -1
  110. agno/{document → knowledge}/chunking/agentic.py +22 -14
  111. agno/{document → knowledge}/chunking/document.py +2 -2
  112. agno/{document → knowledge}/chunking/fixed.py +7 -6
  113. agno/knowledge/chunking/markdown.py +151 -0
  114. agno/{document → knowledge}/chunking/recursive.py +15 -3
  115. agno/knowledge/chunking/row.py +39 -0
  116. agno/knowledge/chunking/semantic.py +91 -0
  117. agno/knowledge/chunking/strategy.py +165 -0
  118. agno/knowledge/content.py +74 -0
  119. agno/knowledge/document/__init__.py +5 -0
  120. agno/{document → knowledge/document}/base.py +12 -2
  121. agno/knowledge/embedder/__init__.py +5 -0
  122. agno/knowledge/embedder/aws_bedrock.py +343 -0
  123. agno/knowledge/embedder/azure_openai.py +210 -0
  124. agno/{embedder → knowledge/embedder}/base.py +8 -0
  125. agno/knowledge/embedder/cohere.py +323 -0
  126. agno/knowledge/embedder/fastembed.py +62 -0
  127. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  128. agno/knowledge/embedder/google.py +258 -0
  129. agno/knowledge/embedder/huggingface.py +94 -0
  130. agno/knowledge/embedder/jina.py +182 -0
  131. agno/knowledge/embedder/langdb.py +22 -0
  132. agno/knowledge/embedder/mistral.py +206 -0
  133. agno/knowledge/embedder/nebius.py +13 -0
  134. agno/knowledge/embedder/ollama.py +154 -0
  135. agno/knowledge/embedder/openai.py +195 -0
  136. agno/knowledge/embedder/sentence_transformer.py +63 -0
  137. agno/{embedder → knowledge/embedder}/together.py +1 -1
  138. agno/knowledge/embedder/vllm.py +262 -0
  139. agno/knowledge/embedder/voyageai.py +165 -0
  140. agno/knowledge/knowledge.py +3006 -0
  141. agno/knowledge/reader/__init__.py +7 -0
  142. agno/knowledge/reader/arxiv_reader.py +81 -0
  143. agno/knowledge/reader/base.py +95 -0
  144. agno/knowledge/reader/csv_reader.py +164 -0
  145. agno/knowledge/reader/docx_reader.py +82 -0
  146. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  147. agno/knowledge/reader/firecrawl_reader.py +201 -0
  148. agno/knowledge/reader/json_reader.py +88 -0
  149. agno/knowledge/reader/markdown_reader.py +137 -0
  150. agno/knowledge/reader/pdf_reader.py +431 -0
  151. agno/knowledge/reader/pptx_reader.py +101 -0
  152. agno/knowledge/reader/reader_factory.py +313 -0
  153. agno/knowledge/reader/s3_reader.py +89 -0
  154. agno/knowledge/reader/tavily_reader.py +193 -0
  155. agno/knowledge/reader/text_reader.py +127 -0
  156. agno/knowledge/reader/web_search_reader.py +325 -0
  157. agno/knowledge/reader/website_reader.py +455 -0
  158. agno/knowledge/reader/wikipedia_reader.py +91 -0
  159. agno/knowledge/reader/youtube_reader.py +78 -0
  160. agno/knowledge/remote_content/remote_content.py +88 -0
  161. agno/knowledge/reranker/__init__.py +3 -0
  162. agno/{reranker → knowledge/reranker}/base.py +1 -1
  163. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  164. agno/knowledge/reranker/infinity.py +195 -0
  165. agno/knowledge/reranker/sentence_transformer.py +54 -0
  166. agno/knowledge/types.py +39 -0
  167. agno/knowledge/utils.py +234 -0
  168. agno/media.py +439 -95
  169. agno/memory/__init__.py +16 -3
  170. agno/memory/manager.py +1474 -123
  171. agno/memory/strategies/__init__.py +15 -0
  172. agno/memory/strategies/base.py +66 -0
  173. agno/memory/strategies/summarize.py +196 -0
  174. agno/memory/strategies/types.py +37 -0
  175. agno/models/aimlapi/__init__.py +5 -0
  176. agno/models/aimlapi/aimlapi.py +62 -0
  177. agno/models/anthropic/__init__.py +4 -0
  178. agno/models/anthropic/claude.py +960 -496
  179. agno/models/aws/__init__.py +15 -0
  180. agno/models/aws/bedrock.py +686 -451
  181. agno/models/aws/claude.py +190 -183
  182. agno/models/azure/__init__.py +18 -1
  183. agno/models/azure/ai_foundry.py +489 -0
  184. agno/models/azure/openai_chat.py +89 -40
  185. agno/models/base.py +2477 -550
  186. agno/models/cerebras/__init__.py +12 -0
  187. agno/models/cerebras/cerebras.py +565 -0
  188. agno/models/cerebras/cerebras_openai.py +131 -0
  189. agno/models/cohere/__init__.py +4 -0
  190. agno/models/cohere/chat.py +306 -492
  191. agno/models/cometapi/__init__.py +5 -0
  192. agno/models/cometapi/cometapi.py +74 -0
  193. agno/models/dashscope/__init__.py +5 -0
  194. agno/models/dashscope/dashscope.py +90 -0
  195. agno/models/deepinfra/__init__.py +5 -0
  196. agno/models/deepinfra/deepinfra.py +45 -0
  197. agno/models/deepseek/__init__.py +4 -0
  198. agno/models/deepseek/deepseek.py +110 -9
  199. agno/models/fireworks/__init__.py +4 -0
  200. agno/models/fireworks/fireworks.py +19 -22
  201. agno/models/google/__init__.py +3 -7
  202. agno/models/google/gemini.py +1717 -662
  203. agno/models/google/utils.py +22 -0
  204. agno/models/groq/__init__.py +4 -0
  205. agno/models/groq/groq.py +391 -666
  206. agno/models/huggingface/__init__.py +4 -0
  207. agno/models/huggingface/huggingface.py +266 -538
  208. agno/models/ibm/__init__.py +5 -0
  209. agno/models/ibm/watsonx.py +432 -0
  210. agno/models/internlm/__init__.py +3 -0
  211. agno/models/internlm/internlm.py +20 -3
  212. agno/models/langdb/__init__.py +1 -0
  213. agno/models/langdb/langdb.py +60 -0
  214. agno/models/litellm/__init__.py +14 -0
  215. agno/models/litellm/chat.py +503 -0
  216. agno/models/litellm/litellm_openai.py +42 -0
  217. agno/models/llama_cpp/__init__.py +5 -0
  218. agno/models/llama_cpp/llama_cpp.py +22 -0
  219. agno/models/lmstudio/__init__.py +5 -0
  220. agno/models/lmstudio/lmstudio.py +25 -0
  221. agno/models/message.py +361 -39
  222. agno/models/meta/__init__.py +12 -0
  223. agno/models/meta/llama.py +502 -0
  224. agno/models/meta/llama_openai.py +79 -0
  225. agno/models/metrics.py +120 -0
  226. agno/models/mistral/__init__.py +4 -0
  227. agno/models/mistral/mistral.py +293 -393
  228. agno/models/nebius/__init__.py +3 -0
  229. agno/models/nebius/nebius.py +53 -0
  230. agno/models/nexus/__init__.py +3 -0
  231. agno/models/nexus/nexus.py +22 -0
  232. agno/models/nvidia/__init__.py +4 -0
  233. agno/models/nvidia/nvidia.py +22 -3
  234. agno/models/ollama/__init__.py +4 -2
  235. agno/models/ollama/chat.py +257 -492
  236. agno/models/openai/__init__.py +7 -0
  237. agno/models/openai/chat.py +725 -770
  238. agno/models/openai/like.py +16 -2
  239. agno/models/openai/responses.py +1121 -0
  240. agno/models/openrouter/__init__.py +4 -0
  241. agno/models/openrouter/openrouter.py +62 -5
  242. agno/models/perplexity/__init__.py +5 -0
  243. agno/models/perplexity/perplexity.py +203 -0
  244. agno/models/portkey/__init__.py +3 -0
  245. agno/models/portkey/portkey.py +82 -0
  246. agno/models/requesty/__init__.py +5 -0
  247. agno/models/requesty/requesty.py +69 -0
  248. agno/models/response.py +177 -7
  249. agno/models/sambanova/__init__.py +4 -0
  250. agno/models/sambanova/sambanova.py +23 -4
  251. agno/models/siliconflow/__init__.py +5 -0
  252. agno/models/siliconflow/siliconflow.py +42 -0
  253. agno/models/together/__init__.py +4 -0
  254. agno/models/together/together.py +21 -164
  255. agno/models/utils.py +266 -0
  256. agno/models/vercel/__init__.py +3 -0
  257. agno/models/vercel/v0.py +43 -0
  258. agno/models/vertexai/__init__.py +0 -1
  259. agno/models/vertexai/claude.py +190 -0
  260. agno/models/vllm/__init__.py +3 -0
  261. agno/models/vllm/vllm.py +83 -0
  262. agno/models/xai/__init__.py +2 -0
  263. agno/models/xai/xai.py +111 -7
  264. agno/os/__init__.py +3 -0
  265. agno/os/app.py +1027 -0
  266. agno/os/auth.py +244 -0
  267. agno/os/config.py +126 -0
  268. agno/os/interfaces/__init__.py +1 -0
  269. agno/os/interfaces/a2a/__init__.py +3 -0
  270. agno/os/interfaces/a2a/a2a.py +42 -0
  271. agno/os/interfaces/a2a/router.py +249 -0
  272. agno/os/interfaces/a2a/utils.py +924 -0
  273. agno/os/interfaces/agui/__init__.py +3 -0
  274. agno/os/interfaces/agui/agui.py +47 -0
  275. agno/os/interfaces/agui/router.py +147 -0
  276. agno/os/interfaces/agui/utils.py +574 -0
  277. agno/os/interfaces/base.py +25 -0
  278. agno/os/interfaces/slack/__init__.py +3 -0
  279. agno/os/interfaces/slack/router.py +148 -0
  280. agno/os/interfaces/slack/security.py +30 -0
  281. agno/os/interfaces/slack/slack.py +47 -0
  282. agno/os/interfaces/whatsapp/__init__.py +3 -0
  283. agno/os/interfaces/whatsapp/router.py +210 -0
  284. agno/os/interfaces/whatsapp/security.py +55 -0
  285. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  286. agno/os/mcp.py +293 -0
  287. agno/os/middleware/__init__.py +9 -0
  288. agno/os/middleware/jwt.py +797 -0
  289. agno/os/router.py +258 -0
  290. agno/os/routers/__init__.py +3 -0
  291. agno/os/routers/agents/__init__.py +3 -0
  292. agno/os/routers/agents/router.py +599 -0
  293. agno/os/routers/agents/schema.py +261 -0
  294. agno/os/routers/evals/__init__.py +3 -0
  295. agno/os/routers/evals/evals.py +450 -0
  296. agno/os/routers/evals/schemas.py +174 -0
  297. agno/os/routers/evals/utils.py +231 -0
  298. agno/os/routers/health.py +31 -0
  299. agno/os/routers/home.py +52 -0
  300. agno/os/routers/knowledge/__init__.py +3 -0
  301. agno/os/routers/knowledge/knowledge.py +1008 -0
  302. agno/os/routers/knowledge/schemas.py +178 -0
  303. agno/os/routers/memory/__init__.py +3 -0
  304. agno/os/routers/memory/memory.py +661 -0
  305. agno/os/routers/memory/schemas.py +88 -0
  306. agno/os/routers/metrics/__init__.py +3 -0
  307. agno/os/routers/metrics/metrics.py +190 -0
  308. agno/os/routers/metrics/schemas.py +47 -0
  309. agno/os/routers/session/__init__.py +3 -0
  310. agno/os/routers/session/session.py +997 -0
  311. agno/os/routers/teams/__init__.py +3 -0
  312. agno/os/routers/teams/router.py +512 -0
  313. agno/os/routers/teams/schema.py +257 -0
  314. agno/os/routers/traces/__init__.py +3 -0
  315. agno/os/routers/traces/schemas.py +414 -0
  316. agno/os/routers/traces/traces.py +499 -0
  317. agno/os/routers/workflows/__init__.py +3 -0
  318. agno/os/routers/workflows/router.py +624 -0
  319. agno/os/routers/workflows/schema.py +75 -0
  320. agno/os/schema.py +534 -0
  321. agno/os/scopes.py +469 -0
  322. agno/{playground → os}/settings.py +7 -15
  323. agno/os/utils.py +973 -0
  324. agno/reasoning/anthropic.py +80 -0
  325. agno/reasoning/azure_ai_foundry.py +67 -0
  326. agno/reasoning/deepseek.py +63 -0
  327. agno/reasoning/default.py +97 -0
  328. agno/reasoning/gemini.py +73 -0
  329. agno/reasoning/groq.py +71 -0
  330. agno/reasoning/helpers.py +24 -1
  331. agno/reasoning/ollama.py +67 -0
  332. agno/reasoning/openai.py +86 -0
  333. agno/reasoning/step.py +2 -1
  334. agno/reasoning/vertexai.py +76 -0
  335. agno/run/__init__.py +6 -0
  336. agno/run/agent.py +822 -0
  337. agno/run/base.py +247 -0
  338. agno/run/cancel.py +81 -0
  339. agno/run/requirement.py +181 -0
  340. agno/run/team.py +767 -0
  341. agno/run/workflow.py +708 -0
  342. agno/session/__init__.py +10 -0
  343. agno/session/agent.py +260 -0
  344. agno/session/summary.py +265 -0
  345. agno/session/team.py +342 -0
  346. agno/session/workflow.py +501 -0
  347. agno/table.py +10 -0
  348. agno/team/__init__.py +37 -0
  349. agno/team/team.py +9536 -0
  350. agno/tools/__init__.py +7 -0
  351. agno/tools/agentql.py +120 -0
  352. agno/tools/airflow.py +22 -12
  353. agno/tools/api.py +122 -0
  354. agno/tools/apify.py +276 -83
  355. agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
  356. agno/tools/aws_lambda.py +28 -7
  357. agno/tools/aws_ses.py +66 -0
  358. agno/tools/baidusearch.py +11 -4
  359. agno/tools/bitbucket.py +292 -0
  360. agno/tools/brandfetch.py +213 -0
  361. agno/tools/bravesearch.py +106 -0
  362. agno/tools/brightdata.py +367 -0
  363. agno/tools/browserbase.py +209 -0
  364. agno/tools/calcom.py +32 -23
  365. agno/tools/calculator.py +24 -37
  366. agno/tools/cartesia.py +187 -0
  367. agno/tools/{clickup_tool.py → clickup.py} +17 -28
  368. agno/tools/confluence.py +91 -26
  369. agno/tools/crawl4ai.py +139 -43
  370. agno/tools/csv_toolkit.py +28 -22
  371. agno/tools/dalle.py +36 -22
  372. agno/tools/daytona.py +475 -0
  373. agno/tools/decorator.py +169 -14
  374. agno/tools/desi_vocal.py +23 -11
  375. agno/tools/discord.py +32 -29
  376. agno/tools/docker.py +716 -0
  377. agno/tools/duckdb.py +76 -81
  378. agno/tools/duckduckgo.py +43 -40
  379. agno/tools/e2b.py +703 -0
  380. agno/tools/eleven_labs.py +65 -54
  381. agno/tools/email.py +13 -5
  382. agno/tools/evm.py +129 -0
  383. agno/tools/exa.py +324 -42
  384. agno/tools/fal.py +39 -35
  385. agno/tools/file.py +196 -30
  386. agno/tools/file_generation.py +356 -0
  387. agno/tools/financial_datasets.py +288 -0
  388. agno/tools/firecrawl.py +108 -33
  389. agno/tools/function.py +960 -122
  390. agno/tools/giphy.py +34 -12
  391. agno/tools/github.py +1294 -97
  392. agno/tools/gmail.py +922 -0
  393. agno/tools/google_bigquery.py +117 -0
  394. agno/tools/google_drive.py +271 -0
  395. agno/tools/google_maps.py +253 -0
  396. agno/tools/googlecalendar.py +607 -107
  397. agno/tools/googlesheets.py +377 -0
  398. agno/tools/hackernews.py +20 -12
  399. agno/tools/jina.py +24 -14
  400. agno/tools/jira.py +48 -19
  401. agno/tools/knowledge.py +218 -0
  402. agno/tools/linear.py +82 -43
  403. agno/tools/linkup.py +58 -0
  404. agno/tools/local_file_system.py +15 -7
  405. agno/tools/lumalab.py +41 -26
  406. agno/tools/mcp/__init__.py +10 -0
  407. agno/tools/mcp/mcp.py +331 -0
  408. agno/tools/mcp/multi_mcp.py +347 -0
  409. agno/tools/mcp/params.py +24 -0
  410. agno/tools/mcp_toolbox.py +284 -0
  411. agno/tools/mem0.py +193 -0
  412. agno/tools/memory.py +419 -0
  413. agno/tools/mlx_transcribe.py +11 -9
  414. agno/tools/models/azure_openai.py +190 -0
  415. agno/tools/models/gemini.py +203 -0
  416. agno/tools/models/groq.py +158 -0
  417. agno/tools/models/morph.py +186 -0
  418. agno/tools/models/nebius.py +124 -0
  419. agno/tools/models_labs.py +163 -82
  420. agno/tools/moviepy_video.py +18 -13
  421. agno/tools/nano_banana.py +151 -0
  422. agno/tools/neo4j.py +134 -0
  423. agno/tools/newspaper.py +15 -4
  424. agno/tools/newspaper4k.py +19 -6
  425. agno/tools/notion.py +204 -0
  426. agno/tools/openai.py +181 -17
  427. agno/tools/openbb.py +27 -20
  428. agno/tools/opencv.py +321 -0
  429. agno/tools/openweather.py +233 -0
  430. agno/tools/oxylabs.py +385 -0
  431. agno/tools/pandas.py +25 -15
  432. agno/tools/parallel.py +314 -0
  433. agno/tools/postgres.py +238 -185
  434. agno/tools/pubmed.py +125 -13
  435. agno/tools/python.py +48 -35
  436. agno/tools/reasoning.py +283 -0
  437. agno/tools/reddit.py +207 -29
  438. agno/tools/redshift.py +406 -0
  439. agno/tools/replicate.py +69 -26
  440. agno/tools/resend.py +11 -6
  441. agno/tools/scrapegraph.py +179 -19
  442. agno/tools/searxng.py +23 -31
  443. agno/tools/serpapi.py +15 -10
  444. agno/tools/serper.py +255 -0
  445. agno/tools/shell.py +23 -12
  446. agno/tools/shopify.py +1519 -0
  447. agno/tools/slack.py +56 -14
  448. agno/tools/sleep.py +8 -6
  449. agno/tools/spider.py +35 -11
  450. agno/tools/spotify.py +919 -0
  451. agno/tools/sql.py +34 -19
  452. agno/tools/tavily.py +158 -8
  453. agno/tools/telegram.py +18 -8
  454. agno/tools/todoist.py +218 -0
  455. agno/tools/toolkit.py +134 -9
  456. agno/tools/trafilatura.py +388 -0
  457. agno/tools/trello.py +25 -28
  458. agno/tools/twilio.py +18 -9
  459. agno/tools/user_control_flow.py +78 -0
  460. agno/tools/valyu.py +228 -0
  461. agno/tools/visualization.py +467 -0
  462. agno/tools/webbrowser.py +28 -0
  463. agno/tools/webex.py +76 -0
  464. agno/tools/website.py +23 -19
  465. agno/tools/webtools.py +45 -0
  466. agno/tools/whatsapp.py +286 -0
  467. agno/tools/wikipedia.py +28 -19
  468. agno/tools/workflow.py +285 -0
  469. agno/tools/{twitter.py → x.py} +142 -46
  470. agno/tools/yfinance.py +41 -39
  471. agno/tools/youtube.py +34 -17
  472. agno/tools/zendesk.py +15 -5
  473. agno/tools/zep.py +454 -0
  474. agno/tools/zoom.py +86 -37
  475. agno/tracing/__init__.py +12 -0
  476. agno/tracing/exporter.py +157 -0
  477. agno/tracing/schemas.py +276 -0
  478. agno/tracing/setup.py +111 -0
  479. agno/utils/agent.py +938 -0
  480. agno/utils/audio.py +37 -1
  481. agno/utils/certs.py +27 -0
  482. agno/utils/code_execution.py +11 -0
  483. agno/utils/common.py +103 -20
  484. agno/utils/cryptography.py +22 -0
  485. agno/utils/dttm.py +33 -0
  486. agno/utils/events.py +700 -0
  487. agno/utils/functions.py +107 -37
  488. agno/utils/gemini.py +426 -0
  489. agno/utils/hooks.py +171 -0
  490. agno/utils/http.py +185 -0
  491. agno/utils/json_schema.py +159 -37
  492. agno/utils/knowledge.py +36 -0
  493. agno/utils/location.py +19 -0
  494. agno/utils/log.py +221 -8
  495. agno/utils/mcp.py +214 -0
  496. agno/utils/media.py +335 -14
  497. agno/utils/merge_dict.py +22 -1
  498. agno/utils/message.py +77 -2
  499. agno/utils/models/ai_foundry.py +50 -0
  500. agno/utils/models/claude.py +373 -0
  501. agno/utils/models/cohere.py +94 -0
  502. agno/utils/models/llama.py +85 -0
  503. agno/utils/models/mistral.py +100 -0
  504. agno/utils/models/openai_responses.py +140 -0
  505. agno/utils/models/schema_utils.py +153 -0
  506. agno/utils/models/watsonx.py +41 -0
  507. agno/utils/openai.py +257 -0
  508. agno/utils/pickle.py +1 -1
  509. agno/utils/pprint.py +124 -8
  510. agno/utils/print_response/agent.py +930 -0
  511. agno/utils/print_response/team.py +1914 -0
  512. agno/utils/print_response/workflow.py +1668 -0
  513. agno/utils/prompts.py +111 -0
  514. agno/utils/reasoning.py +108 -0
  515. agno/utils/response.py +163 -0
  516. agno/utils/serialize.py +32 -0
  517. agno/utils/shell.py +4 -4
  518. agno/utils/streamlit.py +487 -0
  519. agno/utils/string.py +204 -51
  520. agno/utils/team.py +139 -0
  521. agno/utils/timer.py +9 -2
  522. agno/utils/tokens.py +657 -0
  523. agno/utils/tools.py +19 -1
  524. agno/utils/whatsapp.py +305 -0
  525. agno/utils/yaml_io.py +3 -3
  526. agno/vectordb/__init__.py +2 -0
  527. agno/vectordb/base.py +87 -9
  528. agno/vectordb/cassandra/__init__.py +5 -1
  529. agno/vectordb/cassandra/cassandra.py +383 -27
  530. agno/vectordb/chroma/__init__.py +4 -0
  531. agno/vectordb/chroma/chromadb.py +748 -83
  532. agno/vectordb/clickhouse/__init__.py +7 -1
  533. agno/vectordb/clickhouse/clickhousedb.py +554 -53
  534. agno/vectordb/couchbase/__init__.py +3 -0
  535. agno/vectordb/couchbase/couchbase.py +1446 -0
  536. agno/vectordb/lancedb/__init__.py +5 -0
  537. agno/vectordb/lancedb/lance_db.py +730 -98
  538. agno/vectordb/langchaindb/__init__.py +5 -0
  539. agno/vectordb/langchaindb/langchaindb.py +163 -0
  540. agno/vectordb/lightrag/__init__.py +5 -0
  541. agno/vectordb/lightrag/lightrag.py +388 -0
  542. agno/vectordb/llamaindex/__init__.py +3 -0
  543. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  544. agno/vectordb/milvus/__init__.py +3 -0
  545. agno/vectordb/milvus/milvus.py +966 -78
  546. agno/vectordb/mongodb/__init__.py +9 -1
  547. agno/vectordb/mongodb/mongodb.py +1175 -172
  548. agno/vectordb/pgvector/__init__.py +8 -0
  549. agno/vectordb/pgvector/pgvector.py +599 -115
  550. agno/vectordb/pineconedb/__init__.py +5 -1
  551. agno/vectordb/pineconedb/pineconedb.py +406 -43
  552. agno/vectordb/qdrant/__init__.py +4 -0
  553. agno/vectordb/qdrant/qdrant.py +914 -61
  554. agno/vectordb/redis/__init__.py +9 -0
  555. agno/vectordb/redis/redisdb.py +682 -0
  556. agno/vectordb/singlestore/__init__.py +8 -1
  557. agno/vectordb/singlestore/singlestore.py +771 -0
  558. agno/vectordb/surrealdb/__init__.py +3 -0
  559. agno/vectordb/surrealdb/surrealdb.py +663 -0
  560. agno/vectordb/upstashdb/__init__.py +5 -0
  561. agno/vectordb/upstashdb/upstashdb.py +718 -0
  562. agno/vectordb/weaviate/__init__.py +8 -0
  563. agno/vectordb/weaviate/index.py +15 -0
  564. agno/vectordb/weaviate/weaviate.py +1009 -0
  565. agno/workflow/__init__.py +23 -1
  566. agno/workflow/agent.py +299 -0
  567. agno/workflow/condition.py +759 -0
  568. agno/workflow/loop.py +756 -0
  569. agno/workflow/parallel.py +853 -0
  570. agno/workflow/router.py +723 -0
  571. agno/workflow/step.py +1564 -0
  572. agno/workflow/steps.py +613 -0
  573. agno/workflow/types.py +556 -0
  574. agno/workflow/workflow.py +4327 -514
  575. agno-2.3.13.dist-info/METADATA +639 -0
  576. agno-2.3.13.dist-info/RECORD +613 -0
  577. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
  578. agno-2.3.13.dist-info/licenses/LICENSE +201 -0
  579. agno/api/playground.py +0 -91
  580. agno/api/schemas/playground.py +0 -22
  581. agno/api/schemas/user.py +0 -22
  582. agno/api/schemas/workspace.py +0 -46
  583. agno/api/user.py +0 -160
  584. agno/api/workspace.py +0 -151
  585. agno/cli/auth_server.py +0 -118
  586. agno/cli/config.py +0 -275
  587. agno/cli/console.py +0 -88
  588. agno/cli/credentials.py +0 -23
  589. agno/cli/entrypoint.py +0 -571
  590. agno/cli/operator.py +0 -355
  591. agno/cli/settings.py +0 -85
  592. agno/cli/ws/ws_cli.py +0 -817
  593. agno/constants.py +0 -13
  594. agno/document/__init__.py +0 -1
  595. agno/document/chunking/semantic.py +0 -47
  596. agno/document/chunking/strategy.py +0 -31
  597. agno/document/reader/__init__.py +0 -1
  598. agno/document/reader/arxiv_reader.py +0 -41
  599. agno/document/reader/base.py +0 -22
  600. agno/document/reader/csv_reader.py +0 -84
  601. agno/document/reader/docx_reader.py +0 -46
  602. agno/document/reader/firecrawl_reader.py +0 -99
  603. agno/document/reader/json_reader.py +0 -43
  604. agno/document/reader/pdf_reader.py +0 -219
  605. agno/document/reader/s3/pdf_reader.py +0 -46
  606. agno/document/reader/s3/text_reader.py +0 -51
  607. agno/document/reader/text_reader.py +0 -41
  608. agno/document/reader/website_reader.py +0 -175
  609. agno/document/reader/youtube_reader.py +0 -50
  610. agno/embedder/__init__.py +0 -1
  611. agno/embedder/azure_openai.py +0 -86
  612. agno/embedder/cohere.py +0 -72
  613. agno/embedder/fastembed.py +0 -37
  614. agno/embedder/google.py +0 -73
  615. agno/embedder/huggingface.py +0 -54
  616. agno/embedder/mistral.py +0 -80
  617. agno/embedder/ollama.py +0 -57
  618. agno/embedder/openai.py +0 -74
  619. agno/embedder/sentence_transformer.py +0 -38
  620. agno/embedder/voyageai.py +0 -64
  621. agno/eval/perf.py +0 -201
  622. agno/file/__init__.py +0 -1
  623. agno/file/file.py +0 -16
  624. agno/file/local/csv.py +0 -32
  625. agno/file/local/txt.py +0 -19
  626. agno/infra/app.py +0 -240
  627. agno/infra/base.py +0 -144
  628. agno/infra/context.py +0 -20
  629. agno/infra/db_app.py +0 -52
  630. agno/infra/resource.py +0 -205
  631. agno/infra/resources.py +0 -55
  632. agno/knowledge/agent.py +0 -230
  633. agno/knowledge/arxiv.py +0 -22
  634. agno/knowledge/combined.py +0 -22
  635. agno/knowledge/csv.py +0 -28
  636. agno/knowledge/csv_url.py +0 -19
  637. agno/knowledge/document.py +0 -20
  638. agno/knowledge/docx.py +0 -30
  639. agno/knowledge/json.py +0 -28
  640. agno/knowledge/langchain.py +0 -71
  641. agno/knowledge/llamaindex.py +0 -66
  642. agno/knowledge/pdf.py +0 -28
  643. agno/knowledge/pdf_url.py +0 -26
  644. agno/knowledge/s3/base.py +0 -60
  645. agno/knowledge/s3/pdf.py +0 -21
  646. agno/knowledge/s3/text.py +0 -23
  647. agno/knowledge/text.py +0 -30
  648. agno/knowledge/website.py +0 -88
  649. agno/knowledge/wikipedia.py +0 -31
  650. agno/knowledge/youtube.py +0 -22
  651. agno/memory/agent.py +0 -392
  652. agno/memory/classifier.py +0 -104
  653. agno/memory/db/__init__.py +0 -1
  654. agno/memory/db/base.py +0 -42
  655. agno/memory/db/mongodb.py +0 -189
  656. agno/memory/db/postgres.py +0 -203
  657. agno/memory/db/sqlite.py +0 -193
  658. agno/memory/memory.py +0 -15
  659. agno/memory/row.py +0 -36
  660. agno/memory/summarizer.py +0 -192
  661. agno/memory/summary.py +0 -19
  662. agno/memory/workflow.py +0 -38
  663. agno/models/google/gemini_openai.py +0 -26
  664. agno/models/ollama/hermes.py +0 -221
  665. agno/models/ollama/tools.py +0 -362
  666. agno/models/vertexai/gemini.py +0 -595
  667. agno/playground/__init__.py +0 -3
  668. agno/playground/async_router.py +0 -421
  669. agno/playground/deploy.py +0 -249
  670. agno/playground/operator.py +0 -92
  671. agno/playground/playground.py +0 -91
  672. agno/playground/schemas.py +0 -76
  673. agno/playground/serve.py +0 -55
  674. agno/playground/sync_router.py +0 -405
  675. agno/reasoning/agent.py +0 -68
  676. agno/run/response.py +0 -112
  677. agno/storage/agent/__init__.py +0 -0
  678. agno/storage/agent/base.py +0 -38
  679. agno/storage/agent/dynamodb.py +0 -350
  680. agno/storage/agent/json.py +0 -92
  681. agno/storage/agent/mongodb.py +0 -228
  682. agno/storage/agent/postgres.py +0 -367
  683. agno/storage/agent/session.py +0 -79
  684. agno/storage/agent/singlestore.py +0 -303
  685. agno/storage/agent/sqlite.py +0 -357
  686. agno/storage/agent/yaml.py +0 -93
  687. agno/storage/workflow/__init__.py +0 -0
  688. agno/storage/workflow/base.py +0 -40
  689. agno/storage/workflow/mongodb.py +0 -233
  690. agno/storage/workflow/postgres.py +0 -366
  691. agno/storage/workflow/session.py +0 -60
  692. agno/storage/workflow/sqlite.py +0 -359
  693. agno/tools/googlesearch.py +0 -88
  694. agno/utils/defaults.py +0 -57
  695. agno/utils/filesystem.py +0 -39
  696. agno/utils/git.py +0 -52
  697. agno/utils/json_io.py +0 -30
  698. agno/utils/load_env.py +0 -19
  699. agno/utils/py_io.py +0 -19
  700. agno/utils/pyproject.py +0 -18
  701. agno/utils/resource_filter.py +0 -31
  702. agno/vectordb/singlestore/s2vectordb.py +0 -390
  703. agno/vectordb/singlestore/s2vectordb2.py +0 -355
  704. agno/workspace/__init__.py +0 -0
  705. agno/workspace/config.py +0 -325
  706. agno/workspace/enums.py +0 -6
  707. agno/workspace/helpers.py +0 -48
  708. agno/workspace/operator.py +0 -758
  709. agno/workspace/settings.py +0 -63
  710. agno-0.1.2.dist-info/LICENSE +0 -375
  711. agno-0.1.2.dist-info/METADATA +0 -502
  712. agno-0.1.2.dist-info/RECORD +0 -352
  713. agno-0.1.2.dist-info/entry_points.txt +0 -3
  714. /agno/{cli → db/migrations}/__init__.py +0 -0
  715. /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
  716. /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
  717. /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
  718. /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
  719. /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
  720. /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
  721. /agno/{reranker → utils/models}/__init__.py +0 -0
  722. /agno/{storage → utils/print_response}/__init__.py +0 -0
  723. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,743 @@
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.culture import CulturalKnowledge
9
+ from agno.db.schemas.evals import EvalRunRecord
10
+ from agno.db.schemas.knowledge import KnowledgeRow
11
+ from agno.session import Session
12
+ from agno.utils.log import log_debug, log_error, log_info
13
+
14
+ # -- Serialization utils --
15
+
16
+
17
+ def serialize_to_dynamo_item(data: Dict[str, Any]) -> Dict[str, Any]:
18
+ """Serialize the given dict to a valid DynamoDB item
19
+
20
+ Args:
21
+ data: The dict to serialize
22
+
23
+ Returns:
24
+ A DynamoDB-ready dict with the serialized data
25
+
26
+ """
27
+ item: Dict[str, Any] = {}
28
+ for key, value in data.items():
29
+ if value is not None:
30
+ if isinstance(value, (int, float)):
31
+ item[key] = {"N": str(value)}
32
+ elif isinstance(value, str):
33
+ item[key] = {"S": value}
34
+ elif isinstance(value, bool):
35
+ item[key] = {"BOOL": value}
36
+ elif isinstance(value, (dict, list)):
37
+ item[key] = {"S": json.dumps(value)}
38
+ else:
39
+ item[key] = {"S": str(value)}
40
+ return item
41
+
42
+
43
+ def deserialize_from_dynamodb_item(item: Dict[str, Any]) -> Dict[str, Any]:
44
+ data = {}
45
+ for key, value in item.items():
46
+ if "S" in value:
47
+ try:
48
+ data[key] = json.loads(value["S"])
49
+ except (json.JSONDecodeError, TypeError):
50
+ data[key] = value["S"]
51
+ elif "N" in value:
52
+ data[key] = float(value["N"]) if "." in value["N"] else int(value["N"])
53
+ elif "BOOL" in value:
54
+ data[key] = value["BOOL"]
55
+ elif "SS" in value:
56
+ data[key] = value["SS"]
57
+ elif "NS" in value:
58
+ data[key] = [float(n) if "." in n else int(n) for n in value["NS"]]
59
+ elif "M" in value:
60
+ data[key] = deserialize_from_dynamodb_item(value["M"])
61
+ elif "L" in value:
62
+ data[key] = [deserialize_from_dynamodb_item({"item": item})["item"] for item in value["L"]]
63
+ return data
64
+
65
+
66
+ def serialize_knowledge_row(knowledge: KnowledgeRow) -> Dict[str, Any]:
67
+ """Convert KnowledgeRow to DynamoDB item format."""
68
+ return serialize_to_dynamo_item(
69
+ {
70
+ "id": knowledge.id,
71
+ "name": knowledge.name,
72
+ "description": knowledge.description,
73
+ "type": getattr(knowledge, "type", None),
74
+ "status": getattr(knowledge, "status", None),
75
+ "status_message": getattr(knowledge, "status_message", None),
76
+ "metadata": getattr(knowledge, "metadata", None),
77
+ "size": getattr(knowledge, "size", None),
78
+ "linked_to": getattr(knowledge, "linked_to", None),
79
+ "access_count": getattr(knowledge, "access_count", None),
80
+ "created_at": int(knowledge.created_at) if knowledge.created_at else None,
81
+ "updated_at": int(knowledge.updated_at) if knowledge.updated_at else None,
82
+ }
83
+ )
84
+
85
+
86
+ def deserialize_knowledge_row(item: Dict[str, Any]) -> KnowledgeRow:
87
+ """Convert DynamoDB item to KnowledgeRow."""
88
+ data = deserialize_from_dynamodb_item(item)
89
+ return KnowledgeRow(
90
+ id=data["id"],
91
+ name=data["name"],
92
+ description=data["description"],
93
+ metadata=data.get("metadata"),
94
+ type=data.get("type"),
95
+ size=data.get("size"),
96
+ linked_to=data.get("linked_to"),
97
+ access_count=data.get("access_count"),
98
+ status=data.get("status"),
99
+ status_message=data.get("status_message"),
100
+ created_at=data.get("created_at"),
101
+ updated_at=data.get("updated_at"),
102
+ )
103
+
104
+
105
+ def serialize_eval_record(eval_record: EvalRunRecord) -> Dict[str, Any]:
106
+ """Convert EvalRunRecord to DynamoDB item format."""
107
+ return serialize_to_dynamo_item(
108
+ {
109
+ "run_id": eval_record.run_id,
110
+ "eval_type": eval_record.eval_type,
111
+ "eval_data": eval_record.eval_data,
112
+ "name": getattr(eval_record, "name", None),
113
+ "agent_id": getattr(eval_record, "agent_id", None),
114
+ "team_id": getattr(eval_record, "team_id", None),
115
+ "workflow_id": getattr(eval_record, "workflow_id", None),
116
+ "model_id": getattr(eval_record, "model_id", None),
117
+ "model_provider": getattr(eval_record, "model_provider", None),
118
+ "evaluated_component_name": getattr(eval_record, "evaluated_component_name", None),
119
+ }
120
+ )
121
+
122
+
123
+ def deserialize_eval_record(item: Dict[str, Any]) -> EvalRunRecord:
124
+ """Convert DynamoDB item to EvalRunRecord."""
125
+ data = deserialize_from_dynamodb_item(item)
126
+ # Convert timestamp fields back to datetime
127
+ if "created_at" in data and data["created_at"]:
128
+ data["created_at"] = datetime.fromtimestamp(data["created_at"], tz=timezone.utc)
129
+ if "updated_at" in data and data["updated_at"]:
130
+ data["updated_at"] = datetime.fromtimestamp(data["updated_at"], tz=timezone.utc)
131
+ return EvalRunRecord(run_id=data["run_id"], eval_type=data["eval_type"], eval_data=data["eval_data"])
132
+
133
+
134
+ # -- DB Utils --
135
+
136
+
137
+ def create_table_if_not_exists(dynamodb_client, table_name: str, schema: Dict[str, Any]) -> bool:
138
+ """Create DynamoDB table if it doesn't exist."""
139
+ try:
140
+ dynamodb_client.describe_table(TableName=table_name)
141
+ return True
142
+
143
+ except dynamodb_client.exceptions.ResourceNotFoundException:
144
+ log_info(f"Creating table {table_name}")
145
+ try:
146
+ dynamodb_client.create_table(**schema)
147
+ # Wait for table to be created
148
+ waiter = dynamodb_client.get_waiter("table_exists")
149
+ waiter.wait(TableName=table_name)
150
+
151
+ log_debug(f"Table {table_name} created successfully")
152
+
153
+ return True
154
+
155
+ except Exception as e:
156
+ log_error(f"Failed to create table {table_name}: {e}")
157
+ return False
158
+
159
+
160
+ def apply_pagination(
161
+ items: List[Dict[str, Any]], limit: Optional[int] = None, page: Optional[int] = None
162
+ ) -> List[Dict[str, Any]]:
163
+ """Apply pagination to a list of items."""
164
+ if limit is None:
165
+ return items
166
+
167
+ start_index = 0
168
+ if page is not None and page > 1:
169
+ start_index = (page - 1) * limit
170
+
171
+ return items[start_index : start_index + limit]
172
+
173
+
174
+ def apply_sorting(
175
+ items: List[Dict[str, Any]], sort_by: Optional[str] = None, sort_order: Optional[str] = None
176
+ ) -> List[Dict[str, Any]]:
177
+ """Apply sorting to a list of items."""
178
+ if sort_by is None:
179
+ sort_by = "created_at"
180
+
181
+ reverse = sort_order == "desc"
182
+
183
+ return sorted(items, key=lambda x: x.get(sort_by, ""), reverse=reverse)
184
+
185
+
186
+ # -- Session utils --
187
+
188
+
189
+ def prepare_session_data(session: "Session") -> Dict[str, Any]:
190
+ """Prepare session data for storage by serializing JSON fields and setting session type."""
191
+ from agno.session import AgentSession, TeamSession, WorkflowSession
192
+
193
+ serialized_session = session.to_dict()
194
+
195
+ # Handle JSON fields
196
+ json_fields = ["session_data", "memory", "tools", "functions", "additional_data"]
197
+ for field in json_fields:
198
+ if field in serialized_session and serialized_session[field] is not None:
199
+ if isinstance(serialized_session[field], (dict, list)):
200
+ serialized_session[field] = json.dumps(serialized_session[field])
201
+
202
+ # Set the session type
203
+ if isinstance(session, AgentSession):
204
+ serialized_session["session_type"] = SessionType.AGENT.value
205
+ elif isinstance(session, TeamSession):
206
+ serialized_session["session_type"] = SessionType.TEAM.value
207
+ elif isinstance(session, WorkflowSession):
208
+ serialized_session["session_type"] = SessionType.WORKFLOW.value
209
+
210
+ return serialized_session
211
+
212
+
213
+ def merge_with_existing_session(new_session: Dict[str, Any], existing_item: Dict[str, Any]) -> Dict[str, Any]:
214
+ """Merge new session data with existing session, preserving important fields."""
215
+ existing_session = deserialize_from_dynamodb_item(existing_item)
216
+
217
+ # Start with existing session as base
218
+ merged_session = existing_session.copy()
219
+
220
+ if "session_data" in new_session:
221
+ merged_session_data = merge_session_data(
222
+ existing_session.get("session_data", {}), new_session.get("session_data", {})
223
+ )
224
+ merged_session["session_data"] = json.dumps(merged_session_data)
225
+
226
+ for key, value in new_session.items():
227
+ if key != "created_at" and key != "session_data" and value is not None:
228
+ merged_session[key] = value
229
+
230
+ # Always preserve created_at and set updated_at
231
+ merged_session["created_at"] = existing_session.get("created_at")
232
+ merged_session["updated_at"] = int(time.time())
233
+
234
+ return merged_session
235
+
236
+
237
+ def merge_session_data(existing_data: Any, new_data: Any) -> Dict[str, Any]:
238
+ """Merge session_data fields, handling JSON string conversion."""
239
+
240
+ # Parse existing session_data
241
+ if isinstance(existing_data, str):
242
+ existing_data = json.loads(existing_data)
243
+ existing_data = existing_data or {}
244
+
245
+ # Parse new session_data
246
+ if isinstance(new_data, str):
247
+ new_data = json.loads(new_data)
248
+ new_data = new_data or {}
249
+
250
+ # Merge letting new data take precedence
251
+ return {**existing_data, **new_data}
252
+
253
+
254
+ def deserialize_session_result(
255
+ serialized_session: Dict[str, Any], original_session: "Session", deserialize: Optional[bool]
256
+ ) -> Optional[Union["Session", Dict[str, Any]]]:
257
+ """Deserialize the session result based on the deserialize flag and session type."""
258
+ from agno.session import AgentSession, TeamSession, WorkflowSession
259
+
260
+ if not deserialize:
261
+ return serialized_session
262
+
263
+ if isinstance(original_session, AgentSession):
264
+ return AgentSession.from_dict(serialized_session)
265
+ elif isinstance(original_session, TeamSession):
266
+ return TeamSession.from_dict(serialized_session)
267
+ elif isinstance(original_session, WorkflowSession):
268
+ return WorkflowSession.from_dict(serialized_session)
269
+
270
+ return None
271
+
272
+
273
+ def deserialize_session(session: Dict[str, Any]) -> Optional[Session]:
274
+ """Deserialize session data from DynamoDB format to Session object."""
275
+ try:
276
+ deserialized = session.copy()
277
+
278
+ # Handle JSON fields
279
+ json_fields = ["session_data", "memory", "tools", "functions", "additional_data"]
280
+ for field in json_fields:
281
+ if field in deserialized and deserialized[field] is not None:
282
+ if isinstance(deserialized[field], str):
283
+ try:
284
+ deserialized[field] = json.loads(deserialized[field])
285
+ except json.JSONDecodeError:
286
+ log_error(f"Failed to deserialize {field} field")
287
+ deserialized[field] = None
288
+
289
+ # Handle timestamp fields
290
+ for field in ["created_at", "updated_at"]:
291
+ if field in deserialized and deserialized[field] is not None:
292
+ if isinstance(deserialized[field], (int, float)):
293
+ deserialized[field] = datetime.fromtimestamp(deserialized[field], tz=timezone.utc)
294
+ elif isinstance(deserialized[field], str):
295
+ try:
296
+ deserialized[field] = datetime.fromisoformat(deserialized[field])
297
+ except ValueError:
298
+ deserialized[field] = datetime.fromtimestamp(float(deserialized[field]), tz=timezone.utc)
299
+
300
+ return Session.from_dict(deserialized) # type: ignore
301
+
302
+ except Exception as e:
303
+ log_error(f"Failed to deserialize session: {e}")
304
+ return None
305
+
306
+
307
+ # -- Metrics utils --
308
+
309
+
310
+ def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
311
+ """Calculate metrics for the given single date.
312
+ Args:
313
+ date_to_process (date): The date to calculate metrics for.
314
+ sessions_data (dict): The sessions data to calculate metrics for.
315
+ Returns:
316
+ dict: The calculated metrics.
317
+ """
318
+ metrics = {
319
+ "users_count": 0,
320
+ "agent_sessions_count": 0,
321
+ "team_sessions_count": 0,
322
+ "workflow_sessions_count": 0,
323
+ "agent_runs_count": 0,
324
+ "team_runs_count": 0,
325
+ "workflow_runs_count": 0,
326
+ }
327
+ token_metrics = {
328
+ "input_tokens": 0,
329
+ "output_tokens": 0,
330
+ "total_tokens": 0,
331
+ "audio_total_tokens": 0,
332
+ "audio_input_tokens": 0,
333
+ "audio_output_tokens": 0,
334
+ "cache_read_tokens": 0,
335
+ "cache_write_tokens": 0,
336
+ "reasoning_tokens": 0,
337
+ }
338
+ model_counts: Dict[str, int] = {}
339
+ session_types = [
340
+ ("agent", "agent_sessions_count", "agent_runs_count"),
341
+ ("team", "team_sessions_count", "team_runs_count"),
342
+ ("workflow", "workflow_sessions_count", "workflow_runs_count"),
343
+ ]
344
+ all_user_ids = set()
345
+ for session_type, sessions_count_key, runs_count_key in session_types:
346
+ sessions = sessions_data.get(session_type, []) or []
347
+ metrics[sessions_count_key] = len(sessions)
348
+ for session in sessions:
349
+ if session.get("user_id"):
350
+ all_user_ids.add(session["user_id"])
351
+ metrics[runs_count_key] += len(session.get("runs", []))
352
+ if runs := session.get("runs", []):
353
+ for run in runs:
354
+ if model_id := run.get("model"):
355
+ model_provider = run.get("model_provider", "")
356
+ model_counts[f"{model_id}:{model_provider}"] = (
357
+ model_counts.get(f"{model_id}:{model_provider}", 0) + 1
358
+ )
359
+ session_metrics = session.get("session_data", {}).get("session_metrics", {})
360
+ for field in token_metrics:
361
+ token_metrics[field] += session_metrics.get(field, 0)
362
+ model_metrics = []
363
+ for model, count in model_counts.items():
364
+ model_id, model_provider = model.rsplit(":", 1)
365
+ model_metrics.append({"model_id": model_id, "model_provider": model_provider, "count": count})
366
+ metrics["users_count"] = len(all_user_ids)
367
+ current_time = int(time.time())
368
+ return {
369
+ "id": str(uuid4()),
370
+ "date": date_to_process,
371
+ "completed": date_to_process < datetime.now(timezone.utc).date(),
372
+ "token_metrics": token_metrics,
373
+ "model_metrics": model_metrics,
374
+ "created_at": current_time,
375
+ "updated_at": current_time,
376
+ "aggregation_period": "daily",
377
+ **metrics,
378
+ }
379
+
380
+
381
+ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
382
+ """Return the list of dates to calculate metrics for.
383
+ Args:
384
+ starting_date (date): The starting date to calculate metrics for.
385
+ Returns:
386
+ list[date]: The list of dates to calculate metrics for.
387
+ """
388
+ today = datetime.now(timezone.utc).date()
389
+ days_diff = (today - starting_date).days + 1
390
+ if days_diff <= 0:
391
+ return []
392
+ return [starting_date + timedelta(days=x) for x in range(days_diff)]
393
+
394
+
395
+ def fetch_all_sessions_data(
396
+ sessions: List[Dict[str, Any]], dates_to_process: list[date], start_timestamp: int
397
+ ) -> Optional[dict]:
398
+ """Return all session data for the given dates, for all session types.
399
+ Args:
400
+ sessions: List of session data dictionaries
401
+ dates_to_process (list[date]): The dates to fetch session data for.
402
+ start_timestamp: The start timestamp for the range
403
+ Returns:
404
+ dict: A dictionary with dates as keys and session data as values, for all session types.
405
+ Example:
406
+ {
407
+ "2000-01-01": {
408
+ "agent": [<session1>, <session2>, ...],
409
+ "team": [...],
410
+ "workflow": [...],
411
+ }
412
+ }
413
+ """
414
+ if not dates_to_process:
415
+ return None
416
+ all_sessions_data: Dict[str, Dict[str, List[Dict[str, Any]]]] = {
417
+ date_to_process.isoformat(): {"agent": [], "team": [], "workflow": []} for date_to_process in dates_to_process
418
+ }
419
+ for session in sessions:
420
+ session_date = (
421
+ datetime.fromtimestamp(session.get("created_at", start_timestamp), tz=timezone.utc).date().isoformat()
422
+ )
423
+ if session_date in all_sessions_data:
424
+ all_sessions_data[session_date][session["session_type"]].append(session)
425
+ return all_sessions_data
426
+
427
+
428
+ def fetch_all_sessions_data_by_type(
429
+ dynamodb_client,
430
+ table_name: str,
431
+ session_type: str,
432
+ user_id: Optional[str] = None,
433
+ component_id: Optional[str] = None,
434
+ session_name: Optional[str] = None,
435
+ ) -> List[Dict[str, Any]]:
436
+ """Fetch all sessions data from DynamoDB table using GSI for session_type."""
437
+ items = []
438
+
439
+ try:
440
+ # Build filter expression for additional filters
441
+ filter_expression = None
442
+ expression_attribute_names = {}
443
+ expression_attribute_values = {":session_type": {"S": session_type}}
444
+
445
+ if user_id:
446
+ filter_expression = "#user_id = :user_id"
447
+ expression_attribute_names["#user_id"] = "user_id"
448
+ expression_attribute_values[":user_id"] = {"S": user_id}
449
+
450
+ if component_id:
451
+ component_filter = "#component_id = :component_id"
452
+ expression_attribute_names["#component_id"] = "component_id"
453
+ expression_attribute_values[":component_id"] = {"S": component_id}
454
+
455
+ if filter_expression:
456
+ filter_expression += f" AND {component_filter}"
457
+ else:
458
+ filter_expression = component_filter
459
+
460
+ if session_name:
461
+ name_filter = "#session_name = :session_name"
462
+ expression_attribute_names["#session_name"] = "session_name"
463
+ expression_attribute_values[":session_name"] = {"S": session_name}
464
+
465
+ if filter_expression:
466
+ filter_expression += f" AND {name_filter}"
467
+ else:
468
+ filter_expression = name_filter
469
+
470
+ # Use GSI query for session_type (more efficient than scan)
471
+ query_kwargs = {
472
+ "TableName": table_name,
473
+ "IndexName": "session_type-created_at-index",
474
+ "KeyConditionExpression": "session_type = :session_type",
475
+ "ExpressionAttributeValues": expression_attribute_values,
476
+ }
477
+
478
+ if filter_expression:
479
+ query_kwargs["FilterExpression"] = filter_expression
480
+
481
+ if expression_attribute_names:
482
+ query_kwargs["ExpressionAttributeNames"] = expression_attribute_names
483
+
484
+ response = dynamodb_client.query(**query_kwargs)
485
+ items.extend(response.get("Items", []))
486
+
487
+ # Handle pagination
488
+ while "LastEvaluatedKey" in response:
489
+ query_kwargs["ExclusiveStartKey"] = response["LastEvaluatedKey"]
490
+ response = dynamodb_client.query(**query_kwargs)
491
+ items.extend(response.get("Items", []))
492
+
493
+ except Exception as e:
494
+ log_error(f"Failed to fetch sessions: {e}")
495
+
496
+ return items
497
+
498
+
499
+ def bulk_upsert_metrics(dynamodb_client, table_name: str, metrics_data: List[Dict[str, Any]]) -> None:
500
+ """Bulk upsert metrics data to DynamoDB."""
501
+ try:
502
+ # DynamoDB batch write has a limit of 25 items
503
+ batch_size = 25
504
+
505
+ for i in range(0, len(metrics_data), batch_size):
506
+ batch = metrics_data[i : i + batch_size]
507
+
508
+ request_items: Dict[str, List[Dict[str, Any]]] = {table_name: []}
509
+
510
+ for metric in batch:
511
+ request_items[table_name].append({"PutRequest": {"Item": metric}})
512
+
513
+ dynamodb_client.batch_write_item(RequestItems=request_items)
514
+
515
+ except Exception as e:
516
+ log_error(f"Failed to bulk upsert metrics: {e}")
517
+
518
+
519
+ # -- Query utils --
520
+
521
+
522
+ def build_query_filter_expression(filters: Dict[str, Any]) -> tuple[Optional[str], Dict[str, str], Dict[str, Any]]:
523
+ """Build DynamoDB query filter expression from filters dictionary.
524
+
525
+ Args:
526
+ filters: Dictionary of filter key-value pairs
527
+
528
+ Returns:
529
+ Tuple of (filter_expression, expression_attribute_names, expression_attribute_values)
530
+ """
531
+ filter_expressions = []
532
+ expression_attribute_names = {}
533
+ expression_attribute_values = {}
534
+
535
+ for field, value in filters.items():
536
+ if value is not None:
537
+ filter_expressions.append(f"#{field} = :{field}")
538
+ expression_attribute_names[f"#{field}"] = field
539
+ expression_attribute_values[f":{field}"] = {"S": value}
540
+
541
+ filter_expression = " AND ".join(filter_expressions) if filter_expressions else None
542
+ return filter_expression, expression_attribute_names, expression_attribute_values
543
+
544
+
545
+ def build_topic_filter_expression(topics: List[str]) -> tuple[str, Dict[str, Any]]:
546
+ """Build DynamoDB filter expression for topics.
547
+
548
+ Args:
549
+ topics: List of topics to filter by
550
+
551
+ Returns:
552
+ Tuple of (filter_expression, expression_attribute_values)
553
+ """
554
+ topic_filters = []
555
+ expression_attribute_values = {}
556
+
557
+ for i, topic in enumerate(topics):
558
+ topic_key = f":topic_{i}"
559
+ topic_filters.append(f"contains(topics, {topic_key})")
560
+ expression_attribute_values[topic_key] = {"S": topic}
561
+
562
+ filter_expression = f"({' OR '.join(topic_filters)})"
563
+ return filter_expression, expression_attribute_values
564
+
565
+
566
+ def execute_query_with_pagination(
567
+ dynamodb_client,
568
+ table_name: str,
569
+ index_name: str,
570
+ key_condition_expression: str,
571
+ expression_attribute_names: Dict[str, str],
572
+ expression_attribute_values: Dict[str, Any],
573
+ filter_expression: Optional[str] = None,
574
+ sort_by: Optional[str] = None,
575
+ sort_order: Optional[str] = None,
576
+ limit: Optional[int] = None,
577
+ page: Optional[int] = None,
578
+ ) -> List[Dict[str, Any]]:
579
+ """Execute DynamoDB query with pagination support.
580
+
581
+ Args:
582
+ dynamodb_client: DynamoDB client
583
+ table_name: Table name
584
+ index_name: Index name for query
585
+ key_condition_expression: Key condition expression
586
+ expression_attribute_names: Expression attribute names
587
+ expression_attribute_values: Expression attribute values
588
+ filter_expression: Optional filter expression
589
+ sort_by: Field to sort by
590
+ sort_order: Sort order (asc/desc)
591
+ limit: Limit for pagination
592
+ page: Page number
593
+
594
+ Returns:
595
+ List of DynamoDB items
596
+ """
597
+ query_kwargs = {
598
+ "TableName": table_name,
599
+ "IndexName": index_name,
600
+ "KeyConditionExpression": key_condition_expression,
601
+ "ExpressionAttributeValues": expression_attribute_values,
602
+ }
603
+
604
+ if expression_attribute_names:
605
+ query_kwargs["ExpressionAttributeNames"] = expression_attribute_names
606
+
607
+ if filter_expression:
608
+ query_kwargs["FilterExpression"] = filter_expression
609
+
610
+ # Apply sorting at query level if sorting by created_at
611
+ if sort_by == "created_at":
612
+ query_kwargs["ScanIndexForward"] = sort_order != "desc" # type: ignore
613
+
614
+ # Apply limit at DynamoDB level if no pagination
615
+ if limit and not page:
616
+ query_kwargs["Limit"] = limit # type: ignore
617
+
618
+ items = []
619
+ response = dynamodb_client.query(**query_kwargs)
620
+ items.extend(response.get("Items", []))
621
+
622
+ # Handle pagination
623
+ while "LastEvaluatedKey" in response:
624
+ query_kwargs["ExclusiveStartKey"] = response["LastEvaluatedKey"]
625
+ response = dynamodb_client.query(**query_kwargs)
626
+ items.extend(response.get("Items", []))
627
+
628
+ return items
629
+
630
+
631
+ def process_query_results(
632
+ items: List[Dict[str, Any]],
633
+ sort_by: Optional[str] = None,
634
+ sort_order: Optional[str] = None,
635
+ limit: Optional[int] = None,
636
+ page: Optional[int] = None,
637
+ deserialize_func: Optional[Callable] = None,
638
+ deserialize: bool = True,
639
+ ) -> Union[List[Any], tuple[List[Any], int]]:
640
+ """Process query results with sorting, pagination, and deserialization.
641
+
642
+ Args:
643
+ items: List of DynamoDB items
644
+ sort_by: Field to sort by
645
+ sort_order: Sort order (asc/desc)
646
+ limit: Limit for pagination
647
+ page: Page number
648
+ deserialize_func: Function to deserialize items
649
+ deserialize: Whether to deserialize items
650
+
651
+ Returns:
652
+ List of processed items or tuple of (items, total_count)
653
+ """
654
+ # Convert DynamoDB items to data
655
+ processed_data = []
656
+ for item in items:
657
+ data = deserialize_from_dynamodb_item(item)
658
+ if data:
659
+ processed_data.append(data)
660
+
661
+ # Apply in-memory sorting for fields not handled by DynamoDB
662
+ if sort_by and sort_by != "created_at":
663
+ processed_data = apply_sorting(processed_data, sort_by, sort_order)
664
+
665
+ # Get total count before pagination
666
+ total_count = len(processed_data)
667
+
668
+ # Apply pagination
669
+ if page:
670
+ processed_data = apply_pagination(processed_data, limit, page)
671
+
672
+ if not deserialize or not deserialize_func:
673
+ return processed_data, total_count
674
+
675
+ # Deserialize items
676
+ deserialized_items = []
677
+ for data in processed_data:
678
+ try:
679
+ item = deserialize_func(data)
680
+ if item:
681
+ deserialized_items.append(item)
682
+ except Exception as e:
683
+ log_error(f"Failed to deserialize item: {e}")
684
+
685
+ return deserialized_items
686
+
687
+
688
+ # -- Cultural Knowledge util methods --
689
+ def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
690
+ """Serialize a CulturalKnowledge object for database storage.
691
+
692
+ Converts the model's separate content, categories, and notes fields
693
+ into a single JSON dict for the database content column.
694
+ DynamoDB supports nested maps/dicts natively.
695
+
696
+ Args:
697
+ cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
698
+
699
+ Returns:
700
+ Dict[str, Any]: A dictionary with the content field as a dict containing content, categories, and notes.
701
+ """
702
+ content_dict: Dict[str, Any] = {}
703
+ if cultural_knowledge.content is not None:
704
+ content_dict["content"] = cultural_knowledge.content
705
+ if cultural_knowledge.categories is not None:
706
+ content_dict["categories"] = cultural_knowledge.categories
707
+ if cultural_knowledge.notes is not None:
708
+ content_dict["notes"] = cultural_knowledge.notes
709
+
710
+ return content_dict if content_dict else {}
711
+
712
+
713
+ def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
714
+ """Deserialize a database row to a CulturalKnowledge object.
715
+
716
+ The database stores content as a dict containing content, categories, and notes.
717
+ This method extracts those fields and converts them back to the model format.
718
+
719
+ Args:
720
+ db_row (Dict[str, Any]): The database row as a dictionary.
721
+
722
+ Returns:
723
+ CulturalKnowledge: The cultural knowledge object.
724
+ """
725
+ # Extract content, categories, and notes from the content field
726
+ content_json = db_row.get("content", {}) or {}
727
+
728
+ return CulturalKnowledge.from_dict(
729
+ {
730
+ "id": db_row.get("id"),
731
+ "name": db_row.get("name"),
732
+ "summary": db_row.get("summary"),
733
+ "content": content_json.get("content"),
734
+ "categories": content_json.get("categories"),
735
+ "notes": content_json.get("notes"),
736
+ "metadata": db_row.get("metadata"),
737
+ "input": db_row.get("input"),
738
+ "created_at": db_row.get("created_at"),
739
+ "updated_at": db_row.get("updated_at"),
740
+ "agent_id": db_row.get("agent_id"),
741
+ "team_id": db_row.get("team_id"),
742
+ }
743
+ )