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,661 @@
1
+ import logging
2
+ import math
3
+ from typing import List, Optional, Union, cast
4
+ from uuid import uuid4
5
+
6
+ from fastapi import Depends, HTTPException, Path, Query, Request
7
+ from fastapi.routing import APIRouter
8
+
9
+ from agno.db.base import AsyncBaseDb, BaseDb
10
+ from agno.db.schemas import UserMemory
11
+ from agno.models.utils import get_model
12
+ from agno.os.auth import get_authentication_dependency
13
+ from agno.os.routers.memory.schemas import (
14
+ DeleteMemoriesRequest,
15
+ OptimizeMemoriesRequest,
16
+ OptimizeMemoriesResponse,
17
+ UserMemoryCreateSchema,
18
+ UserMemorySchema,
19
+ UserStatsSchema,
20
+ )
21
+ from agno.os.schema import (
22
+ BadRequestResponse,
23
+ InternalServerErrorResponse,
24
+ NotFoundResponse,
25
+ PaginatedResponse,
26
+ PaginationInfo,
27
+ SortOrder,
28
+ UnauthenticatedResponse,
29
+ ValidationErrorResponse,
30
+ )
31
+ from agno.os.settings import AgnoAPISettings
32
+ from agno.os.utils import get_db
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+
37
+ def get_memory_router(
38
+ dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], settings: AgnoAPISettings = AgnoAPISettings(), **kwargs
39
+ ) -> APIRouter:
40
+ """Create memory router with comprehensive OpenAPI documentation for user memory management endpoints."""
41
+ router = APIRouter(
42
+ dependencies=[Depends(get_authentication_dependency(settings))],
43
+ tags=["Memory"],
44
+ responses={
45
+ 400: {"description": "Bad Request", "model": BadRequestResponse},
46
+ 401: {"description": "Unauthorized", "model": UnauthenticatedResponse},
47
+ 404: {"description": "Not Found", "model": NotFoundResponse},
48
+ 422: {"description": "Validation Error", "model": ValidationErrorResponse},
49
+ 500: {"description": "Internal Server Error", "model": InternalServerErrorResponse},
50
+ },
51
+ )
52
+ return attach_routes(router=router, dbs=dbs)
53
+
54
+
55
+ def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]]) -> APIRouter:
56
+ @router.post(
57
+ "/memories",
58
+ response_model=UserMemorySchema,
59
+ status_code=200,
60
+ operation_id="create_memory",
61
+ summary="Create Memory",
62
+ description=(
63
+ "Create a new user memory with content and associated topics. "
64
+ "Memories are used to store contextual information for users across conversations."
65
+ ),
66
+ responses={
67
+ 200: {
68
+ "description": "Memory created successfully",
69
+ "content": {
70
+ "application/json": {
71
+ "example": {
72
+ "memory_id": "mem-123",
73
+ "memory": "User prefers technical explanations with code examples",
74
+ "topics": ["preferences", "communication_style", "technical"],
75
+ "user_id": "user-456",
76
+ "created_at": "2024-01-15T10:30:00Z",
77
+ "updated_at": "2024-01-15T10:30:00Z",
78
+ }
79
+ }
80
+ },
81
+ },
82
+ 400: {"description": "Invalid request data", "model": BadRequestResponse},
83
+ 422: {"description": "Validation error in payload", "model": ValidationErrorResponse},
84
+ 500: {"description": "Failed to create memory", "model": InternalServerErrorResponse},
85
+ },
86
+ )
87
+ async def create_memory(
88
+ request: Request,
89
+ payload: UserMemoryCreateSchema,
90
+ db_id: Optional[str] = Query(default=None, description="Database ID to use for memory storage"),
91
+ table: Optional[str] = Query(default=None, description="Table to use for memory storage"),
92
+ ) -> UserMemorySchema:
93
+ if hasattr(request.state, "user_id"):
94
+ user_id = request.state.user_id
95
+ payload.user_id = user_id
96
+
97
+ if payload.user_id is None:
98
+ raise HTTPException(status_code=400, detail="User ID is required")
99
+
100
+ db = await get_db(dbs, db_id, table)
101
+
102
+ if isinstance(db, AsyncBaseDb):
103
+ db = cast(AsyncBaseDb, db)
104
+ user_memory = await db.upsert_user_memory(
105
+ memory=UserMemory(
106
+ memory_id=str(uuid4()),
107
+ memory=payload.memory,
108
+ topics=payload.topics or [],
109
+ user_id=payload.user_id,
110
+ ),
111
+ deserialize=False,
112
+ )
113
+ else:
114
+ user_memory = db.upsert_user_memory(
115
+ memory=UserMemory(
116
+ memory_id=str(uuid4()),
117
+ memory=payload.memory,
118
+ topics=payload.topics or [],
119
+ user_id=payload.user_id,
120
+ ),
121
+ deserialize=False,
122
+ )
123
+
124
+ if not user_memory:
125
+ raise HTTPException(status_code=500, detail="Failed to create memory")
126
+
127
+ return UserMemorySchema.from_dict(user_memory) # type: ignore
128
+
129
+ @router.delete(
130
+ "/memories/{memory_id}",
131
+ status_code=204,
132
+ operation_id="delete_memory",
133
+ summary="Delete Memory",
134
+ description="Permanently delete a specific user memory. This action cannot be undone.",
135
+ responses={
136
+ 204: {"description": "Memory deleted successfully"},
137
+ 404: {"description": "Memory not found", "model": NotFoundResponse},
138
+ 500: {"description": "Failed to delete memory", "model": InternalServerErrorResponse},
139
+ },
140
+ )
141
+ async def delete_memory(
142
+ memory_id: str = Path(description="Memory ID to delete"),
143
+ user_id: Optional[str] = Query(default=None, description="User ID to delete memory for"),
144
+ db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
145
+ table: Optional[str] = Query(default=None, description="Table to use for deletion"),
146
+ ) -> None:
147
+ db = await get_db(dbs, db_id, table)
148
+ if isinstance(db, AsyncBaseDb):
149
+ db = cast(AsyncBaseDb, db)
150
+ await db.delete_user_memory(memory_id=memory_id, user_id=user_id)
151
+ else:
152
+ db.delete_user_memory(memory_id=memory_id, user_id=user_id)
153
+
154
+ @router.delete(
155
+ "/memories",
156
+ status_code=204,
157
+ operation_id="delete_memories",
158
+ summary="Delete Multiple Memories",
159
+ description=(
160
+ "Delete multiple user memories by their IDs in a single operation. "
161
+ "This action cannot be undone and all specified memories will be permanently removed."
162
+ ),
163
+ responses={
164
+ 204: {"description": "Memories deleted successfully"},
165
+ 400: {"description": "Invalid request - empty memory_ids list", "model": BadRequestResponse},
166
+ 500: {"description": "Failed to delete memories", "model": InternalServerErrorResponse},
167
+ },
168
+ )
169
+ async def delete_memories(
170
+ request: DeleteMemoriesRequest,
171
+ db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
172
+ table: Optional[str] = Query(default=None, description="Table to use for deletion"),
173
+ ) -> None:
174
+ db = await get_db(dbs, db_id, table)
175
+ if isinstance(db, AsyncBaseDb):
176
+ db = cast(AsyncBaseDb, db)
177
+ await db.delete_user_memories(memory_ids=request.memory_ids, user_id=request.user_id)
178
+ else:
179
+ db.delete_user_memories(memory_ids=request.memory_ids, user_id=request.user_id)
180
+
181
+ @router.get(
182
+ "/memories",
183
+ response_model=PaginatedResponse[UserMemorySchema],
184
+ status_code=200,
185
+ operation_id="get_memories",
186
+ summary="List Memories",
187
+ description=(
188
+ "Retrieve paginated list of user memories with filtering and search capabilities. "
189
+ "Filter by user, agent, team, topics, or search within memory content."
190
+ ),
191
+ responses={
192
+ 200: {
193
+ "description": "Memories retrieved successfully",
194
+ "content": {
195
+ "application/json": {
196
+ "example": {
197
+ "data": [
198
+ {
199
+ "memory_id": "f9361a69-2997-40c7-ae4e-a5861d434047",
200
+ "memory": "User likes coffee.",
201
+ "topics": ["preferences"],
202
+ "agent_id": None,
203
+ "team_id": None,
204
+ "user_id": "123",
205
+ "updated_at": "2025-09-01T07:53:17Z",
206
+ }
207
+ ]
208
+ }
209
+ }
210
+ },
211
+ }
212
+ },
213
+ )
214
+ async def get_memories(
215
+ request: Request,
216
+ user_id: Optional[str] = Query(default=None, description="Filter memories by user ID"),
217
+ agent_id: Optional[str] = Query(default=None, description="Filter memories by agent ID"),
218
+ team_id: Optional[str] = Query(default=None, description="Filter memories by team ID"),
219
+ topics: Optional[List[str]] = Depends(parse_topics),
220
+ search_content: Optional[str] = Query(default=None, description="Fuzzy search within memory content"),
221
+ limit: Optional[int] = Query(default=20, description="Number of memories to return per page"),
222
+ page: Optional[int] = Query(default=1, description="Page number for pagination"),
223
+ sort_by: Optional[str] = Query(default="updated_at", description="Field to sort memories by"),
224
+ sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
225
+ db_id: Optional[str] = Query(default=None, description="Database ID to query memories from"),
226
+ table: Optional[str] = Query(default=None, description="The database table to use"),
227
+ ) -> PaginatedResponse[UserMemorySchema]:
228
+ db = await get_db(dbs, db_id, table)
229
+
230
+ if hasattr(request.state, "user_id"):
231
+ user_id = request.state.user_id
232
+
233
+ if isinstance(db, AsyncBaseDb):
234
+ db = cast(AsyncBaseDb, db)
235
+ user_memories, total_count = await db.get_user_memories(
236
+ limit=limit,
237
+ page=page,
238
+ user_id=user_id,
239
+ agent_id=agent_id,
240
+ team_id=team_id,
241
+ topics=topics,
242
+ search_content=search_content,
243
+ sort_by=sort_by,
244
+ sort_order=sort_order,
245
+ deserialize=False,
246
+ )
247
+ else:
248
+ user_memories, total_count = db.get_user_memories( # type: ignore
249
+ limit=limit,
250
+ page=page,
251
+ user_id=user_id,
252
+ agent_id=agent_id,
253
+ team_id=team_id,
254
+ topics=topics,
255
+ search_content=search_content,
256
+ sort_by=sort_by,
257
+ sort_order=sort_order,
258
+ deserialize=False,
259
+ )
260
+
261
+ memories = [UserMemorySchema.from_dict(user_memory) for user_memory in user_memories] # type: ignore
262
+ return PaginatedResponse(
263
+ data=[memory for memory in memories if memory is not None],
264
+ meta=PaginationInfo(
265
+ page=page,
266
+ limit=limit,
267
+ total_count=total_count, # type: ignore
268
+ total_pages=math.ceil(total_count / limit) if limit is not None and limit > 0 else 0, # type: ignore
269
+ ),
270
+ )
271
+
272
+ @router.get(
273
+ "/memories/{memory_id}",
274
+ response_model=UserMemorySchema,
275
+ status_code=200,
276
+ operation_id="get_memory",
277
+ summary="Get Memory by ID",
278
+ description="Retrieve detailed information about a specific user memory by its ID.",
279
+ responses={
280
+ 200: {
281
+ "description": "Memory retrieved successfully",
282
+ "content": {
283
+ "application/json": {
284
+ "example": {
285
+ "memory_id": "f9361a69-2997-40c7-ae4e-a5861d434047",
286
+ "memory": "User likes coffee.",
287
+ "topics": ["preferences"],
288
+ "agent_id": None,
289
+ "team_id": None,
290
+ "user_id": "123",
291
+ "updated_at": "2025-09-01T07:53:17Z",
292
+ }
293
+ }
294
+ },
295
+ },
296
+ 404: {"description": "Memory not found", "model": NotFoundResponse},
297
+ },
298
+ )
299
+ async def get_memory(
300
+ request: Request,
301
+ memory_id: str = Path(description="Memory ID to retrieve"),
302
+ user_id: Optional[str] = Query(default=None, description="User ID to query memory for"),
303
+ db_id: Optional[str] = Query(default=None, description="Database ID to query memory from"),
304
+ table: Optional[str] = Query(default=None, description="Table to query memory from"),
305
+ ) -> UserMemorySchema:
306
+ db = await get_db(dbs, db_id, table)
307
+
308
+ if hasattr(request.state, "user_id"):
309
+ user_id = request.state.user_id
310
+
311
+ if isinstance(db, AsyncBaseDb):
312
+ db = cast(AsyncBaseDb, db)
313
+ user_memory = await db.get_user_memory(memory_id=memory_id, user_id=user_id, deserialize=False)
314
+ else:
315
+ user_memory = db.get_user_memory(memory_id=memory_id, user_id=user_id, deserialize=False)
316
+ if not user_memory:
317
+ raise HTTPException(status_code=404, detail=f"Memory with ID {memory_id} not found")
318
+
319
+ return UserMemorySchema.from_dict(user_memory) # type: ignore
320
+
321
+ @router.get(
322
+ "/memory_topics",
323
+ response_model=List[str],
324
+ status_code=200,
325
+ operation_id="get_memory_topics",
326
+ summary="Get Memory Topics",
327
+ description=(
328
+ "Retrieve all unique topics associated with memories in the system. "
329
+ "Useful for filtering and categorizing memories by topic."
330
+ ),
331
+ responses={
332
+ 200: {
333
+ "description": "Memory topics retrieved successfully",
334
+ "content": {
335
+ "application/json": {
336
+ "example": [
337
+ "preferences",
338
+ "communication_style",
339
+ "technical",
340
+ "industry",
341
+ "compliance",
342
+ "code_examples",
343
+ "requirements",
344
+ "healthcare",
345
+ "finance",
346
+ ]
347
+ }
348
+ },
349
+ }
350
+ },
351
+ )
352
+ async def get_topics(
353
+ db_id: Optional[str] = Query(default=None, description="Database ID to query topics from"),
354
+ table: Optional[str] = Query(default=None, description="Table to query topics from"),
355
+ ) -> List[str]:
356
+ db = await get_db(dbs, db_id, table)
357
+ if isinstance(db, AsyncBaseDb):
358
+ db = cast(AsyncBaseDb, db)
359
+ return await db.get_all_memory_topics()
360
+ else:
361
+ return db.get_all_memory_topics()
362
+
363
+ @router.patch(
364
+ "/memories/{memory_id}",
365
+ response_model=UserMemorySchema,
366
+ status_code=200,
367
+ operation_id="update_memory",
368
+ summary="Update Memory",
369
+ description=(
370
+ "Update an existing user memory's content and topics. "
371
+ "Replaces the entire memory content and topic list with the provided values."
372
+ ),
373
+ responses={
374
+ 200: {
375
+ "description": "Memory updated successfully",
376
+ "content": {
377
+ "application/json": {
378
+ "example": {
379
+ "memory_id": "f9361a69-2997-40c7-ae4e-a5861d434047",
380
+ "memory": "User likes coffee.",
381
+ "topics": ["preferences"],
382
+ "agent_id": None,
383
+ "team_id": None,
384
+ "user_id": "123",
385
+ "updated_at": "2025-09-01T07:53:17Z",
386
+ }
387
+ }
388
+ },
389
+ },
390
+ 400: {"description": "Invalid request data", "model": BadRequestResponse},
391
+ 404: {"description": "Memory not found", "model": NotFoundResponse},
392
+ 422: {"description": "Validation error in payload", "model": ValidationErrorResponse},
393
+ 500: {"description": "Failed to update memory", "model": InternalServerErrorResponse},
394
+ },
395
+ )
396
+ async def update_memory(
397
+ request: Request,
398
+ payload: UserMemoryCreateSchema,
399
+ memory_id: str = Path(description="Memory ID to update"),
400
+ db_id: Optional[str] = Query(default=None, description="Database ID to use for update"),
401
+ table: Optional[str] = Query(default=None, description="Table to use for update"),
402
+ ) -> UserMemorySchema:
403
+ if hasattr(request.state, "user_id"):
404
+ user_id = request.state.user_id
405
+ payload.user_id = user_id
406
+
407
+ if payload.user_id is None:
408
+ raise HTTPException(status_code=400, detail="User ID is required")
409
+
410
+ db = await get_db(dbs, db_id, table)
411
+
412
+ if isinstance(db, AsyncBaseDb):
413
+ db = cast(AsyncBaseDb, db)
414
+ user_memory = await db.upsert_user_memory(
415
+ memory=UserMemory(
416
+ memory_id=memory_id,
417
+ memory=payload.memory,
418
+ topics=payload.topics or [],
419
+ user_id=payload.user_id,
420
+ ),
421
+ deserialize=False,
422
+ )
423
+ else:
424
+ user_memory = db.upsert_user_memory(
425
+ memory=UserMemory(
426
+ memory_id=memory_id,
427
+ memory=payload.memory,
428
+ topics=payload.topics or [],
429
+ user_id=payload.user_id,
430
+ ),
431
+ deserialize=False,
432
+ )
433
+ if not user_memory:
434
+ raise HTTPException(status_code=500, detail="Failed to update memory")
435
+
436
+ return UserMemorySchema.from_dict(user_memory) # type: ignore
437
+
438
+ @router.get(
439
+ "/user_memory_stats",
440
+ response_model=PaginatedResponse[UserStatsSchema],
441
+ status_code=200,
442
+ operation_id="get_user_memory_stats",
443
+ summary="Get User Memory Statistics",
444
+ description=(
445
+ "Retrieve paginated statistics about memory usage by user. "
446
+ "Provides insights into user engagement and memory distribution across users."
447
+ ),
448
+ responses={
449
+ 200: {
450
+ "description": "User memory statistics retrieved successfully",
451
+ "content": {
452
+ "application/json": {
453
+ "example": {
454
+ "data": [
455
+ {
456
+ "user_id": "123",
457
+ "total_memories": 3,
458
+ "last_memory_updated_at": "2025-09-01T07:53:17Z",
459
+ }
460
+ ]
461
+ }
462
+ }
463
+ },
464
+ },
465
+ 500: {"description": "Failed to retrieve user statistics", "model": InternalServerErrorResponse},
466
+ },
467
+ )
468
+ async def get_user_memory_stats(
469
+ limit: Optional[int] = Query(default=20, description="Number of user statistics to return per page"),
470
+ page: Optional[int] = Query(default=1, description="Page number for pagination"),
471
+ db_id: Optional[str] = Query(default=None, description="Database ID to query statistics from"),
472
+ table: Optional[str] = Query(default=None, description="Table to query statistics from"),
473
+ ) -> PaginatedResponse[UserStatsSchema]:
474
+ db = await get_db(dbs, db_id, table)
475
+ try:
476
+ # Ensure limit and page are integers
477
+ limit = int(limit) if limit is not None else 20
478
+ page = int(page) if page is not None else 1
479
+ if isinstance(db, AsyncBaseDb):
480
+ db = cast(AsyncBaseDb, db)
481
+ user_stats, total_count = await db.get_user_memory_stats(
482
+ limit=limit,
483
+ page=page,
484
+ )
485
+ else:
486
+ user_stats, total_count = db.get_user_memory_stats(
487
+ limit=limit,
488
+ page=page,
489
+ )
490
+ return PaginatedResponse(
491
+ data=[UserStatsSchema.from_dict(stats) for stats in user_stats],
492
+ meta=PaginationInfo(
493
+ page=page,
494
+ limit=limit,
495
+ total_count=total_count,
496
+ total_pages=(total_count + limit - 1) // limit if limit is not None and limit > 0 else 0,
497
+ ),
498
+ )
499
+
500
+ except Exception as e:
501
+ raise HTTPException(status_code=500, detail=f"Failed to get user statistics: {str(e)}")
502
+
503
+ @router.post(
504
+ "/optimize-memories",
505
+ response_model=OptimizeMemoriesResponse,
506
+ status_code=200,
507
+ operation_id="optimize_memories",
508
+ summary="Optimize User Memories",
509
+ description=(
510
+ "Optimize all memories for a given user using the default summarize strategy. "
511
+ "This operation combines all memories into a single comprehensive summary, "
512
+ "achieving maximum token reduction while preserving all key information. "
513
+ "To use a custom model, specify the model parameter in 'provider:model_id' format "
514
+ "(e.g., 'openai:gpt-4o-mini', 'anthropic:claude-3-5-sonnet-20241022'). "
515
+ "If not specified, uses MemoryManager's default model (gpt-4o). "
516
+ "Set apply=false to preview optimization results without saving to database."
517
+ ),
518
+ responses={
519
+ 200: {
520
+ "description": "Memories optimized successfully",
521
+ "content": {
522
+ "application/json": {
523
+ "example": {
524
+ "memories": [
525
+ {
526
+ "memory_id": "f9361a69-2997-40c7-ae4e-a5861d434047",
527
+ "memory": "User has a 3-year-old golden retriever named Max who loves fetch and walks. Lives in San Francisco's Mission district, works as a product manager in tech. Enjoys hiking Bay Area trails, trying new restaurants (especially Japanese, Thai, Mexican), and learning piano for 1.5 years.",
528
+ "topics": ["pets", "location", "work", "hobbies", "food_preferences"],
529
+ "user_id": "user2",
530
+ "updated_at": "2025-11-18T10:30:00Z",
531
+ }
532
+ ],
533
+ "memories_before": 4,
534
+ "memories_after": 1,
535
+ "tokens_before": 450,
536
+ "tokens_after": 180,
537
+ "tokens_saved": 270,
538
+ "reduction_percentage": 60.0,
539
+ }
540
+ }
541
+ },
542
+ },
543
+ 400: {
544
+ "description": "Bad request - User ID is required or invalid model string format",
545
+ "model": BadRequestResponse,
546
+ },
547
+ 404: {"description": "No memories found for user", "model": NotFoundResponse},
548
+ 500: {"description": "Failed to optimize memories", "model": InternalServerErrorResponse},
549
+ },
550
+ )
551
+ async def optimize_memories(
552
+ request: OptimizeMemoriesRequest,
553
+ db_id: Optional[str] = Query(default=None, description="Database ID to use for optimization"),
554
+ table: Optional[str] = Query(default=None, description="Table to use for optimization"),
555
+ ) -> OptimizeMemoriesResponse:
556
+ """Optimize user memories using the default summarize strategy."""
557
+ from agno.memory import MemoryManager
558
+ from agno.memory.strategies.types import MemoryOptimizationStrategyType
559
+
560
+ try:
561
+ # Get database instance
562
+ db = await get_db(dbs, db_id, table)
563
+
564
+ # Create memory manager with optional model
565
+ if request.model:
566
+ try:
567
+ model_instance = get_model(request.model)
568
+ except ValueError as e:
569
+ raise HTTPException(status_code=400, detail=str(e))
570
+ memory_manager = MemoryManager(model=model_instance, db=db)
571
+ else:
572
+ # No model specified - use MemoryManager's default
573
+ memory_manager = MemoryManager(db=db)
574
+
575
+ # Get current memories to count tokens before optimization
576
+ if isinstance(db, AsyncBaseDb):
577
+ memories_before = await memory_manager.aget_user_memories(user_id=request.user_id)
578
+ else:
579
+ memories_before = memory_manager.get_user_memories(user_id=request.user_id)
580
+
581
+ if not memories_before:
582
+ raise HTTPException(status_code=404, detail=f"No memories found for user {request.user_id}")
583
+
584
+ # Count tokens before optimization
585
+ from agno.memory.strategies.summarize import SummarizeStrategy
586
+
587
+ strategy = SummarizeStrategy()
588
+ tokens_before = strategy.count_tokens(memories_before)
589
+ memories_before_count = len(memories_before)
590
+
591
+ # Optimize memories with default SUMMARIZE strategy
592
+ if isinstance(db, AsyncBaseDb):
593
+ optimized_memories = await memory_manager.aoptimize_memories(
594
+ user_id=request.user_id,
595
+ strategy=MemoryOptimizationStrategyType.SUMMARIZE,
596
+ apply=request.apply,
597
+ )
598
+ else:
599
+ optimized_memories = memory_manager.optimize_memories(
600
+ user_id=request.user_id,
601
+ strategy=MemoryOptimizationStrategyType.SUMMARIZE,
602
+ apply=request.apply,
603
+ )
604
+
605
+ # Count tokens after optimization
606
+ tokens_after = strategy.count_tokens(optimized_memories)
607
+ memories_after_count = len(optimized_memories)
608
+
609
+ # Calculate statistics
610
+ tokens_saved = tokens_before - tokens_after
611
+ reduction_percentage = (tokens_saved / tokens_before * 100.0) if tokens_before > 0 else 0.0
612
+
613
+ # Convert to schema objects
614
+ optimized_memory_schemas = [
615
+ UserMemorySchema(
616
+ memory_id=mem.memory_id or "",
617
+ memory=mem.memory or "",
618
+ topics=mem.topics,
619
+ agent_id=mem.agent_id,
620
+ team_id=mem.team_id,
621
+ user_id=mem.user_id,
622
+ updated_at=mem.updated_at,
623
+ )
624
+ for mem in optimized_memories
625
+ ]
626
+
627
+ return OptimizeMemoriesResponse(
628
+ memories=optimized_memory_schemas,
629
+ memories_before=memories_before_count,
630
+ memories_after=memories_after_count,
631
+ tokens_before=tokens_before,
632
+ tokens_after=tokens_after,
633
+ tokens_saved=tokens_saved,
634
+ reduction_percentage=reduction_percentage,
635
+ )
636
+
637
+ except HTTPException:
638
+ raise
639
+ except Exception as e:
640
+ logger.error(f"Failed to optimize memories for user {request.user_id}: {str(e)}")
641
+ raise HTTPException(status_code=500, detail=f"Failed to optimize memories: {str(e)}")
642
+
643
+ return router
644
+
645
+
646
+ def parse_topics(
647
+ topics: Optional[List[str]] = Query(
648
+ default=None,
649
+ description="Comma-separated list of topics to filter by",
650
+ examples=["preferences,technical,communication_style"],
651
+ ),
652
+ ) -> Optional[List[str]]:
653
+ """Parse comma-separated topics into a list for filtering memories by topic."""
654
+ if not topics:
655
+ return None
656
+
657
+ try:
658
+ return [topic.strip() for topic in topics[0].split(",") if topic.strip()]
659
+
660
+ except Exception as e:
661
+ raise HTTPException(status_code=422, detail=f"Invalid topics format: {e}")