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
agno/memory/manager.py CHANGED
@@ -1,191 +1,1542 @@
1
- from typing import Any, List, Optional, cast
1
+ from copy import deepcopy
2
+ from dataclasses import dataclass
3
+ from os import getenv
4
+ from textwrap import dedent
5
+ from typing import Any, Callable, Dict, List, Literal, Optional, Type, Union
2
6
 
3
- from pydantic import BaseModel, ConfigDict
7
+ from pydantic import BaseModel, Field
4
8
 
5
- from agno.memory.db import MemoryDb
6
- from agno.memory.memory import Memory
7
- from agno.memory.row import MemoryRow
9
+ from agno.db.base import AsyncBaseDb, BaseDb
10
+ from agno.db.schemas import UserMemory
11
+ from agno.memory.strategies import MemoryOptimizationStrategy
12
+ from agno.memory.strategies.types import (
13
+ MemoryOptimizationStrategyFactory,
14
+ MemoryOptimizationStrategyType,
15
+ )
8
16
  from agno.models.base import Model
9
17
  from agno.models.message import Message
10
- from agno.utils.log import logger
18
+ from agno.models.utils import get_model
19
+ from agno.tools.function import Function
20
+ from agno.utils.dttm import now_epoch_s
21
+ from agno.utils.log import (
22
+ log_debug,
23
+ log_error,
24
+ log_warning,
25
+ set_log_level_to_debug,
26
+ set_log_level_to_info,
27
+ )
28
+ from agno.utils.prompts import get_json_output_prompt
29
+ from agno.utils.string import parse_response_model_str
11
30
 
12
31
 
13
- class MemoryManager(BaseModel):
32
+ class MemorySearchResponse(BaseModel):
33
+ """Model for Memory Search Response."""
34
+
35
+ memory_ids: List[str] = Field(
36
+ ...,
37
+ description="The IDs of the memories that are most semantically similar to the query.",
38
+ )
39
+
40
+
41
+ @dataclass
42
+ class MemoryManager:
43
+ """Memory Manager"""
44
+
45
+ # Model used for memory management
14
46
  model: Optional[Model] = None
15
- user_id: Optional[str] = None
16
47
 
17
- # Provide the system prompt for the manager as a string
18
- system_prompt: Optional[str] = None
19
- # Memory Database
20
- db: Optional[MemoryDb] = None
48
+ # Provide the system message for the manager as a string. If not provided, the default system message will be used.
49
+ system_message: Optional[str] = None
50
+ # Provide the memory capture instructions for the manager as a string. If not provided, the default memory capture instructions will be used.
51
+ memory_capture_instructions: Optional[str] = None
52
+ # Additional instructions for the manager. These instructions are appended to the default system message.
53
+ additional_instructions: Optional[str] = None
54
+
55
+ # Whether memories were created in the last run
56
+ memories_updated: bool = False
21
57
 
22
- # Do not set the input message here, it will be set by the run method
23
- input_message: Optional[str] = None
58
+ # ----- db tools ---------
59
+ # Whether to delete memories
60
+ delete_memories: bool = True
61
+ # Whether to clear memories
62
+ clear_memories: bool = True
63
+ # Whether to update memories
64
+ update_memories: bool = True
65
+ # whether to add memories
66
+ add_memories: bool = True
24
67
 
25
- model_config = ConfigDict(arbitrary_types_allowed=True)
68
+ # The database to store memories
69
+ db: Optional[Union[BaseDb, AsyncBaseDb]] = None
26
70
 
27
- def update_model(self) -> None:
71
+ debug_mode: bool = False
72
+
73
+ def __init__(
74
+ self,
75
+ model: Optional[Union[Model, str]] = None,
76
+ system_message: Optional[str] = None,
77
+ memory_capture_instructions: Optional[str] = None,
78
+ additional_instructions: Optional[str] = None,
79
+ db: Optional[Union[BaseDb, AsyncBaseDb]] = None,
80
+ delete_memories: bool = False,
81
+ update_memories: bool = True,
82
+ add_memories: bool = True,
83
+ clear_memories: bool = False,
84
+ debug_mode: bool = False,
85
+ ):
86
+ self.model = model # type: ignore[assignment]
87
+ self.system_message = system_message
88
+ self.memory_capture_instructions = memory_capture_instructions
89
+ self.additional_instructions = additional_instructions
90
+ self.db = db
91
+ self.delete_memories = delete_memories
92
+ self.update_memories = update_memories
93
+ self.add_memories = add_memories
94
+ self.clear_memories = clear_memories
95
+ self.debug_mode = debug_mode
96
+
97
+ if self.model is not None:
98
+ self.model = get_model(self.model)
99
+
100
+ def get_model(self) -> Model:
28
101
  if self.model is None:
29
102
  try:
30
103
  from agno.models.openai import OpenAIChat
31
104
  except ModuleNotFoundError as e:
32
- logger.exception(e)
33
- logger.error(
105
+ log_error(e)
106
+ log_error(
34
107
  "Agno uses `openai` as the default model provider. Please provide a `model` or install `openai`."
35
108
  )
36
109
  exit(1)
37
110
  self.model = OpenAIChat(id="gpt-4o")
111
+ return self.model
38
112
 
39
- self.model.add_tool(self.add_memory)
40
- self.model.add_tool(self.update_memory)
41
- self.model.add_tool(self.delete_memory)
42
- self.model.add_tool(self.clear_memory)
113
+ def read_from_db(self, user_id: Optional[str] = None):
114
+ if self.db:
115
+ # If no user_id is provided, read all memories
116
+ if user_id is None:
117
+ all_memories: List[UserMemory] = self.db.get_user_memories() # type: ignore
118
+ else:
119
+ all_memories = self.db.get_user_memories(user_id=user_id) # type: ignore
43
120
 
44
- def get_existing_memories(self) -> Optional[List[MemoryRow]]:
45
- if self.db is None:
121
+ memories: Dict[str, List[UserMemory]] = {}
122
+ for memory in all_memories:
123
+ if memory.user_id is not None and memory.memory_id is not None:
124
+ memories.setdefault(memory.user_id, []).append(memory)
125
+
126
+ return memories
127
+ return None
128
+
129
+ async def aread_from_db(self, user_id: Optional[str] = None):
130
+ if self.db:
131
+ if isinstance(self.db, AsyncBaseDb):
132
+ # If no user_id is provided, read all memories
133
+ if user_id is None:
134
+ all_memories: List[UserMemory] = await self.db.get_user_memories() # type: ignore
135
+ else:
136
+ all_memories = await self.db.get_user_memories(user_id=user_id) # type: ignore
137
+ else:
138
+ if user_id is None:
139
+ all_memories = self.db.get_user_memories() # type: ignore
140
+ else:
141
+ all_memories = self.db.get_user_memories(user_id=user_id) # type: ignore
142
+
143
+ memories: Dict[str, List[UserMemory]] = {}
144
+ for memory in all_memories:
145
+ if memory.user_id is not None and memory.memory_id is not None:
146
+ memories.setdefault(memory.user_id, []).append(memory)
147
+
148
+ return memories
149
+ return None
150
+
151
+ def set_log_level(self):
152
+ if self.debug_mode or getenv("AGNO_DEBUG", "false").lower() == "true":
153
+ self.debug_mode = True
154
+ set_log_level_to_debug()
155
+ else:
156
+ set_log_level_to_info()
157
+
158
+ def initialize(self, user_id: Optional[str] = None):
159
+ self.set_log_level()
160
+
161
+ # -*- Public Functions
162
+ def get_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
163
+ """Get the user memories for a given user id"""
164
+ if self.db:
165
+ if user_id is None:
166
+ user_id = "default"
167
+ # Refresh from the Db
168
+ memories = self.read_from_db(user_id=user_id)
169
+ if memories is None:
170
+ return []
171
+ return memories.get(user_id, [])
172
+ else:
173
+ log_warning("Memory Db not provided.")
174
+ return []
175
+
176
+ async def aget_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
177
+ """Get the user memories for a given user id"""
178
+ if self.db:
179
+ if user_id is None:
180
+ user_id = "default"
181
+ # Refresh from the Db
182
+ memories = await self.aread_from_db(user_id=user_id)
183
+ if memories is None:
184
+ return []
185
+ return memories.get(user_id, [])
186
+ else:
187
+ log_warning("Memory Db not provided.")
188
+ return []
189
+
190
+ def get_user_memory(self, memory_id: str, user_id: Optional[str] = None) -> Optional[UserMemory]:
191
+ """Get the user memory for a given user id"""
192
+ if self.db:
193
+ if user_id is None:
194
+ user_id = "default"
195
+ # Refresh from the DB
196
+ memories = self.read_from_db(user_id=user_id)
197
+ if memories is None:
198
+ return None
199
+ memories_for_user = memories.get(user_id, [])
200
+ for memory in memories_for_user:
201
+ if memory.memory_id == memory_id:
202
+ return memory
203
+ return None
204
+ else:
205
+ log_warning("Memory Db not provided.")
46
206
  return None
47
207
 
48
- return self.db.read_memories(user_id=self.user_id)
208
+ def add_user_memory(
209
+ self,
210
+ memory: UserMemory,
211
+ user_id: Optional[str] = None,
212
+ ) -> Optional[str]:
213
+ """Add a user memory for a given user id
214
+ Args:
215
+ memory (UserMemory): The memory to add
216
+ user_id (Optional[str]): The user id to add the memory to. If not provided, the memory is added to the "default" user.
217
+ Returns:
218
+ str: The id of the memory
219
+ """
220
+ if self.db:
221
+ if memory.memory_id is None:
222
+ from uuid import uuid4
49
223
 
50
- def add_memory(self, memory: str) -> str:
51
- """Use this function to add a memory to the database.
224
+ memory_id = memory.memory_id or str(uuid4())
225
+ memory.memory_id = memory_id
226
+
227
+ if user_id is None:
228
+ user_id = "default"
229
+ memory.user_id = user_id
230
+
231
+ if not memory.updated_at:
232
+ memory.updated_at = now_epoch_s()
233
+
234
+ self._upsert_db_memory(memory=memory)
235
+ return memory.memory_id
236
+
237
+ else:
238
+ log_warning("Memory Db not provided.")
239
+ return None
240
+
241
+ def replace_user_memory(
242
+ self,
243
+ memory_id: str,
244
+ memory: UserMemory,
245
+ user_id: Optional[str] = None,
246
+ ) -> Optional[str]:
247
+ """Replace a user memory for a given user id
52
248
  Args:
53
- memory (str): The memory to be stored.
249
+ memory_id (str): The id of the memory to replace
250
+ memory (UserMemory): The memory to add
251
+ user_id (Optional[str]): The user id to add the memory to. If not provided, the memory is added to the "default" user.
54
252
  Returns:
55
- str: A message indicating if the memory was added successfully or not.
253
+ str: The id of the memory
56
254
  """
255
+ if self.db:
256
+ if user_id is None:
257
+ user_id = "default"
258
+
259
+ if not memory.updated_at:
260
+ memory.updated_at = now_epoch_s()
261
+
262
+ memory.memory_id = memory_id
263
+ memory.user_id = user_id
264
+
265
+ self._upsert_db_memory(memory=memory)
266
+
267
+ return memory.memory_id
268
+ else:
269
+ log_warning("Memory Db not provided.")
270
+ return None
271
+
272
+ def clear(self) -> None:
273
+ """Clears the memory."""
274
+ if self.db:
275
+ self.db.clear_memories()
276
+
277
+ def delete_user_memory(
278
+ self,
279
+ memory_id: str,
280
+ user_id: Optional[str] = None,
281
+ ) -> None:
282
+ """Delete a user memory for a given user id
283
+ Args:
284
+ memory_id (str): The id of the memory to delete
285
+ user_id (Optional[str]): The user id to delete the memory from. If not provided, the memory is deleted from the "default" user.
286
+ """
287
+ if user_id is None:
288
+ user_id = "default"
289
+
290
+ if self.db:
291
+ self._delete_db_memory(memory_id=memory_id, user_id=user_id)
292
+ else:
293
+ log_warning("Memory DB not provided.")
294
+ return None
295
+
296
+ def clear_user_memories(self, user_id: Optional[str] = None) -> None:
297
+ """Clear all memories for a specific user.
298
+
299
+ Args:
300
+ user_id (Optional[str]): The user id to clear memories for. If not provided, clears memories for the "default" user.
301
+ """
302
+ if user_id is None:
303
+ log_warning("Using default user id.")
304
+ user_id = "default"
305
+
306
+ if not self.db:
307
+ log_warning("Memory DB not provided.")
308
+ return
309
+
310
+ if isinstance(self.db, AsyncBaseDb):
311
+ raise ValueError(
312
+ "clear_user_memories() is not supported with an async DB. Please use aclear_user_memories() instead."
313
+ )
314
+
315
+ # TODO: This is inefficient - we fetch all memories just to get their IDs.
316
+ # Extend delete_user_memories() to accept just user_id and delete all memories
317
+ # for that user directly without requiring a list of memory_ids.
318
+ memories = self.get_user_memories(user_id=user_id)
319
+ if not memories:
320
+ log_debug(f"No memories found for user {user_id}")
321
+ return
322
+
323
+ # Extract memory IDs
324
+ memory_ids = [mem.memory_id for mem in memories if mem.memory_id]
325
+
326
+ if memory_ids:
327
+ # Delete all memories in a single batch operation
328
+ self.db.delete_user_memories(memory_ids=memory_ids, user_id=user_id)
329
+ log_debug(f"Cleared {len(memory_ids)} memories for user {user_id}")
330
+
331
+ async def aclear_user_memories(self, user_id: Optional[str] = None) -> None:
332
+ """Clear all memories for a specific user (async).
333
+
334
+ Args:
335
+ user_id (Optional[str]): The user id to clear memories for. If not provided, clears memories for the "default" user.
336
+ """
337
+ if user_id is None:
338
+ user_id = "default"
339
+
340
+ if not self.db:
341
+ log_warning("Memory DB not provided.")
342
+ return
343
+
344
+ if isinstance(self.db, AsyncBaseDb):
345
+ memories = await self.aget_user_memories(user_id=user_id)
346
+ else:
347
+ memories = self.get_user_memories(user_id=user_id)
348
+
349
+ if not memories:
350
+ log_debug(f"No memories found for user {user_id}")
351
+ return
352
+
353
+ # Extract memory IDs
354
+ memory_ids = [mem.memory_id for mem in memories if mem.memory_id]
355
+
356
+ if memory_ids:
357
+ # Delete all memories in a single batch operation
358
+ if isinstance(self.db, AsyncBaseDb):
359
+ await self.db.delete_user_memories(memory_ids=memory_ids, user_id=user_id)
360
+ else:
361
+ self.db.delete_user_memories(memory_ids=memory_ids, user_id=user_id)
362
+ log_debug(f"Cleared {len(memory_ids)} memories for user {user_id}")
363
+
364
+ # -*- Agent Functions
365
+ def create_user_memories(
366
+ self,
367
+ message: Optional[str] = None,
368
+ messages: Optional[List[Message]] = None,
369
+ agent_id: Optional[str] = None,
370
+ team_id: Optional[str] = None,
371
+ user_id: Optional[str] = None,
372
+ ) -> str:
373
+ """Creates memories from multiple messages and adds them to the memory db."""
374
+ self.set_log_level()
375
+
376
+ if self.db is None:
377
+ log_warning("MemoryDb not provided.")
378
+ return "Please provide a db to store memories"
379
+
380
+ if isinstance(self.db, AsyncBaseDb):
381
+ raise ValueError(
382
+ "create_user_memories() is not supported with an async DB. Please use acreate_user_memories() instead."
383
+ )
384
+
385
+ if not messages and not message:
386
+ raise ValueError("You must provide either a message or a list of messages")
387
+
388
+ if message:
389
+ messages = [Message(role="user", content=message)]
390
+
391
+ if not messages or not isinstance(messages, list):
392
+ raise ValueError("Invalid messages list")
393
+
394
+ if user_id is None:
395
+ user_id = "default"
396
+
397
+ memories = self.read_from_db(user_id=user_id)
398
+ if memories is None:
399
+ memories = {}
400
+
401
+ existing_memories = memories.get(user_id, []) # type: ignore
402
+ existing_memories = [{"memory_id": memory.memory_id, "memory": memory.memory} for memory in existing_memories]
403
+ response = self.create_or_update_memories( # type: ignore
404
+ messages=messages,
405
+ existing_memories=existing_memories,
406
+ user_id=user_id,
407
+ agent_id=agent_id,
408
+ team_id=team_id,
409
+ db=self.db,
410
+ update_memories=self.update_memories,
411
+ add_memories=self.add_memories,
412
+ )
413
+
414
+ # We refresh from the DB
415
+ self.read_from_db(user_id=user_id)
416
+ return response
417
+
418
+ async def acreate_user_memories(
419
+ self,
420
+ message: Optional[str] = None,
421
+ messages: Optional[List[Message]] = None,
422
+ agent_id: Optional[str] = None,
423
+ team_id: Optional[str] = None,
424
+ user_id: Optional[str] = None,
425
+ ) -> str:
426
+ """Creates memories from multiple messages and adds them to the memory db."""
427
+ self.set_log_level()
428
+
429
+ if self.db is None:
430
+ log_warning("MemoryDb not provided.")
431
+ return "Please provide a db to store memories"
432
+
433
+ if not messages and not message:
434
+ raise ValueError("You must provide either a message or a list of messages")
435
+
436
+ if message:
437
+ messages = [Message(role="user", content=message)]
438
+
439
+ if not messages or not isinstance(messages, list):
440
+ raise ValueError("Invalid messages list")
441
+
442
+ if user_id is None:
443
+ user_id = "default"
444
+
445
+ if isinstance(self.db, AsyncBaseDb):
446
+ memories = await self.aread_from_db(user_id=user_id)
447
+ else:
448
+ memories = self.read_from_db(user_id=user_id)
449
+ if memories is None:
450
+ memories = {}
451
+
452
+ existing_memories = memories.get(user_id, []) # type: ignore
453
+ existing_memories = [{"memory_id": memory.memory_id, "memory": memory.memory} for memory in existing_memories]
454
+
455
+ response = await self.acreate_or_update_memories( # type: ignore
456
+ messages=messages,
457
+ existing_memories=existing_memories,
458
+ user_id=user_id,
459
+ agent_id=agent_id,
460
+ team_id=team_id,
461
+ db=self.db,
462
+ update_memories=self.update_memories,
463
+ add_memories=self.add_memories,
464
+ )
465
+
466
+ # We refresh from the DB
467
+ if isinstance(self.db, AsyncBaseDb):
468
+ memories = await self.aread_from_db(user_id=user_id)
469
+ else:
470
+ memories = self.read_from_db(user_id=user_id)
471
+
472
+ return response
473
+
474
+ def update_memory_task(self, task: str, user_id: Optional[str] = None) -> str:
475
+ """Updates the memory with a task"""
476
+
477
+ if not self.db:
478
+ log_warning("MemoryDb not provided.")
479
+ return "Please provide a db to store memories"
480
+
481
+ if not isinstance(self.db, BaseDb):
482
+ raise ValueError(
483
+ "update_memory_task() is not supported with an async DB. Please use aupdate_memory_task() instead."
484
+ )
485
+
486
+ if user_id is None:
487
+ user_id = "default"
488
+
489
+ memories = self.read_from_db(user_id=user_id)
490
+ if memories is None:
491
+ memories = {}
492
+
493
+ existing_memories = memories.get(user_id, []) # type: ignore
494
+ existing_memories = [{"memory_id": memory.memory_id, "memory": memory.memory} for memory in existing_memories]
495
+ # The memory manager updates the DB directly
496
+ response = self.run_memory_task( # type: ignore
497
+ task=task,
498
+ existing_memories=existing_memories,
499
+ user_id=user_id,
500
+ db=self.db,
501
+ delete_memories=self.delete_memories,
502
+ update_memories=self.update_memories,
503
+ add_memories=self.add_memories,
504
+ clear_memories=self.clear_memories,
505
+ )
506
+
507
+ # We refresh from the DB
508
+ self.read_from_db(user_id=user_id)
509
+
510
+ return response
511
+
512
+ async def aupdate_memory_task(self, task: str, user_id: Optional[str] = None) -> str:
513
+ """Updates the memory with a task"""
514
+ self.set_log_level()
515
+
516
+ if not self.db:
517
+ log_warning("MemoryDb not provided.")
518
+ return "Please provide a db to store memories"
519
+
520
+ if user_id is None:
521
+ user_id = "default"
522
+
523
+ if isinstance(self.db, AsyncBaseDb):
524
+ memories = await self.aread_from_db(user_id=user_id)
525
+ else:
526
+ memories = self.read_from_db(user_id=user_id)
527
+
528
+ if memories is None:
529
+ memories = {}
530
+
531
+ existing_memories = memories.get(user_id, []) # type: ignore
532
+ existing_memories = [{"memory_id": memory.memory_id, "memory": memory.memory} for memory in existing_memories]
533
+ # The memory manager updates the DB directly
534
+ response = await self.arun_memory_task( # type: ignore
535
+ task=task,
536
+ existing_memories=existing_memories,
537
+ user_id=user_id,
538
+ db=self.db,
539
+ delete_memories=self.delete_memories,
540
+ update_memories=self.update_memories,
541
+ add_memories=self.add_memories,
542
+ clear_memories=self.clear_memories,
543
+ )
544
+
545
+ # We refresh from the DB
546
+ if isinstance(self.db, AsyncBaseDb):
547
+ await self.aread_from_db(user_id=user_id)
548
+ else:
549
+ self.read_from_db(user_id=user_id)
550
+
551
+ return response
552
+
553
+ # -*- Memory Db Functions
554
+ def _upsert_db_memory(self, memory: UserMemory) -> str:
555
+ """Use this function to add a memory to the database."""
57
556
  try:
58
- if self.db:
59
- self.db.upsert_memory(
60
- MemoryRow(user_id=self.user_id, memory=Memory(memory=memory, input=self.input_message).to_dict())
61
- )
557
+ if not self.db:
558
+ raise ValueError("Memory db not initialized")
559
+ self.db.upsert_user_memory(memory=memory)
62
560
  return "Memory added successfully"
63
561
  except Exception as e:
64
- logger.warning(f"Error storing memory in db: {e}")
562
+ log_warning(f"Error storing memory in db: {e}")
65
563
  return f"Error adding memory: {e}"
66
564
 
67
- def delete_memory(self, id: str) -> str:
68
- """Use this function to delete a memory from the database.
69
- Args:
70
- id (str): The id of the memory to be deleted.
71
- Returns:
72
- str: A message indicating if the memory was deleted successfully or not.
73
- """
565
+ def _delete_db_memory(self, memory_id: str, user_id: Optional[str] = None) -> str:
566
+ """Use this function to delete a memory from the database."""
74
567
  try:
75
- if self.db:
76
- self.db.delete_memory(id=id)
568
+ if not self.db:
569
+ raise ValueError("Memory db not initialized")
570
+
571
+ if user_id is None:
572
+ user_id = "default"
573
+
574
+ self.db.delete_user_memory(memory_id=memory_id, user_id=user_id)
77
575
  return "Memory deleted successfully"
78
576
  except Exception as e:
79
- logger.warning(f"Error deleting memory in db: {e}")
577
+ log_warning(f"Error deleting memory in db: {e}")
80
578
  return f"Error deleting memory: {e}"
81
579
 
82
- def update_memory(self, id: str, memory: str) -> str:
83
- """Use this function to update a memory in the database.
580
+ # -*- Utility Functions
581
+ def search_user_memories(
582
+ self,
583
+ query: Optional[str] = None,
584
+ limit: Optional[int] = None,
585
+ retrieval_method: Optional[Literal["last_n", "first_n", "agentic"]] = None,
586
+ user_id: Optional[str] = None,
587
+ ) -> List[UserMemory]:
588
+ """Search through user memories using the specified retrieval method.
589
+
84
590
  Args:
85
- id (str): The id of the memory to be updated.
86
- memory (str): The updated memory.
591
+ query: The search query for agentic search. Required if retrieval_method is "agentic".
592
+ limit: Maximum number of memories to return. Defaults to self.retrieval_limit if not specified. Optional.
593
+ retrieval_method: The method to use for retrieving memories. Defaults to self.retrieval if not specified.
594
+ - "last_n": Return the most recent memories
595
+ - "first_n": Return the oldest memories
596
+ - "agentic": Return memories most similar to the query, but using an agentic approach
597
+ user_id: The user to search for. Optional.
598
+
87
599
  Returns:
88
- str: A message indicating if the memory was updated successfully or not.
600
+ A list of UserMemory objects matching the search criteria.
89
601
  """
90
- try:
91
- if self.db:
92
- self.db.upsert_memory(
93
- MemoryRow(
94
- id=id, user_id=self.user_id, memory=Memory(memory=memory, input=self.input_message).to_dict()
95
- )
96
- )
97
- return "Memory updated successfully"
98
- except Exception as e:
99
- logger.warning(f"Error updating memory in db: {e}")
100
- return f"Error updating memory: {e}"
101
602
 
102
- def clear_memory(self) -> str:
103
- """Use this function to clear all memories from the database.
603
+ if user_id is None:
604
+ user_id = "default"
605
+
606
+ self.set_log_level()
607
+
608
+ memories = self.read_from_db(user_id=user_id)
609
+ if memories is None:
610
+ memories = {}
611
+
612
+ if not memories:
613
+ return []
614
+
615
+ # Use default retrieval method if not specified
616
+ retrieval_method = retrieval_method
617
+ # Use default limit if not specified
618
+ limit = limit
619
+
620
+ # Handle different retrieval methods
621
+ if retrieval_method == "agentic":
622
+ if not query:
623
+ raise ValueError("Query is required for agentic search")
624
+
625
+ return self._search_user_memories_agentic(user_id=user_id, query=query, limit=limit)
626
+
627
+ elif retrieval_method == "first_n":
628
+ return self._get_first_n_memories(user_id=user_id, limit=limit)
629
+
630
+ else: # Default to last_n
631
+ return self._get_last_n_memories(user_id=user_id, limit=limit)
632
+
633
+ def _get_response_format(self) -> Union[Dict[str, Any], Type[BaseModel]]:
634
+ model = self.get_model()
635
+ if model.supports_native_structured_outputs:
636
+ return MemorySearchResponse
637
+
638
+ elif model.supports_json_schema_outputs:
639
+ return {
640
+ "type": "json_schema",
641
+ "json_schema": {
642
+ "name": MemorySearchResponse.__name__,
643
+ "schema": MemorySearchResponse.model_json_schema(),
644
+ },
645
+ }
646
+ else:
647
+ return {"type": "json_object"}
648
+
649
+ def _search_user_memories_agentic(self, user_id: str, query: str, limit: Optional[int] = None) -> List[UserMemory]:
650
+ """Search through user memories using agentic search."""
651
+ memories = self.read_from_db(user_id=user_id)
652
+ if memories is None:
653
+ memories = {}
654
+
655
+ if not memories:
656
+ return []
657
+
658
+ model = self.get_model()
659
+
660
+ response_format = self._get_response_format()
661
+
662
+ log_debug("Searching for memories", center=True)
663
+
664
+ # Get all memories as a list
665
+ user_memories: List[UserMemory] = memories[user_id]
666
+ system_message_str = "Your task is to search through user memories and return the IDs of the memories that are related to the query.\n"
667
+ system_message_str += "\n<user_memories>\n"
668
+ for memory in user_memories:
669
+ system_message_str += f"ID: {memory.memory_id}\n"
670
+ system_message_str += f"Memory: {memory.memory}\n"
671
+ if memory.topics:
672
+ system_message_str += f"Topics: {','.join(memory.topics)}\n"
673
+ system_message_str += "\n"
674
+ system_message_str = system_message_str.strip()
675
+ system_message_str += "\n</user_memories>\n\n"
676
+ system_message_str += "REMEMBER: Only return the IDs of the memories that are related to the query."
677
+
678
+ if response_format == {"type": "json_object"}:
679
+ system_message_str += "\n" + get_json_output_prompt(MemorySearchResponse) # type: ignore
680
+
681
+ messages_for_model = [
682
+ Message(role="system", content=system_message_str),
683
+ Message(
684
+ role="user",
685
+ content=f"Return the IDs of the memories related to the following query: {query}",
686
+ ),
687
+ ]
688
+
689
+ # Generate a response from the Model (includes running function calls)
690
+ response = model.response(messages=messages_for_model, response_format=response_format)
691
+ log_debug("Search for memories complete", center=True)
692
+
693
+ memory_search: Optional[MemorySearchResponse] = None
694
+ # If the model natively supports structured outputs, the parsed value is already in the structured format
695
+ if (
696
+ model.supports_native_structured_outputs
697
+ and response.parsed is not None
698
+ and isinstance(response.parsed, MemorySearchResponse)
699
+ ):
700
+ memory_search = response.parsed
701
+
702
+ # Otherwise convert the response to the structured format
703
+ if isinstance(response.content, str):
704
+ try:
705
+ memory_search = parse_response_model_str(response.content, MemorySearchResponse) # type: ignore
706
+
707
+ # Update RunOutput
708
+ if memory_search is None:
709
+ log_warning("Failed to convert memory_search response to MemorySearchResponse")
710
+ return []
711
+ except Exception as e:
712
+ log_warning(f"Failed to convert memory_search response to MemorySearchResponse: {e}")
713
+ return []
714
+
715
+ memories_to_return = []
716
+ if memory_search:
717
+ for memory_id in memory_search.memory_ids:
718
+ for memory in user_memories:
719
+ if memory.memory_id == memory_id:
720
+ memories_to_return.append(memory)
721
+ return memories_to_return[:limit]
722
+
723
+ def _get_last_n_memories(self, user_id: str, limit: Optional[int] = None) -> List[UserMemory]:
724
+ """Get the most recent user memories.
725
+
726
+ Args:
727
+ limit: Maximum number of memories to return.
104
728
 
105
729
  Returns:
106
- str: A message indicating if the memory was cleared successfully or not.
730
+ A list of the most recent UserMemory objects.
107
731
  """
108
- try:
109
- if self.db:
110
- self.db.clear()
111
- return "Memory cleared successfully"
112
- except Exception as e:
113
- logger.warning(f"Error clearing memory in db: {e}")
114
- return f"Error clearing memory: {e}"
732
+ memories = self.read_from_db(user_id=user_id)
733
+ if memories is None:
734
+ memories = {}
735
+
736
+ memories_list = memories.get(user_id, [])
737
+
738
+ # Sort memories by updated_at timestamp if available
739
+ if memories_list:
740
+ # Sort memories by updated_at timestamp (newest first)
741
+ # If updated_at is None, place at the beginning of the list
742
+ sorted_memories_list = sorted(
743
+ memories_list,
744
+ key=lambda m: m.updated_at if m.updated_at is not None else 0,
745
+ )
746
+ else:
747
+ sorted_memories_list = []
748
+
749
+ if limit is not None and limit > 0:
750
+ sorted_memories_list = sorted_memories_list[-limit:]
751
+
752
+ return sorted_memories_list
753
+
754
+ def _get_first_n_memories(self, user_id: str, limit: Optional[int] = None) -> List[UserMemory]:
755
+ """Get the oldest user memories.
756
+
757
+ Args:
758
+ limit: Maximum number of memories to return.
759
+
760
+ Returns:
761
+ A list of the oldest UserMemory objects.
762
+ """
763
+ memories = self.read_from_db(user_id=user_id)
764
+ if memories is None:
765
+ memories = {}
766
+
767
+ MAX_UNIX_TS = 2**63 - 1
768
+ memories_list = memories.get(user_id, [])
769
+ # Sort memories by updated_at timestamp if available
770
+ if memories_list:
771
+ # Sort memories by updated_at timestamp (oldest first)
772
+ # If updated_at is None, place at the end of the list
773
+ sorted_memories_list = sorted(
774
+ memories_list,
775
+ key=lambda m: m.updated_at if m.updated_at is not None else MAX_UNIX_TS,
776
+ )
777
+
778
+ else:
779
+ sorted_memories_list = []
780
+
781
+ if limit is not None and limit > 0:
782
+ sorted_memories_list = sorted_memories_list[:limit]
783
+
784
+ return sorted_memories_list
785
+
786
+ def optimize_memories(
787
+ self,
788
+ user_id: Optional[str] = None,
789
+ strategy: Union[
790
+ MemoryOptimizationStrategyType, MemoryOptimizationStrategy
791
+ ] = MemoryOptimizationStrategyType.SUMMARIZE,
792
+ apply: bool = True,
793
+ ) -> List[UserMemory]:
794
+ """Optimize user memories using the specified strategy.
795
+
796
+ Args:
797
+ user_id: User ID to optimize memories for. Defaults to "default".
798
+ strategy: Optimization strategy. Can be:
799
+ - Enum: MemoryOptimizationStrategyType.SUMMARIZE
800
+ - Instance: Custom MemoryOptimizationStrategy instance
801
+ apply: If True, automatically replace memories in database.
802
+
803
+ Returns:
804
+ List of optimized UserMemory objects.
805
+ """
806
+ if user_id is None:
807
+ user_id = "default"
808
+
809
+ if isinstance(self.db, AsyncBaseDb):
810
+ raise ValueError(
811
+ "optimize_memories() is not supported with an async DB. Please use aoptimize_memories() instead."
812
+ )
813
+
814
+ # Get user memories
815
+ memories = self.get_user_memories(user_id=user_id)
816
+ if not memories:
817
+ log_debug("No memories to optimize")
818
+ return []
819
+
820
+ # Get strategy instance
821
+ if isinstance(strategy, MemoryOptimizationStrategyType):
822
+ strategy_instance = MemoryOptimizationStrategyFactory.create_strategy(strategy)
823
+ else:
824
+ # Already a strategy instance
825
+ strategy_instance = strategy
826
+
827
+ # Optimize memories using strategy
828
+ optimization_model = self.get_model()
829
+ optimized_memories = strategy_instance.optimize(memories=memories, model=optimization_model)
830
+
831
+ # Apply to database if requested
832
+ if apply:
833
+ log_debug(f"Applying optimized memories to database for user {user_id}")
834
+
835
+ if not self.db:
836
+ log_warning("Memory DB not provided. Cannot apply optimized memories.")
837
+ return optimized_memories
838
+
839
+ # Clear all existing memories for the user
840
+ self.clear_user_memories(user_id=user_id)
841
+
842
+ # Add all optimized memories
843
+ for opt_mem in optimized_memories:
844
+ # Ensure memory has an ID (generate if needed for new memories)
845
+ if not opt_mem.memory_id:
846
+ from uuid import uuid4
847
+
848
+ opt_mem.memory_id = str(uuid4())
849
+
850
+ self.db.upsert_user_memory(memory=opt_mem)
851
+
852
+ optimized_tokens = strategy_instance.count_tokens(optimized_memories)
853
+ log_debug(f"Optimization complete. New token count: {optimized_tokens}")
854
+
855
+ return optimized_memories
856
+
857
+ async def aoptimize_memories(
858
+ self,
859
+ user_id: Optional[str] = None,
860
+ strategy: Union[
861
+ MemoryOptimizationStrategyType, MemoryOptimizationStrategy
862
+ ] = MemoryOptimizationStrategyType.SUMMARIZE,
863
+ apply: bool = True,
864
+ ) -> List[UserMemory]:
865
+ """Async version of optimize_memories.
866
+
867
+ Args:
868
+ user_id: User ID to optimize memories for. Defaults to "default".
869
+ strategy: Optimization strategy. Can be:
870
+ - Enum: MemoryOptimizationStrategyType.SUMMARIZE
871
+ - Instance: Custom MemoryOptimizationStrategy instance
872
+ apply: If True, automatically replace memories in database.
873
+
874
+ Returns:
875
+ List of optimized UserMemory objects.
876
+ """
877
+ if user_id is None:
878
+ user_id = "default"
879
+
880
+ # Get user memories - handle both sync and async DBs
881
+ if isinstance(self.db, AsyncBaseDb):
882
+ memories = await self.aget_user_memories(user_id=user_id)
883
+ else:
884
+ memories = self.get_user_memories(user_id=user_id)
885
+
886
+ if not memories:
887
+ log_debug("No memories to optimize")
888
+ return []
889
+
890
+ # Get strategy instance
891
+ if isinstance(strategy, MemoryOptimizationStrategyType):
892
+ strategy_instance = MemoryOptimizationStrategyFactory.create_strategy(strategy)
893
+ else:
894
+ # Already a strategy instance
895
+ strategy_instance = strategy
896
+
897
+ # Optimize memories using strategy (async)
898
+ optimization_model = self.get_model()
899
+ optimized_memories = await strategy_instance.aoptimize(memories=memories, model=optimization_model)
900
+
901
+ # Apply to database if requested
902
+ if apply:
903
+ log_debug(f"Optimizing memories for user {user_id}")
904
+
905
+ if not self.db:
906
+ log_warning("Memory DB not provided. Cannot apply optimized memories.")
907
+ return optimized_memories
908
+
909
+ # Clear all existing memories for the user
910
+ await self.aclear_user_memories(user_id=user_id)
911
+
912
+ # Add all optimized memories
913
+ for opt_mem in optimized_memories:
914
+ # Ensure memory has an ID (generate if needed for new memories)
915
+ if not opt_mem.memory_id:
916
+ from uuid import uuid4
917
+
918
+ opt_mem.memory_id = str(uuid4())
919
+
920
+ if isinstance(self.db, AsyncBaseDb):
921
+ await self.db.upsert_user_memory(memory=opt_mem)
922
+ elif isinstance(self.db, BaseDb):
923
+ self.db.upsert_user_memory(memory=opt_mem)
924
+
925
+ optimized_tokens = strategy_instance.count_tokens(optimized_memories)
926
+ log_debug(f"Memory optimization complete. New token count: {optimized_tokens}")
927
+
928
+ return optimized_memories
929
+
930
+ # --Memory Manager Functions--
931
+ def determine_tools_for_model(self, tools: List[Callable]) -> List[Union[Function, dict]]:
932
+ # Have to reset each time, because of different user IDs
933
+ _function_names = []
934
+ _functions: List[Union[Function, dict]] = []
935
+
936
+ for tool in tools:
937
+ try:
938
+ function_name = tool.__name__
939
+ if function_name in _function_names:
940
+ continue
941
+ _function_names.append(function_name)
942
+ func = Function.from_callable(tool, strict=True) # type: ignore
943
+ func.strict = True
944
+ _functions.append(func)
945
+ log_debug(f"Added function {func.name}")
946
+ except Exception as e:
947
+ log_warning(f"Could not add function {tool}: {e}")
948
+
949
+ return _functions
950
+
951
+ def get_system_message(
952
+ self,
953
+ existing_memories: Optional[List[Dict[str, Any]]] = None,
954
+ enable_delete_memory: bool = True,
955
+ enable_clear_memory: bool = True,
956
+ enable_update_memory: bool = True,
957
+ enable_add_memory: bool = True,
958
+ ) -> Message:
959
+ if self.system_message is not None:
960
+ return Message(role="system", content=self.system_message)
961
+
962
+ memory_capture_instructions = self.memory_capture_instructions or dedent(
963
+ """\
964
+ Memories should capture personal information about the user that is relevant to the current conversation, such as:
965
+ - Personal facts: name, age, occupation, location, interests, and preferences
966
+ - Opinions and preferences: what the user likes, dislikes, enjoys, or finds frustrating
967
+ - Significant life events or experiences shared by the user
968
+ - Important context about the user's current situation, challenges, or goals
969
+ - Any other details that offer meaningful insight into the user's personality, perspective, or needs
970
+ """
971
+ )
115
972
 
116
- def get_system_message(self) -> Message:
117
973
  # -*- Return a system message for the memory manager
118
974
  system_prompt_lines = [
119
- "Your task is to generate a concise memory for the user's message. "
120
- "Create a memory that captures the key information provided by the user, as if you were storing it for future reference. "
121
- "The memory should be a brief, third-person statement that encapsulates the most important aspect of the user's input, without adding any extraneous details. "
122
- "This memory will be used to enhance the user's experience in subsequent conversations.",
123
- "You will also be provided with a list of existing memories. You may:",
124
- " 1. Add a new memory using the `add_memory` tool.",
125
- " 2. Update a memory using the `update_memory` tool.",
126
- " 3. Delete a memory using the `delete_memory` tool.",
127
- " 4. Clear all memories using the `clear_memory` tool. Use this with extreme caution, as it will remove all memories from the database.",
975
+ "You are a Memory Manager that is responsible for managing information and preferences about the user. "
976
+ "You will be provided with a criteria for memories to capture in the <memories_to_capture> section and a list of existing memories in the <existing_memories> section.",
977
+ "",
978
+ "## When to add or update memories",
979
+ "- Your first task is to decide if a memory needs to be added, updated, or deleted based on the user's message OR if no changes are needed.",
980
+ "- If the user's message meets the criteria in the <memories_to_capture> section and that information is not already captured in the <existing_memories> section, you should capture it as a memory.",
981
+ "- If the users messages does not meet the criteria in the <memories_to_capture> section, no memory updates are needed.",
982
+ "- If the existing memories in the <existing_memories> section capture all relevant information, no memory updates are needed.",
983
+ "",
984
+ "## How to add or update memories",
985
+ "- If you decide to add a new memory, create memories that captures key information, as if you were storing it for future reference.",
986
+ "- Memories should be a brief, third-person statements that encapsulate the most important aspect of the user's input, without adding any extraneous information.",
987
+ " - Example: If the user's message is 'I'm going to the gym', a memory could be `John Doe goes to the gym regularly`.",
988
+ " - Example: If the user's message is 'My name is John Doe', a memory could be `User's name is John Doe`.",
989
+ "- Don't make a single memory too long or complex, create multiple memories if needed to capture all the information.",
990
+ "- Don't repeat the same information in multiple memories. Rather update existing memories if needed.",
991
+ "- If a user asks for a memory to be updated or forgotten, remove all reference to the information that should be forgotten. Don't say 'The user used to like ...`",
992
+ "- When updating a memory, append the existing memory with new information rather than completely overwriting it.",
993
+ "- When a user's preferences change, update the relevant memories to reflect the new preferences but also capture what the user's preferences used to be and what has changed.",
994
+ "",
995
+ "## Criteria for creating memories",
996
+ "Use the following criteria to determine if a user's message should be captured as a memory.",
997
+ "",
998
+ "<memories_to_capture>",
999
+ memory_capture_instructions,
1000
+ "</memories_to_capture>",
1001
+ "",
1002
+ "## Updating memories",
1003
+ "You will also be provided with a list of existing memories in the <existing_memories> section. You can:",
1004
+ " - Decide to make no changes.",
128
1005
  ]
129
- existing_memories = self.get_existing_memories()
1006
+ if enable_add_memory:
1007
+ system_prompt_lines.append(" - Decide to add a new memory, using the `add_memory` tool.")
1008
+ if enable_update_memory:
1009
+ system_prompt_lines.append(" - Decide to update an existing memory, using the `update_memory` tool.")
1010
+ if enable_delete_memory:
1011
+ system_prompt_lines.append(" - Decide to delete an existing memory, using the `delete_memory` tool.")
1012
+ if enable_clear_memory:
1013
+ system_prompt_lines.append(" - Decide to clear all memories, using the `clear_memory` tool.")
1014
+
1015
+ system_prompt_lines += [
1016
+ "You can call multiple tools in a single response if needed. ",
1017
+ "Only add or update memories if it is necessary to capture key information provided by the user.",
1018
+ ]
1019
+
130
1020
  if existing_memories and len(existing_memories) > 0:
131
- system_prompt_lines.extend(
132
- [
133
- "\nExisting memories:",
134
- "<existing_memories>\n"
135
- + "\n".join([f" - id: {m.id} | memory: {m.memory}" for m in existing_memories])
136
- + "\n</existing_memories>",
137
- ]
138
- )
1021
+ system_prompt_lines.append("\n<existing_memories>")
1022
+ for existing_memory in existing_memories:
1023
+ system_prompt_lines.append(f"ID: {existing_memory['memory_id']}")
1024
+ system_prompt_lines.append(f"Memory: {existing_memory['memory']}")
1025
+ system_prompt_lines.append("")
1026
+ system_prompt_lines.append("</existing_memories>")
1027
+
1028
+ if self.additional_instructions:
1029
+ system_prompt_lines.append(self.additional_instructions)
1030
+
139
1031
  return Message(role="system", content="\n".join(system_prompt_lines))
140
1032
 
141
- def run(
1033
+ def create_or_update_memories(
142
1034
  self,
143
- message: Optional[str] = None,
144
- **kwargs: Any,
145
- ) -> Optional[str]:
146
- logger.debug("*********** MemoryManager Start ***********")
1035
+ messages: List[Message],
1036
+ existing_memories: List[Dict[str, Any]],
1037
+ user_id: str,
1038
+ db: BaseDb,
1039
+ agent_id: Optional[str] = None,
1040
+ team_id: Optional[str] = None,
1041
+ update_memories: bool = True,
1042
+ add_memories: bool = True,
1043
+ ) -> str:
1044
+ if self.model is None:
1045
+ log_error("No model provided for memory manager")
1046
+ return "No model provided for memory manager"
1047
+
1048
+ log_debug("MemoryManager Start", center=True)
147
1049
 
1050
+ if len(messages) == 1:
1051
+ input_string = messages[0].get_content_string()
1052
+ else:
1053
+ input_string = f"{', '.join([m.get_content_string() for m in messages if m.role == 'user' and m.content])}"
1054
+
1055
+ model_copy = deepcopy(self.model)
148
1056
  # Update the Model (set defaults, add logit etc.)
149
- self.update_model()
1057
+ _tools = self.determine_tools_for_model(
1058
+ self._get_db_tools(
1059
+ user_id,
1060
+ db,
1061
+ input_string,
1062
+ agent_id=agent_id,
1063
+ team_id=team_id,
1064
+ enable_add_memory=add_memories,
1065
+ enable_update_memory=update_memories,
1066
+ enable_delete_memory=True,
1067
+ enable_clear_memory=False,
1068
+ ),
1069
+ )
150
1070
 
151
1071
  # Prepare the List of messages to send to the Model
152
- messages_for_model: List[Message] = [self.get_system_message()]
153
- # Add the user prompt message
154
- user_prompt_message = Message(role="user", content=message, **kwargs) if message else None
155
- if user_prompt_message is not None:
156
- messages_for_model += [user_prompt_message]
1072
+ messages_for_model: List[Message] = [
1073
+ self.get_system_message(
1074
+ existing_memories=existing_memories,
1075
+ enable_update_memory=update_memories,
1076
+ enable_add_memory=add_memories,
1077
+ enable_delete_memory=True,
1078
+ enable_clear_memory=False,
1079
+ ),
1080
+ *messages,
1081
+ ]
157
1082
 
158
- # Set input message added with the memory
159
- self.input_message = message
1083
+ # Generate a response from the Model (includes running function calls)
1084
+ response = model_copy.response(
1085
+ messages=messages_for_model,
1086
+ tools=_tools,
1087
+ )
1088
+
1089
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
1090
+ self.memories_updated = True
1091
+ log_debug("MemoryManager End", center=True)
1092
+
1093
+ return response.content or "No response from model"
1094
+
1095
+ async def acreate_or_update_memories(
1096
+ self,
1097
+ messages: List[Message],
1098
+ existing_memories: List[Dict[str, Any]],
1099
+ user_id: str,
1100
+ db: Union[BaseDb, AsyncBaseDb],
1101
+ agent_id: Optional[str] = None,
1102
+ team_id: Optional[str] = None,
1103
+ update_memories: bool = True,
1104
+ add_memories: bool = True,
1105
+ ) -> str:
1106
+ if self.model is None:
1107
+ log_error("No model provided for memory manager")
1108
+ return "No model provided for memory manager"
1109
+
1110
+ log_debug("MemoryManager Start", center=True)
1111
+
1112
+ if len(messages) == 1:
1113
+ input_string = messages[0].get_content_string()
1114
+ else:
1115
+ input_string = f"{', '.join([m.get_content_string() for m in messages if m.role == 'user' and m.content])}"
1116
+
1117
+ model_copy = deepcopy(self.model)
1118
+ # Update the Model (set defaults, add logit etc.)
1119
+ if isinstance(db, AsyncBaseDb):
1120
+ _tools = self.determine_tools_for_model(
1121
+ await self._aget_db_tools(
1122
+ user_id,
1123
+ db,
1124
+ input_string,
1125
+ agent_id=agent_id,
1126
+ team_id=team_id,
1127
+ enable_add_memory=add_memories,
1128
+ enable_update_memory=update_memories,
1129
+ enable_delete_memory=True,
1130
+ enable_clear_memory=False,
1131
+ ),
1132
+ )
1133
+ else:
1134
+ _tools = self.determine_tools_for_model(
1135
+ self._get_db_tools(
1136
+ user_id,
1137
+ db,
1138
+ input_string,
1139
+ agent_id=agent_id,
1140
+ team_id=team_id,
1141
+ enable_add_memory=add_memories,
1142
+ enable_update_memory=update_memories,
1143
+ enable_delete_memory=True,
1144
+ enable_clear_memory=False,
1145
+ ),
1146
+ )
1147
+
1148
+ # Prepare the List of messages to send to the Model
1149
+ messages_for_model: List[Message] = [
1150
+ self.get_system_message(
1151
+ existing_memories=existing_memories,
1152
+ enable_update_memory=update_memories,
1153
+ enable_add_memory=add_memories,
1154
+ enable_delete_memory=True,
1155
+ enable_clear_memory=False,
1156
+ ),
1157
+ *messages,
1158
+ ]
160
1159
 
161
1160
  # Generate a response from the Model (includes running function calls)
162
- self.model = cast(Model, self.model)
163
- response = self.model.response(messages=messages_for_model)
164
- logger.debug("*********** MemoryManager End ***********")
165
- return response.content
1161
+ response = await model_copy.aresponse(
1162
+ messages=messages_for_model,
1163
+ tools=_tools,
1164
+ )
166
1165
 
167
- async def arun(
1166
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
1167
+ self.memories_updated = True
1168
+ log_debug("MemoryManager End", center=True)
1169
+
1170
+ return response.content or "No response from model"
1171
+
1172
+ def run_memory_task(
168
1173
  self,
169
- message: Optional[str] = None,
170
- **kwargs: Any,
171
- ) -> Optional[str]:
172
- logger.debug("*********** Async MemoryManager Start ***********")
1174
+ task: str,
1175
+ existing_memories: List[Dict[str, Any]],
1176
+ user_id: str,
1177
+ db: BaseDb,
1178
+ delete_memories: bool = True,
1179
+ update_memories: bool = True,
1180
+ add_memories: bool = True,
1181
+ clear_memories: bool = True,
1182
+ ) -> str:
1183
+ if self.model is None:
1184
+ log_error("No model provided for memory manager")
1185
+ return "No model provided for memory manager"
1186
+
1187
+ log_debug("MemoryManager Start", center=True)
173
1188
 
1189
+ model_copy = deepcopy(self.model)
174
1190
  # Update the Model (set defaults, add logit etc.)
175
- self.update_model()
1191
+ _tools = self.determine_tools_for_model(
1192
+ self._get_db_tools(
1193
+ user_id,
1194
+ db,
1195
+ task,
1196
+ enable_delete_memory=delete_memories,
1197
+ enable_clear_memory=clear_memories,
1198
+ enable_update_memory=update_memories,
1199
+ enable_add_memory=add_memories,
1200
+ ),
1201
+ )
176
1202
 
177
1203
  # Prepare the List of messages to send to the Model
178
- messages_for_model: List[Message] = [self.get_system_message()]
179
- # Add the user prompt message
180
- user_prompt_message = Message(role="user", content=message, **kwargs) if message else None
181
- if user_prompt_message is not None:
182
- messages_for_model += [user_prompt_message]
1204
+ messages_for_model: List[Message] = [
1205
+ self.get_system_message(
1206
+ existing_memories,
1207
+ enable_delete_memory=delete_memories,
1208
+ enable_clear_memory=clear_memories,
1209
+ enable_update_memory=update_memories,
1210
+ enable_add_memory=add_memories,
1211
+ ),
1212
+ # For models that require a non-system message
1213
+ Message(role="user", content=task),
1214
+ ]
1215
+
1216
+ # Generate a response from the Model (includes running function calls)
1217
+ response = model_copy.response(
1218
+ messages=messages_for_model,
1219
+ tools=_tools,
1220
+ )
1221
+
1222
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
1223
+ self.memories_updated = True
1224
+ log_debug("MemoryManager End", center=True)
1225
+
1226
+ return response.content or "No response from model"
1227
+
1228
+ async def arun_memory_task(
1229
+ self,
1230
+ task: str,
1231
+ existing_memories: List[Dict[str, Any]],
1232
+ user_id: str,
1233
+ db: Union[BaseDb, AsyncBaseDb],
1234
+ delete_memories: bool = True,
1235
+ clear_memories: bool = True,
1236
+ update_memories: bool = True,
1237
+ add_memories: bool = True,
1238
+ ) -> str:
1239
+ if self.model is None:
1240
+ log_error("No model provided for memory manager")
1241
+ return "No model provided for memory manager"
183
1242
 
184
- # Set input message added with the memory
185
- self.input_message = message
1243
+ log_debug("MemoryManager Start", center=True)
1244
+
1245
+ model_copy = deepcopy(self.model)
1246
+ # Update the Model (set defaults, add logit etc.)
1247
+ if isinstance(db, AsyncBaseDb):
1248
+ _tools = self.determine_tools_for_model(
1249
+ await self._aget_db_tools(
1250
+ user_id,
1251
+ db,
1252
+ task,
1253
+ enable_delete_memory=delete_memories,
1254
+ enable_clear_memory=clear_memories,
1255
+ enable_update_memory=update_memories,
1256
+ enable_add_memory=add_memories,
1257
+ ),
1258
+ )
1259
+ else:
1260
+ _tools = self.determine_tools_for_model(
1261
+ self._get_db_tools(
1262
+ user_id,
1263
+ db,
1264
+ task,
1265
+ enable_delete_memory=delete_memories,
1266
+ enable_clear_memory=clear_memories,
1267
+ enable_update_memory=update_memories,
1268
+ enable_add_memory=add_memories,
1269
+ ),
1270
+ )
1271
+
1272
+ # Prepare the List of messages to send to the Model
1273
+ messages_for_model: List[Message] = [
1274
+ self.get_system_message(
1275
+ existing_memories,
1276
+ enable_delete_memory=delete_memories,
1277
+ enable_clear_memory=clear_memories,
1278
+ enable_update_memory=update_memories,
1279
+ enable_add_memory=add_memories,
1280
+ ),
1281
+ # For models that require a non-system message
1282
+ Message(role="user", content=task),
1283
+ ]
186
1284
 
187
1285
  # Generate a response from the Model (includes running function calls)
188
- self.model = cast(Model, self.model)
189
- response = await self.model.aresponse(messages=messages_for_model)
190
- logger.debug("*********** Async MemoryManager End ***********")
191
- return response.content
1286
+ response = await model_copy.aresponse(
1287
+ messages=messages_for_model,
1288
+ tools=_tools,
1289
+ )
1290
+
1291
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
1292
+ self.memories_updated = True
1293
+ log_debug("MemoryManager End", center=True)
1294
+
1295
+ return response.content or "No response from model"
1296
+
1297
+ # -*- DB Functions
1298
+ def _get_db_tools(
1299
+ self,
1300
+ user_id: str,
1301
+ db: BaseDb,
1302
+ input_string: str,
1303
+ enable_add_memory: bool = True,
1304
+ enable_update_memory: bool = True,
1305
+ enable_delete_memory: bool = True,
1306
+ enable_clear_memory: bool = True,
1307
+ agent_id: Optional[str] = None,
1308
+ team_id: Optional[str] = None,
1309
+ ) -> List[Callable]:
1310
+ def add_memory(memory: str, topics: Optional[List[str]] = None) -> str:
1311
+ """Use this function to add a memory to the database.
1312
+ Args:
1313
+ memory (str): The memory to be added.
1314
+ topics (Optional[List[str]]): The topics of the memory (e.g. ["name", "hobbies", "location"]).
1315
+ Returns:
1316
+ str: A message indicating if the memory was added successfully or not.
1317
+ """
1318
+ from uuid import uuid4
1319
+
1320
+ from agno.db.base import UserMemory
1321
+
1322
+ try:
1323
+ memory_id = str(uuid4())
1324
+ db.upsert_user_memory(
1325
+ UserMemory(
1326
+ memory_id=memory_id,
1327
+ user_id=user_id,
1328
+ agent_id=agent_id,
1329
+ team_id=team_id,
1330
+ memory=memory,
1331
+ topics=topics,
1332
+ input=input_string,
1333
+ )
1334
+ )
1335
+ log_debug(f"Memory added: {memory_id}")
1336
+ return "Memory added successfully"
1337
+ except Exception as e:
1338
+ log_warning(f"Error storing memory in db: {e}")
1339
+ return f"Error adding memory: {e}"
1340
+
1341
+ def update_memory(memory_id: str, memory: str, topics: Optional[List[str]] = None) -> str:
1342
+ """Use this function to update an existing memory in the database.
1343
+ Args:
1344
+ memory_id (str): The id of the memory to be updated.
1345
+ memory (str): The updated memory.
1346
+ topics (Optional[List[str]]): The topics of the memory (e.g. ["name", "hobbies", "location"]).
1347
+ Returns:
1348
+ str: A message indicating if the memory was updated successfully or not.
1349
+ """
1350
+ from agno.db.base import UserMemory
1351
+
1352
+ if memory == "":
1353
+ return "Can't update memory with empty string. Use the delete memory function if available."
1354
+
1355
+ try:
1356
+ db.upsert_user_memory(
1357
+ UserMemory(
1358
+ memory_id=memory_id,
1359
+ memory=memory,
1360
+ topics=topics,
1361
+ user_id=user_id,
1362
+ input=input_string,
1363
+ )
1364
+ )
1365
+ log_debug("Memory updated")
1366
+ return "Memory updated successfully"
1367
+ except Exception as e:
1368
+ log_warning(f"Error storing memory in db: {e}")
1369
+ return f"Error adding memory: {e}"
1370
+
1371
+ def delete_memory(memory_id: str) -> str:
1372
+ """Use this function to delete a single memory from the database.
1373
+ Args:
1374
+ memory_id (str): The id of the memory to be deleted.
1375
+ Returns:
1376
+ str: A message indicating if the memory was deleted successfully or not.
1377
+ """
1378
+ try:
1379
+ db.delete_user_memory(memory_id=memory_id, user_id=user_id)
1380
+ log_debug("Memory deleted")
1381
+ return "Memory deleted successfully"
1382
+ except Exception as e:
1383
+ log_warning(f"Error deleting memory in db: {e}")
1384
+ return f"Error deleting memory: {e}"
1385
+
1386
+ def clear_memory() -> str:
1387
+ """Use this function to remove all (or clear all) memories from the database.
1388
+
1389
+ Returns:
1390
+ str: A message indicating if the memory was cleared successfully or not.
1391
+ """
1392
+ db.clear_memories()
1393
+ log_debug("Memory cleared")
1394
+ return "Memory cleared successfully"
1395
+
1396
+ functions: List[Callable] = []
1397
+ if enable_add_memory:
1398
+ functions.append(add_memory)
1399
+ if enable_update_memory:
1400
+ functions.append(update_memory)
1401
+ if enable_delete_memory:
1402
+ functions.append(delete_memory)
1403
+ if enable_clear_memory:
1404
+ functions.append(clear_memory)
1405
+ return functions
1406
+
1407
+ async def _aget_db_tools(
1408
+ self,
1409
+ user_id: str,
1410
+ db: Union[BaseDb, AsyncBaseDb],
1411
+ input_string: str,
1412
+ enable_add_memory: bool = True,
1413
+ enable_update_memory: bool = True,
1414
+ enable_delete_memory: bool = True,
1415
+ enable_clear_memory: bool = True,
1416
+ agent_id: Optional[str] = None,
1417
+ team_id: Optional[str] = None,
1418
+ ) -> List[Callable]:
1419
+ async def add_memory(memory: str, topics: Optional[List[str]] = None) -> str:
1420
+ """Use this function to add a memory to the database.
1421
+ Args:
1422
+ memory (str): The memory to be added.
1423
+ topics (Optional[List[str]]): The topics of the memory (e.g. ["name", "hobbies", "location"]).
1424
+ Returns:
1425
+ str: A message indicating if the memory was added successfully or not.
1426
+ """
1427
+ from uuid import uuid4
1428
+
1429
+ from agno.db.base import UserMemory
1430
+
1431
+ try:
1432
+ memory_id = str(uuid4())
1433
+ if isinstance(db, AsyncBaseDb):
1434
+ await db.upsert_user_memory(
1435
+ UserMemory(
1436
+ memory_id=memory_id,
1437
+ user_id=user_id,
1438
+ agent_id=agent_id,
1439
+ team_id=team_id,
1440
+ memory=memory,
1441
+ topics=topics,
1442
+ input=input_string,
1443
+ )
1444
+ )
1445
+ else:
1446
+ db.upsert_user_memory(
1447
+ UserMemory(
1448
+ memory_id=memory_id,
1449
+ user_id=user_id,
1450
+ agent_id=agent_id,
1451
+ team_id=team_id,
1452
+ memory=memory,
1453
+ topics=topics,
1454
+ input=input_string,
1455
+ )
1456
+ )
1457
+ log_debug(f"Memory added: {memory_id}")
1458
+ return "Memory added successfully"
1459
+ except Exception as e:
1460
+ log_warning(f"Error storing memory in db: {e}")
1461
+ return f"Error adding memory: {e}"
1462
+
1463
+ async def update_memory(memory_id: str, memory: str, topics: Optional[List[str]] = None) -> str:
1464
+ """Use this function to update an existing memory in the database.
1465
+ Args:
1466
+ memory_id (str): The id of the memory to be updated.
1467
+ memory (str): The updated memory.
1468
+ topics (Optional[List[str]]): The topics of the memory (e.g. ["name", "hobbies", "location"]).
1469
+ Returns:
1470
+ str: A message indicating if the memory was updated successfully or not.
1471
+ """
1472
+ from agno.db.base import UserMemory
1473
+
1474
+ if memory == "":
1475
+ return "Can't update memory with empty string. Use the delete memory function if available."
1476
+
1477
+ try:
1478
+ if isinstance(db, AsyncBaseDb):
1479
+ await db.upsert_user_memory(
1480
+ UserMemory(
1481
+ memory_id=memory_id,
1482
+ memory=memory,
1483
+ topics=topics,
1484
+ input=input_string,
1485
+ )
1486
+ )
1487
+ else:
1488
+ db.upsert_user_memory(
1489
+ UserMemory(
1490
+ memory_id=memory_id,
1491
+ memory=memory,
1492
+ topics=topics,
1493
+ input=input_string,
1494
+ )
1495
+ )
1496
+ log_debug("Memory updated")
1497
+ return "Memory updated successfully"
1498
+ except Exception as e:
1499
+ log_warning(f"Error storing memory in db: {e}")
1500
+ return f"Error adding memory: {e}"
1501
+
1502
+ async def delete_memory(memory_id: str) -> str:
1503
+ """Use this function to delete a single memory from the database.
1504
+ Args:
1505
+ memory_id (str): The id of the memory to be deleted.
1506
+ Returns:
1507
+ str: A message indicating if the memory was deleted successfully or not.
1508
+ """
1509
+ try:
1510
+ if isinstance(db, AsyncBaseDb):
1511
+ await db.delete_user_memory(memory_id=memory_id)
1512
+ else:
1513
+ db.delete_user_memory(memory_id=memory_id)
1514
+ log_debug("Memory deleted")
1515
+ return "Memory deleted successfully"
1516
+ except Exception as e:
1517
+ log_warning(f"Error deleting memory in db: {e}")
1518
+ return f"Error deleting memory: {e}"
1519
+
1520
+ async def clear_memory() -> str:
1521
+ """Use this function to remove all (or clear all) memories from the database.
1522
+
1523
+ Returns:
1524
+ str: A message indicating if the memory was cleared successfully or not.
1525
+ """
1526
+ if isinstance(db, AsyncBaseDb):
1527
+ await db.clear_memories()
1528
+ else:
1529
+ db.clear_memories()
1530
+ log_debug("Memory cleared")
1531
+ return "Memory cleared successfully"
1532
+
1533
+ functions: List[Callable] = []
1534
+ if enable_add_memory:
1535
+ functions.append(add_memory)
1536
+ if enable_update_memory:
1537
+ functions.append(update_memory)
1538
+ if enable_delete_memory:
1539
+ functions.append(delete_memory)
1540
+ if enable_clear_memory:
1541
+ functions.append(clear_memory)
1542
+ return functions