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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (590) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +3143 -4170
  4. agno/api/agent.py +11 -67
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +8 -19
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -41
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +5 -21
  11. agno/api/schemas/evals.py +7 -16
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +5 -21
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +11 -7
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +11 -66
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/db/__init__.py +24 -0
  25. agno/db/base.py +245 -0
  26. agno/db/dynamo/__init__.py +3 -0
  27. agno/db/dynamo/dynamo.py +1743 -0
  28. agno/db/dynamo/schemas.py +278 -0
  29. agno/db/dynamo/utils.py +684 -0
  30. agno/db/firestore/__init__.py +3 -0
  31. agno/db/firestore/firestore.py +1432 -0
  32. agno/db/firestore/schemas.py +130 -0
  33. agno/db/firestore/utils.py +278 -0
  34. agno/db/gcs_json/__init__.py +3 -0
  35. agno/db/gcs_json/gcs_json_db.py +1001 -0
  36. agno/db/gcs_json/utils.py +194 -0
  37. agno/db/in_memory/__init__.py +3 -0
  38. agno/db/in_memory/in_memory_db.py +882 -0
  39. agno/db/in_memory/utils.py +172 -0
  40. agno/db/json/__init__.py +3 -0
  41. agno/db/json/json_db.py +1045 -0
  42. agno/db/json/utils.py +196 -0
  43. agno/db/migrations/v1_to_v2.py +162 -0
  44. agno/db/mongo/__init__.py +3 -0
  45. agno/db/mongo/mongo.py +1416 -0
  46. agno/db/mongo/schemas.py +77 -0
  47. agno/db/mongo/utils.py +204 -0
  48. agno/db/mysql/__init__.py +3 -0
  49. agno/db/mysql/mysql.py +1719 -0
  50. agno/db/mysql/schemas.py +124 -0
  51. agno/db/mysql/utils.py +297 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1710 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +280 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1367 -0
  58. agno/db/redis/schemas.py +109 -0
  59. agno/db/redis/utils.py +288 -0
  60. agno/db/schemas/__init__.py +3 -0
  61. agno/db/schemas/evals.py +33 -0
  62. agno/db/schemas/knowledge.py +40 -0
  63. agno/db/schemas/memory.py +46 -0
  64. agno/db/singlestore/__init__.py +3 -0
  65. agno/db/singlestore/schemas.py +116 -0
  66. agno/db/singlestore/singlestore.py +1712 -0
  67. agno/db/singlestore/utils.py +326 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1676 -0
  71. agno/db/sqlite/utils.py +268 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +154 -48
  75. agno/eval/performance.py +88 -23
  76. agno/eval/reliability.py +73 -20
  77. agno/eval/utils.py +23 -13
  78. agno/integrations/discord/__init__.py +3 -0
  79. agno/{app → integrations}/discord/client.py +15 -11
  80. agno/knowledge/__init__.py +2 -2
  81. agno/{document → knowledge}/chunking/agentic.py +2 -2
  82. agno/{document → knowledge}/chunking/document.py +2 -2
  83. agno/{document → knowledge}/chunking/fixed.py +3 -3
  84. agno/{document → knowledge}/chunking/markdown.py +2 -2
  85. agno/{document → knowledge}/chunking/recursive.py +2 -2
  86. agno/{document → knowledge}/chunking/row.py +2 -2
  87. agno/knowledge/chunking/semantic.py +59 -0
  88. agno/knowledge/chunking/strategy.py +121 -0
  89. agno/knowledge/content.py +74 -0
  90. agno/knowledge/document/__init__.py +5 -0
  91. agno/{document → knowledge/document}/base.py +12 -2
  92. agno/knowledge/embedder/__init__.py +5 -0
  93. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  94. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  95. agno/{embedder → knowledge/embedder}/base.py +6 -0
  96. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  97. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  98. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  99. agno/{embedder → knowledge/embedder}/google.py +74 -1
  100. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  101. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  102. agno/knowledge/embedder/langdb.py +22 -0
  103. agno/knowledge/embedder/mistral.py +139 -0
  104. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  105. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  106. agno/knowledge/embedder/openai.py +223 -0
  107. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  108. agno/{embedder → knowledge/embedder}/together.py +1 -1
  109. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  110. agno/knowledge/knowledge.py +1551 -0
  111. agno/knowledge/reader/__init__.py +7 -0
  112. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  113. agno/knowledge/reader/base.py +88 -0
  114. agno/{document → knowledge}/reader/csv_reader.py +47 -65
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/{document → knowledge}/reader/json_reader.py +30 -9
  118. agno/{document → knowledge}/reader/markdown_reader.py +58 -9
  119. agno/{document → knowledge}/reader/pdf_reader.py +71 -126
  120. agno/knowledge/reader/reader_factory.py +268 -0
  121. agno/knowledge/reader/s3_reader.py +101 -0
  122. agno/{document → knowledge}/reader/text_reader.py +31 -10
  123. agno/knowledge/reader/url_reader.py +128 -0
  124. agno/knowledge/reader/web_search_reader.py +366 -0
  125. agno/{document → knowledge}/reader/website_reader.py +37 -10
  126. agno/knowledge/reader/wikipedia_reader.py +59 -0
  127. agno/knowledge/reader/youtube_reader.py +78 -0
  128. agno/knowledge/remote_content/remote_content.py +88 -0
  129. agno/{reranker → knowledge/reranker}/base.py +1 -1
  130. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  131. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  132. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  133. agno/knowledge/types.py +30 -0
  134. agno/knowledge/utils.py +169 -0
  135. agno/media.py +269 -268
  136. agno/memory/__init__.py +2 -10
  137. agno/memory/manager.py +1003 -148
  138. agno/models/aimlapi/__init__.py +2 -2
  139. agno/models/aimlapi/aimlapi.py +6 -6
  140. agno/models/anthropic/claude.py +131 -131
  141. agno/models/aws/bedrock.py +110 -182
  142. agno/models/aws/claude.py +64 -18
  143. agno/models/azure/ai_foundry.py +73 -23
  144. agno/models/base.py +346 -290
  145. agno/models/cerebras/cerebras.py +84 -27
  146. agno/models/cohere/chat.py +106 -98
  147. agno/models/google/gemini.py +105 -46
  148. agno/models/groq/groq.py +97 -35
  149. agno/models/huggingface/huggingface.py +92 -27
  150. agno/models/ibm/watsonx.py +72 -13
  151. agno/models/litellm/chat.py +85 -13
  152. agno/models/message.py +46 -151
  153. agno/models/meta/llama.py +85 -49
  154. agno/models/metrics.py +120 -0
  155. agno/models/mistral/mistral.py +90 -21
  156. agno/models/ollama/__init__.py +0 -2
  157. agno/models/ollama/chat.py +85 -47
  158. agno/models/openai/chat.py +154 -37
  159. agno/models/openai/responses.py +178 -105
  160. agno/models/perplexity/perplexity.py +26 -2
  161. agno/models/portkey/portkey.py +0 -7
  162. agno/models/response.py +15 -9
  163. agno/models/utils.py +20 -0
  164. agno/models/vercel/__init__.py +2 -2
  165. agno/models/vercel/v0.py +1 -1
  166. agno/models/vllm/__init__.py +2 -2
  167. agno/models/vllm/vllm.py +3 -3
  168. agno/models/xai/xai.py +10 -10
  169. agno/os/__init__.py +3 -0
  170. agno/os/app.py +497 -0
  171. agno/os/auth.py +47 -0
  172. agno/os/config.py +103 -0
  173. agno/os/interfaces/agui/__init__.py +3 -0
  174. agno/os/interfaces/agui/agui.py +31 -0
  175. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  176. agno/{app → os/interfaces}/agui/utils.py +77 -33
  177. agno/os/interfaces/base.py +21 -0
  178. agno/os/interfaces/slack/__init__.py +3 -0
  179. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  180. agno/os/interfaces/slack/slack.py +32 -0
  181. agno/os/interfaces/whatsapp/__init__.py +3 -0
  182. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  183. agno/os/interfaces/whatsapp/whatsapp.py +29 -0
  184. agno/os/mcp.py +235 -0
  185. agno/os/router.py +1400 -0
  186. agno/os/routers/__init__.py +3 -0
  187. agno/os/routers/evals/__init__.py +3 -0
  188. agno/os/routers/evals/evals.py +393 -0
  189. agno/os/routers/evals/schemas.py +142 -0
  190. agno/os/routers/evals/utils.py +161 -0
  191. agno/os/routers/knowledge/__init__.py +3 -0
  192. agno/os/routers/knowledge/knowledge.py +850 -0
  193. agno/os/routers/knowledge/schemas.py +118 -0
  194. agno/os/routers/memory/__init__.py +3 -0
  195. agno/os/routers/memory/memory.py +410 -0
  196. agno/os/routers/memory/schemas.py +58 -0
  197. agno/os/routers/metrics/__init__.py +3 -0
  198. agno/os/routers/metrics/metrics.py +178 -0
  199. agno/os/routers/metrics/schemas.py +47 -0
  200. agno/os/routers/session/__init__.py +3 -0
  201. agno/os/routers/session/session.py +536 -0
  202. agno/os/schema.py +945 -0
  203. agno/{app/playground → os}/settings.py +7 -15
  204. agno/os/utils.py +270 -0
  205. agno/reasoning/azure_ai_foundry.py +4 -4
  206. agno/reasoning/deepseek.py +4 -4
  207. agno/reasoning/default.py +6 -11
  208. agno/reasoning/groq.py +4 -4
  209. agno/reasoning/helpers.py +4 -6
  210. agno/reasoning/ollama.py +4 -4
  211. agno/reasoning/openai.py +4 -4
  212. agno/run/agent.py +633 -0
  213. agno/run/base.py +53 -77
  214. agno/run/cancel.py +81 -0
  215. agno/run/team.py +243 -96
  216. agno/run/workflow.py +550 -12
  217. agno/session/__init__.py +10 -0
  218. agno/session/agent.py +244 -0
  219. agno/session/summary.py +225 -0
  220. agno/session/team.py +262 -0
  221. agno/{storage/session/v2 → session}/workflow.py +47 -24
  222. agno/team/__init__.py +15 -16
  223. agno/team/team.py +3260 -4824
  224. agno/tools/agentql.py +14 -5
  225. agno/tools/airflow.py +9 -4
  226. agno/tools/api.py +7 -3
  227. agno/tools/apify.py +2 -46
  228. agno/tools/arxiv.py +8 -3
  229. agno/tools/aws_lambda.py +7 -5
  230. agno/tools/aws_ses.py +7 -1
  231. agno/tools/baidusearch.py +4 -1
  232. agno/tools/bitbucket.py +4 -4
  233. agno/tools/brandfetch.py +14 -11
  234. agno/tools/bravesearch.py +4 -1
  235. agno/tools/brightdata.py +43 -23
  236. agno/tools/browserbase.py +13 -4
  237. agno/tools/calcom.py +12 -10
  238. agno/tools/calculator.py +10 -27
  239. agno/tools/cartesia.py +20 -17
  240. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  241. agno/tools/confluence.py +8 -8
  242. agno/tools/crawl4ai.py +7 -1
  243. agno/tools/csv_toolkit.py +9 -8
  244. agno/tools/dalle.py +22 -12
  245. agno/tools/daytona.py +13 -16
  246. agno/tools/decorator.py +6 -3
  247. agno/tools/desi_vocal.py +17 -8
  248. agno/tools/discord.py +11 -8
  249. agno/tools/docker.py +30 -42
  250. agno/tools/duckdb.py +34 -53
  251. agno/tools/duckduckgo.py +8 -7
  252. agno/tools/e2b.py +62 -62
  253. agno/tools/eleven_labs.py +36 -29
  254. agno/tools/email.py +4 -1
  255. agno/tools/evm.py +7 -1
  256. agno/tools/exa.py +19 -14
  257. agno/tools/fal.py +30 -30
  258. agno/tools/file.py +9 -8
  259. agno/tools/financial_datasets.py +25 -44
  260. agno/tools/firecrawl.py +22 -22
  261. agno/tools/function.py +127 -18
  262. agno/tools/giphy.py +23 -11
  263. agno/tools/github.py +48 -126
  264. agno/tools/gmail.py +45 -61
  265. agno/tools/google_bigquery.py +7 -6
  266. agno/tools/google_maps.py +11 -26
  267. agno/tools/googlesearch.py +7 -2
  268. agno/tools/googlesheets.py +21 -17
  269. agno/tools/hackernews.py +9 -5
  270. agno/tools/jina.py +5 -4
  271. agno/tools/jira.py +18 -9
  272. agno/tools/knowledge.py +31 -32
  273. agno/tools/linear.py +19 -34
  274. agno/tools/linkup.py +5 -1
  275. agno/tools/local_file_system.py +8 -5
  276. agno/tools/lumalab.py +32 -20
  277. agno/tools/mcp.py +1 -2
  278. agno/tools/mem0.py +18 -12
  279. agno/tools/memori.py +14 -10
  280. agno/tools/mlx_transcribe.py +3 -2
  281. agno/tools/models/azure_openai.py +33 -15
  282. agno/tools/models/gemini.py +59 -32
  283. agno/tools/models/groq.py +30 -23
  284. agno/tools/models/nebius.py +28 -12
  285. agno/tools/models_labs.py +40 -16
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +10 -8
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +58 -32
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +63 -47
  293. agno/tools/openweather.py +14 -12
  294. agno/tools/pandas.py +11 -3
  295. agno/tools/postgres.py +4 -12
  296. agno/tools/pubmed.py +4 -1
  297. agno/tools/python.py +9 -22
  298. agno/tools/reasoning.py +35 -27
  299. agno/tools/reddit.py +11 -26
  300. agno/tools/replicate.py +55 -42
  301. agno/tools/resend.py +4 -1
  302. agno/tools/scrapegraph.py +15 -14
  303. agno/tools/searxng.py +10 -23
  304. agno/tools/serpapi.py +6 -3
  305. agno/tools/serper.py +13 -4
  306. agno/tools/shell.py +9 -2
  307. agno/tools/slack.py +12 -11
  308. agno/tools/sleep.py +3 -2
  309. agno/tools/spider.py +24 -4
  310. agno/tools/sql.py +7 -6
  311. agno/tools/tavily.py +6 -4
  312. agno/tools/telegram.py +12 -4
  313. agno/tools/todoist.py +11 -31
  314. agno/tools/toolkit.py +1 -1
  315. agno/tools/trafilatura.py +22 -6
  316. agno/tools/trello.py +9 -22
  317. agno/tools/twilio.py +10 -3
  318. agno/tools/user_control_flow.py +6 -1
  319. agno/tools/valyu.py +34 -5
  320. agno/tools/visualization.py +19 -28
  321. agno/tools/webbrowser.py +4 -3
  322. agno/tools/webex.py +11 -7
  323. agno/tools/website.py +15 -46
  324. agno/tools/webtools.py +12 -4
  325. agno/tools/whatsapp.py +5 -9
  326. agno/tools/wikipedia.py +20 -13
  327. agno/tools/x.py +14 -13
  328. agno/tools/yfinance.py +13 -40
  329. agno/tools/youtube.py +26 -20
  330. agno/tools/zendesk.py +7 -2
  331. agno/tools/zep.py +10 -7
  332. agno/tools/zoom.py +10 -9
  333. agno/utils/common.py +1 -19
  334. agno/utils/events.py +100 -123
  335. agno/utils/gemini.py +32 -2
  336. agno/utils/knowledge.py +29 -0
  337. agno/utils/log.py +54 -4
  338. agno/utils/mcp.py +68 -10
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/aws_claude.py +1 -1
  342. agno/utils/models/claude.py +47 -4
  343. agno/utils/models/cohere.py +1 -1
  344. agno/utils/models/mistral.py +8 -7
  345. agno/utils/models/schema_utils.py +3 -3
  346. agno/utils/models/watsonx.py +1 -1
  347. agno/utils/openai.py +1 -1
  348. agno/utils/pprint.py +33 -32
  349. agno/utils/print_response/agent.py +779 -0
  350. agno/utils/print_response/team.py +1669 -0
  351. agno/utils/print_response/workflow.py +1451 -0
  352. agno/utils/prompts.py +14 -14
  353. agno/utils/reasoning.py +87 -0
  354. agno/utils/response.py +42 -42
  355. agno/utils/streamlit.py +481 -0
  356. agno/utils/string.py +8 -22
  357. agno/utils/team.py +50 -0
  358. agno/utils/timer.py +2 -2
  359. agno/vectordb/base.py +33 -21
  360. agno/vectordb/cassandra/cassandra.py +287 -23
  361. agno/vectordb/chroma/chromadb.py +482 -59
  362. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  363. agno/vectordb/couchbase/couchbase.py +309 -29
  364. agno/vectordb/lancedb/lance_db.py +360 -21
  365. agno/vectordb/langchaindb/__init__.py +5 -0
  366. agno/vectordb/langchaindb/langchaindb.py +145 -0
  367. agno/vectordb/lightrag/__init__.py +5 -0
  368. agno/vectordb/lightrag/lightrag.py +374 -0
  369. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  370. agno/vectordb/milvus/milvus.py +242 -32
  371. agno/vectordb/mongodb/mongodb.py +200 -24
  372. agno/vectordb/pgvector/pgvector.py +319 -37
  373. agno/vectordb/pineconedb/pineconedb.py +221 -27
  374. agno/vectordb/qdrant/qdrant.py +334 -14
  375. agno/vectordb/singlestore/singlestore.py +286 -29
  376. agno/vectordb/surrealdb/surrealdb.py +187 -7
  377. agno/vectordb/upstashdb/upstashdb.py +342 -26
  378. agno/vectordb/weaviate/weaviate.py +227 -165
  379. agno/workflow/__init__.py +17 -13
  380. agno/workflow/{v2/condition.py → condition.py} +135 -32
  381. agno/workflow/{v2/loop.py → loop.py} +115 -28
  382. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  383. agno/workflow/{v2/router.py → router.py} +133 -32
  384. agno/workflow/{v2/step.py → step.py} +207 -49
  385. agno/workflow/{v2/steps.py → steps.py} +147 -66
  386. agno/workflow/types.py +482 -0
  387. agno/workflow/workflow.py +2410 -696
  388. agno-2.0.0.dist-info/METADATA +494 -0
  389. agno-2.0.0.dist-info/RECORD +515 -0
  390. agno-2.0.0.dist-info/licenses/LICENSE +201 -0
  391. agno/agent/metrics.py +0 -107
  392. agno/api/app.py +0 -35
  393. agno/api/playground.py +0 -92
  394. agno/api/schemas/app.py +0 -12
  395. agno/api/schemas/playground.py +0 -22
  396. agno/api/schemas/user.py +0 -35
  397. agno/api/schemas/workspace.py +0 -46
  398. agno/api/user.py +0 -160
  399. agno/api/workflows.py +0 -33
  400. agno/api/workspace.py +0 -175
  401. agno/app/agui/__init__.py +0 -3
  402. agno/app/agui/app.py +0 -17
  403. agno/app/agui/sync_router.py +0 -120
  404. agno/app/base.py +0 -186
  405. agno/app/discord/__init__.py +0 -3
  406. agno/app/fastapi/__init__.py +0 -3
  407. agno/app/fastapi/app.py +0 -107
  408. agno/app/fastapi/async_router.py +0 -457
  409. agno/app/fastapi/sync_router.py +0 -448
  410. agno/app/playground/app.py +0 -228
  411. agno/app/playground/async_router.py +0 -1050
  412. agno/app/playground/deploy.py +0 -249
  413. agno/app/playground/operator.py +0 -183
  414. agno/app/playground/schemas.py +0 -220
  415. agno/app/playground/serve.py +0 -55
  416. agno/app/playground/sync_router.py +0 -1042
  417. agno/app/playground/utils.py +0 -46
  418. agno/app/settings.py +0 -15
  419. agno/app/slack/__init__.py +0 -3
  420. agno/app/slack/app.py +0 -19
  421. agno/app/slack/sync_router.py +0 -92
  422. agno/app/utils.py +0 -54
  423. agno/app/whatsapp/__init__.py +0 -3
  424. agno/app/whatsapp/app.py +0 -15
  425. agno/app/whatsapp/sync_router.py +0 -197
  426. agno/cli/auth_server.py +0 -249
  427. agno/cli/config.py +0 -274
  428. agno/cli/console.py +0 -88
  429. agno/cli/credentials.py +0 -23
  430. agno/cli/entrypoint.py +0 -571
  431. agno/cli/operator.py +0 -357
  432. agno/cli/settings.py +0 -96
  433. agno/cli/ws/ws_cli.py +0 -817
  434. agno/constants.py +0 -13
  435. agno/document/__init__.py +0 -5
  436. agno/document/chunking/semantic.py +0 -45
  437. agno/document/chunking/strategy.py +0 -31
  438. agno/document/reader/__init__.py +0 -5
  439. agno/document/reader/base.py +0 -47
  440. agno/document/reader/docx_reader.py +0 -60
  441. agno/document/reader/gcs/pdf_reader.py +0 -44
  442. agno/document/reader/s3/pdf_reader.py +0 -59
  443. agno/document/reader/s3/text_reader.py +0 -63
  444. agno/document/reader/url_reader.py +0 -59
  445. agno/document/reader/youtube_reader.py +0 -58
  446. agno/embedder/__init__.py +0 -5
  447. agno/embedder/langdb.py +0 -80
  448. agno/embedder/mistral.py +0 -82
  449. agno/embedder/openai.py +0 -78
  450. agno/file/__init__.py +0 -5
  451. agno/file/file.py +0 -16
  452. agno/file/local/csv.py +0 -32
  453. agno/file/local/txt.py +0 -19
  454. agno/infra/app.py +0 -240
  455. agno/infra/base.py +0 -144
  456. agno/infra/context.py +0 -20
  457. agno/infra/db_app.py +0 -52
  458. agno/infra/resource.py +0 -205
  459. agno/infra/resources.py +0 -55
  460. agno/knowledge/agent.py +0 -702
  461. agno/knowledge/arxiv.py +0 -33
  462. agno/knowledge/combined.py +0 -36
  463. agno/knowledge/csv.py +0 -144
  464. agno/knowledge/csv_url.py +0 -124
  465. agno/knowledge/document.py +0 -223
  466. agno/knowledge/docx.py +0 -137
  467. agno/knowledge/firecrawl.py +0 -34
  468. agno/knowledge/gcs/__init__.py +0 -0
  469. agno/knowledge/gcs/base.py +0 -39
  470. agno/knowledge/gcs/pdf.py +0 -125
  471. agno/knowledge/json.py +0 -137
  472. agno/knowledge/langchain.py +0 -71
  473. agno/knowledge/light_rag.py +0 -273
  474. agno/knowledge/llamaindex.py +0 -66
  475. agno/knowledge/markdown.py +0 -154
  476. agno/knowledge/pdf.py +0 -164
  477. agno/knowledge/pdf_bytes.py +0 -42
  478. agno/knowledge/pdf_url.py +0 -148
  479. agno/knowledge/s3/__init__.py +0 -0
  480. agno/knowledge/s3/base.py +0 -64
  481. agno/knowledge/s3/pdf.py +0 -33
  482. agno/knowledge/s3/text.py +0 -34
  483. agno/knowledge/text.py +0 -141
  484. agno/knowledge/url.py +0 -46
  485. agno/knowledge/website.py +0 -179
  486. agno/knowledge/wikipedia.py +0 -32
  487. agno/knowledge/youtube.py +0 -35
  488. agno/memory/agent.py +0 -423
  489. agno/memory/classifier.py +0 -104
  490. agno/memory/db/__init__.py +0 -5
  491. agno/memory/db/base.py +0 -42
  492. agno/memory/db/mongodb.py +0 -189
  493. agno/memory/db/postgres.py +0 -203
  494. agno/memory/db/sqlite.py +0 -193
  495. agno/memory/memory.py +0 -22
  496. agno/memory/row.py +0 -36
  497. agno/memory/summarizer.py +0 -201
  498. agno/memory/summary.py +0 -19
  499. agno/memory/team.py +0 -415
  500. agno/memory/v2/__init__.py +0 -2
  501. agno/memory/v2/db/__init__.py +0 -1
  502. agno/memory/v2/db/base.py +0 -42
  503. agno/memory/v2/db/firestore.py +0 -339
  504. agno/memory/v2/db/mongodb.py +0 -196
  505. agno/memory/v2/db/postgres.py +0 -214
  506. agno/memory/v2/db/redis.py +0 -187
  507. agno/memory/v2/db/schema.py +0 -54
  508. agno/memory/v2/db/sqlite.py +0 -209
  509. agno/memory/v2/manager.py +0 -437
  510. agno/memory/v2/memory.py +0 -1097
  511. agno/memory/v2/schema.py +0 -55
  512. agno/memory/v2/summarizer.py +0 -215
  513. agno/memory/workflow.py +0 -38
  514. agno/models/ollama/tools.py +0 -430
  515. agno/models/qwen/__init__.py +0 -5
  516. agno/playground/__init__.py +0 -10
  517. agno/playground/deploy.py +0 -3
  518. agno/playground/playground.py +0 -3
  519. agno/playground/serve.py +0 -3
  520. agno/playground/settings.py +0 -3
  521. agno/reranker/__init__.py +0 -0
  522. agno/run/response.py +0 -467
  523. agno/run/v2/__init__.py +0 -0
  524. agno/run/v2/workflow.py +0 -567
  525. agno/storage/__init__.py +0 -0
  526. agno/storage/agent/__init__.py +0 -0
  527. agno/storage/agent/dynamodb.py +0 -1
  528. agno/storage/agent/json.py +0 -1
  529. agno/storage/agent/mongodb.py +0 -1
  530. agno/storage/agent/postgres.py +0 -1
  531. agno/storage/agent/singlestore.py +0 -1
  532. agno/storage/agent/sqlite.py +0 -1
  533. agno/storage/agent/yaml.py +0 -1
  534. agno/storage/base.py +0 -60
  535. agno/storage/dynamodb.py +0 -673
  536. agno/storage/firestore.py +0 -297
  537. agno/storage/gcs_json.py +0 -261
  538. agno/storage/in_memory.py +0 -234
  539. agno/storage/json.py +0 -237
  540. agno/storage/mongodb.py +0 -328
  541. agno/storage/mysql.py +0 -685
  542. agno/storage/postgres.py +0 -682
  543. agno/storage/redis.py +0 -336
  544. agno/storage/session/__init__.py +0 -16
  545. agno/storage/session/agent.py +0 -64
  546. agno/storage/session/team.py +0 -63
  547. agno/storage/session/v2/__init__.py +0 -5
  548. agno/storage/session/workflow.py +0 -61
  549. agno/storage/singlestore.py +0 -606
  550. agno/storage/sqlite.py +0 -646
  551. agno/storage/workflow/__init__.py +0 -0
  552. agno/storage/workflow/mongodb.py +0 -1
  553. agno/storage/workflow/postgres.py +0 -1
  554. agno/storage/workflow/sqlite.py +0 -1
  555. agno/storage/yaml.py +0 -241
  556. agno/tools/thinking.py +0 -73
  557. agno/utils/defaults.py +0 -57
  558. agno/utils/filesystem.py +0 -39
  559. agno/utils/git.py +0 -52
  560. agno/utils/json_io.py +0 -30
  561. agno/utils/load_env.py +0 -19
  562. agno/utils/py_io.py +0 -19
  563. agno/utils/pyproject.py +0 -18
  564. agno/utils/resource_filter.py +0 -31
  565. agno/workflow/v2/__init__.py +0 -21
  566. agno/workflow/v2/types.py +0 -357
  567. agno/workflow/v2/workflow.py +0 -3312
  568. agno/workspace/__init__.py +0 -0
  569. agno/workspace/config.py +0 -325
  570. agno/workspace/enums.py +0 -6
  571. agno/workspace/helpers.py +0 -52
  572. agno/workspace/operator.py +0 -757
  573. agno/workspace/settings.py +0 -158
  574. agno-1.8.1.dist-info/METADATA +0 -982
  575. agno-1.8.1.dist-info/RECORD +0 -566
  576. agno-1.8.1.dist-info/entry_points.txt +0 -3
  577. agno-1.8.1.dist-info/licenses/LICENSE +0 -375
  578. /agno/{app → db/migrations}/__init__.py +0 -0
  579. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  580. /agno/{cli → integrations}/__init__.py +0 -0
  581. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  582. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  583. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  584. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  585. /agno/{app → os/interfaces}/slack/security.py +0 -0
  586. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  587. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  588. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  589. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
  590. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
agno/memory/manager.py CHANGED
@@ -1,218 +1,1073 @@
1
- from typing import Any, Dict, List, Optional, cast
1
+ from copy import deepcopy
2
+ from dataclasses import dataclass
3
+ from datetime import datetime
4
+ from os import getenv
5
+ from textwrap import dedent
6
+ from typing import Any, Callable, Dict, List, Literal, Optional, Type, Union
2
7
 
3
- from pydantic import BaseModel, ConfigDict
8
+ from pydantic import BaseModel, Field
4
9
 
5
- from agno.memory.db import MemoryDb
6
- from agno.memory.memory import Memory
7
- from agno.memory.row import MemoryRow
10
+ from agno.db.base import BaseDb
11
+ from agno.db.schemas import UserMemory
8
12
  from agno.models.base import Model
9
13
  from agno.models.message import Message
10
14
  from agno.tools.function import Function
11
- from agno.utils.log import log_debug, logger
15
+ from agno.utils.log import log_debug, log_error, log_warning, set_log_level_to_debug, set_log_level_to_info
16
+ from agno.utils.prompts import get_json_output_prompt
17
+ from agno.utils.string import parse_response_model_str
12
18
 
13
19
 
14
- class MemoryManager(BaseModel):
20
+ class MemorySearchResponse(BaseModel):
21
+ """Model for Memory Search Response."""
22
+
23
+ memory_ids: List[str] = Field(
24
+ ..., description="The IDs of the memories that are most semantically similar to the query."
25
+ )
26
+
27
+
28
+ @dataclass
29
+ class MemoryManager:
30
+ """Memory Manager"""
31
+
32
+ # Model used for memory management
15
33
  model: Optional[Model] = None
16
- user_id: Optional[str] = None
17
- limit: Optional[int] = None
18
- # Provide the system prompt for the manager as a string
19
- system_prompt: Optional[str] = None
20
- # Memory Database
21
- db: Optional[MemoryDb] = None
22
-
23
- # Do not set the input message here, it will be set by the run method
24
- input_message: Optional[str] = None
25
- _tools_for_model: Optional[List[Dict]] = None
26
- _functions_for_model: Optional[Dict[str, Function]] = None
27
-
28
- model_config = ConfigDict(arbitrary_types_allowed=True)
29
-
30
- def update_model(self) -> None:
31
- # Use the default Model (OpenAIChat) if no model is provided
34
+
35
+ # Provide the system message for the manager as a string. If not provided, a default prompt will be used.
36
+ system_message: Optional[str] = None
37
+ # Provide the memory capture instructions for the manager as a string. If not provided, a default prompt will be used.
38
+ memory_capture_instructions: Optional[str] = None
39
+ # Additional instructions for the manager
40
+ additional_instructions: Optional[str] = None
41
+
42
+ # Whether memories were created in the last run
43
+ memories_updated: bool = False
44
+
45
+ # ----- db tools ---------
46
+ # Whether to delete memories
47
+ delete_memories: bool = True
48
+ # Whether to clear memories
49
+ clear_memories: bool = True
50
+ # Whether to update memories
51
+ update_memories: bool = True
52
+ # whether to add memories
53
+ add_memories: bool = True
54
+
55
+ # The database to store memories
56
+ db: Optional[BaseDb] = None
57
+
58
+ debug_mode: bool = False
59
+
60
+ def __init__(
61
+ self,
62
+ model: Optional[Model] = None,
63
+ system_message: Optional[str] = None,
64
+ memory_capture_instructions: Optional[str] = None,
65
+ additional_instructions: Optional[str] = None,
66
+ db: Optional[BaseDb] = None,
67
+ delete_memories: bool = True,
68
+ update_memories: bool = True,
69
+ add_memories: bool = True,
70
+ clear_memories: bool = True,
71
+ debug_mode: bool = False,
72
+ ):
73
+ self.model = model
74
+ if self.model is not None and isinstance(self.model, str):
75
+ raise ValueError("Model must be a Model object, not a string")
76
+ self.system_message = system_message
77
+ self.memory_capture_instructions = memory_capture_instructions
78
+ self.additional_instructions = additional_instructions
79
+ self.db = db
80
+ self.delete_memories = delete_memories
81
+ self.update_memories = update_memories
82
+ self.add_memories = add_memories
83
+ self.clear_memories = clear_memories
84
+ self.debug_mode = debug_mode
85
+ self._tools_for_model: Optional[List[Dict[str, Any]]] = None
86
+ self._functions_for_model: Optional[Dict[str, Function]] = None
87
+
88
+ def get_model(self) -> Model:
32
89
  if self.model is None:
33
90
  try:
34
91
  from agno.models.openai import OpenAIChat
35
92
  except ModuleNotFoundError as e:
36
- logger.exception(e)
37
- logger.error(
93
+ log_error(e)
94
+ log_error(
38
95
  "Agno uses `openai` as the default model provider. Please provide a `model` or install `openai`."
39
96
  )
40
97
  exit(1)
41
98
  self.model = OpenAIChat(id="gpt-4o")
99
+ return self.model
42
100
 
43
- def determine_tools_for_model(self) -> None:
44
- if self._tools_for_model is None:
45
- self._tools_for_model = []
46
- if self._functions_for_model is None:
47
- self._functions_for_model = {}
48
-
49
- for tool in [
50
- self.add_memory,
51
- self.update_memory,
52
- self.delete_memory,
53
- self.clear_memory,
54
- ]:
55
- try:
56
- function_name = tool.__name__
57
- if function_name not in self._functions_for_model:
58
- func = Function.from_callable(tool) # type: ignore
59
- self._functions_for_model[func.name] = func
60
- self._tools_for_model.append({"type": "function", "function": func.to_dict()})
61
- log_debug(f"Added function {func.name}")
62
- except Exception as e:
63
- logger.warning(f"Could not add function {tool}: {e}")
101
+ def read_from_db(self, user_id: Optional[str] = None):
102
+ if self.db:
103
+ # If no user_id is provided, read all memories
104
+ if user_id is None:
105
+ all_memories: List[UserMemory] = self.db.get_user_memories() # type: ignore
106
+ else:
107
+ all_memories = self.db.get_user_memories(user_id=user_id) # type: ignore
64
108
 
65
- def get_existing_memories(self) -> Optional[List[MemoryRow]]:
66
- if self.db is None:
109
+ memories: Dict[str, List[UserMemory]] = {}
110
+ for memory in all_memories:
111
+ if memory.user_id is not None and memory.memory_id is not None:
112
+ memories.setdefault(memory.user_id, []).append(memory)
113
+
114
+ return memories
115
+ return None
116
+
117
+ def set_log_level(self):
118
+ if self.debug_mode or getenv("AGNO_DEBUG", "false").lower() == "true":
119
+ self.debug_mode = True
120
+ set_log_level_to_debug()
121
+ else:
122
+ set_log_level_to_info()
123
+
124
+ def initialize(self, user_id: Optional[str] = None):
125
+ self.set_log_level()
126
+
127
+ # -*- Public Functions
128
+ def get_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
129
+ """Get the user memories for a given user id"""
130
+ if self.db:
131
+ if user_id is None:
132
+ user_id = "default"
133
+ # Refresh from the Db
134
+ memories = self.read_from_db(user_id=user_id)
135
+ if memories is None:
136
+ return []
137
+ return memories.get(user_id, [])
138
+ else:
139
+ log_warning("Memory Db not provided.")
140
+ return []
141
+
142
+ def get_user_memory(self, memory_id: str, user_id: Optional[str] = None) -> Optional[UserMemory]:
143
+ """Get the user memory for a given user id"""
144
+ if self.db:
145
+ if user_id is None:
146
+ user_id = "default"
147
+ # Refresh from the DB
148
+ memories = self.read_from_db(user_id=user_id)
149
+ if memories is None:
150
+ return None
151
+ memories_for_user = memories.get(user_id, [])
152
+ for memory in memories_for_user:
153
+ if memory.memory_id == memory_id:
154
+ return memory
155
+ return None
156
+ else:
157
+ log_warning("Memory Db not provided.")
67
158
  return None
68
159
 
69
- return self.db.read_memories(user_id=self.user_id, limit=self.limit)
160
+ def add_user_memory(
161
+ self,
162
+ memory: UserMemory,
163
+ user_id: Optional[str] = None,
164
+ ) -> Optional[str]:
165
+ """Add a user memory for a given user id
166
+ Args:
167
+ memory (UserMemory): The memory to add
168
+ user_id (Optional[str]): The user id to add the memory to. If not provided, the memory is added to the "default" user.
169
+ Returns:
170
+ str: The id of the memory
171
+ """
172
+ if self.db:
173
+ if memory.memory_id is None:
174
+ from uuid import uuid4
70
175
 
71
- def add_memory(self, memory: str) -> str:
72
- """Use this function to add a memory to the database.
176
+ memory_id = memory.memory_id or str(uuid4())
177
+ memory.memory_id = memory_id
178
+
179
+ if user_id is None:
180
+ user_id = "default"
181
+ memory.user_id = user_id
182
+
183
+ if not memory.updated_at:
184
+ memory.updated_at = datetime.now()
185
+
186
+ self._upsert_db_memory(memory=memory)
187
+ return memory.memory_id
188
+
189
+ else:
190
+ log_warning("Memory Db not provided.")
191
+ return None
192
+
193
+ def replace_user_memory(
194
+ self,
195
+ memory_id: str,
196
+ memory: UserMemory,
197
+ user_id: Optional[str] = None,
198
+ ) -> Optional[str]:
199
+ """Replace a user memory for a given user id
73
200
  Args:
74
- memory (str): The memory to be stored.
201
+ memory_id (str): The id of the memory to replace
202
+ memory (UserMemory): The memory to add
203
+ user_id (Optional[str]): The user id to add the memory to. If not provided, the memory is added to the "default" user.
75
204
  Returns:
76
- str: A message indicating if the memory was added successfully or not.
205
+ str: The id of the memory
206
+ """
207
+ if self.db:
208
+ if user_id is None:
209
+ user_id = "default"
210
+
211
+ if not memory.updated_at:
212
+ memory.updated_at = datetime.now()
213
+
214
+ memory.memory_id = memory_id
215
+ memory.user_id = user_id
216
+
217
+ self._upsert_db_memory(memory=memory)
218
+
219
+ return memory.memory_id
220
+ else:
221
+ log_warning("Memory Db not provided.")
222
+ return None
223
+
224
+ def clear(self) -> None:
225
+ """Clears the memory."""
226
+ if self.db:
227
+ self.db.clear_memories()
228
+
229
+ def delete_user_memory(
230
+ self,
231
+ memory_id: str,
232
+ user_id: Optional[str] = None,
233
+ ) -> None:
234
+ """Delete a user memory for a given user id
235
+ Args:
236
+ memory_id (str): The id of the memory to delete
237
+ user_id (Optional[str]): The user id to delete the memory from. If not provided, the memory is deleted from the "default" user.
77
238
  """
239
+ if self.db:
240
+ self._delete_db_memory(memory_id=memory_id)
241
+ else:
242
+ log_warning("Memory DB not provided.")
243
+ return None
244
+
245
+ # -*- Agent Functions
246
+ def create_user_memories(
247
+ self,
248
+ message: Optional[str] = None,
249
+ messages: Optional[List[Message]] = None,
250
+ agent_id: Optional[str] = None,
251
+ team_id: Optional[str] = None,
252
+ user_id: Optional[str] = None,
253
+ ) -> str:
254
+ """Creates memories from multiple messages and adds them to the memory db."""
255
+ self.set_log_level()
256
+
257
+ if self.db is None:
258
+ log_warning("MemoryDb not provided.")
259
+ return "Please provide a db to store memories"
260
+
261
+ if not messages and not message:
262
+ raise ValueError("You must provide either a message or a list of messages")
263
+
264
+ if message:
265
+ messages = [Message(role="user", content=message)]
266
+
267
+ if not messages or not isinstance(messages, list):
268
+ raise ValueError("Invalid messages list")
269
+
270
+ if user_id is None:
271
+ user_id = "default"
272
+
273
+ memories = self.read_from_db(user_id=user_id)
274
+ if memories is None:
275
+ memories = {}
276
+
277
+ existing_memories = memories.get(user_id, []) # type: ignore
278
+ existing_memories = [{"memory_id": memory.memory_id, "memory": memory.memory} for memory in existing_memories]
279
+ response = self.create_or_update_memories( # type: ignore
280
+ messages=messages,
281
+ existing_memories=existing_memories,
282
+ user_id=user_id,
283
+ agent_id=agent_id,
284
+ team_id=team_id,
285
+ db=self.db,
286
+ update_memories=self.update_memories,
287
+ add_memories=self.add_memories,
288
+ )
289
+
290
+ # We refresh from the DB
291
+ self.read_from_db(user_id=user_id)
292
+ return response
293
+
294
+ async def acreate_user_memories(
295
+ self,
296
+ message: Optional[str] = None,
297
+ messages: Optional[List[Message]] = None,
298
+ agent_id: Optional[str] = None,
299
+ team_id: Optional[str] = None,
300
+ user_id: Optional[str] = None,
301
+ ) -> str:
302
+ """Creates memories from multiple messages and adds them to the memory db."""
303
+ self.set_log_level()
304
+
305
+ if self.db is None:
306
+ log_warning("MemoryDb not provided.")
307
+ return "Please provide a db to store memories"
308
+
309
+ if not messages and not message:
310
+ raise ValueError("You must provide either a message or a list of messages")
311
+
312
+ if message:
313
+ messages = [Message(role="user", content=message)]
314
+
315
+ if not messages or not isinstance(messages, list):
316
+ raise ValueError("Invalid messages list")
317
+
318
+ if user_id is None:
319
+ user_id = "default"
320
+
321
+ memories = self.read_from_db(user_id=user_id)
322
+ if memories is None:
323
+ memories = {}
324
+
325
+ existing_memories = memories.get(user_id, []) # type: ignore
326
+ existing_memories = [{"memory_id": memory.memory_id, "memory": memory.memory} for memory in existing_memories]
327
+
328
+ response = await self.acreate_or_update_memories( # type: ignore
329
+ messages=messages,
330
+ existing_memories=existing_memories,
331
+ user_id=user_id,
332
+ agent_id=agent_id,
333
+ team_id=team_id,
334
+ db=self.db,
335
+ update_memories=self.update_memories,
336
+ add_memories=self.add_memories,
337
+ )
338
+
339
+ # We refresh from the DB
340
+ self.read_from_db(user_id=user_id)
341
+
342
+ return response
343
+
344
+ def update_memory_task(self, task: str, user_id: Optional[str] = None) -> str:
345
+ """Updates the memory with a task"""
346
+
347
+ if not self.db:
348
+ log_warning("MemoryDb not provided.")
349
+ return "Please provide a db to store memories"
350
+
351
+ if user_id is None:
352
+ user_id = "default"
353
+
354
+ memories = self.read_from_db(user_id=user_id)
355
+ if memories is None:
356
+ memories = {}
357
+
358
+ existing_memories = memories.get(user_id, []) # type: ignore
359
+ existing_memories = [{"memory_id": memory.memory_id, "memory": memory.memory} for memory in existing_memories]
360
+ # The memory manager updates the DB directly
361
+ response = self.run_memory_task( # type: ignore
362
+ task=task,
363
+ existing_memories=existing_memories,
364
+ user_id=user_id,
365
+ db=self.db,
366
+ delete_memories=self.delete_memories,
367
+ update_memories=self.update_memories,
368
+ add_memories=self.add_memories,
369
+ clear_memories=self.clear_memories,
370
+ )
371
+
372
+ # We refresh from the DB
373
+ self.read_from_db(user_id=user_id)
374
+
375
+ return response
376
+
377
+ async def aupdate_memory_task(self, task: str, user_id: Optional[str] = None) -> str:
378
+ """Updates the memory with a task"""
379
+ self.set_log_level()
380
+
381
+ if not self.db:
382
+ log_warning("MemoryDb not provided.")
383
+ return "Please provide a db to store memories"
384
+
385
+ if user_id is None:
386
+ user_id = "default"
387
+
388
+ memories = self.read_from_db(user_id=user_id)
389
+ if memories is None:
390
+ memories = {}
391
+
392
+ existing_memories = memories.get(user_id, []) # type: ignore
393
+ existing_memories = [{"memory_id": memory.memory_id, "memory": memory.memory} for memory in existing_memories]
394
+ # The memory manager updates the DB directly
395
+ response = await self.arun_memory_task( # type: ignore
396
+ task=task,
397
+ existing_memories=existing_memories,
398
+ user_id=user_id,
399
+ db=self.db,
400
+ delete_memories=self.delete_memories,
401
+ update_memories=self.update_memories,
402
+ add_memories=self.add_memories,
403
+ clear_memories=self.clear_memories,
404
+ )
405
+
406
+ # We refresh from the DB
407
+ self.read_from_db(user_id=user_id)
408
+
409
+ return response
410
+
411
+ # -*- Memory Db Functions
412
+ def _upsert_db_memory(self, memory: UserMemory) -> str:
413
+ """Use this function to add a memory to the database."""
78
414
  try:
79
- if self.db:
80
- self.db.upsert_memory(
81
- MemoryRow(user_id=self.user_id, memory=Memory(memory=memory, input=self.input_message).to_dict())
82
- )
415
+ if not self.db:
416
+ raise ValueError("Memory db not initialized")
417
+ self.db.upsert_user_memory(memory=memory)
83
418
  return "Memory added successfully"
84
419
  except Exception as e:
85
- logger.warning(f"Error storing memory in db: {e}")
420
+ log_warning(f"Error storing memory in db: {e}")
86
421
  return f"Error adding memory: {e}"
87
422
 
88
- def delete_memory(self, id: str) -> str:
89
- """Use this function to delete a memory from the database.
90
- Args:
91
- id (str): The id of the memory to be deleted.
92
- Returns:
93
- str: A message indicating if the memory was deleted successfully or not.
94
- """
423
+ def _delete_db_memory(self, memory_id: str) -> str:
424
+ """Use this function to delete a memory from the database."""
95
425
  try:
96
- if self.db:
97
- self.db.delete_memory(id=id)
426
+ if not self.db:
427
+ raise ValueError("Memory db not initialized")
428
+ self.db.delete_user_memory(memory_id=memory_id)
98
429
  return "Memory deleted successfully"
99
430
  except Exception as e:
100
- logger.warning(f"Error deleting memory in db: {e}")
431
+ log_warning(f"Error deleting memory in db: {e}")
101
432
  return f"Error deleting memory: {e}"
102
433
 
103
- def update_memory(self, id: str, memory: str) -> str:
104
- """Use this function to update a memory in the database.
434
+ # -*- Utility Functions
435
+ def search_user_memories(
436
+ self,
437
+ query: Optional[str] = None,
438
+ limit: Optional[int] = None,
439
+ retrieval_method: Optional[Literal["last_n", "first_n", "agentic"]] = None,
440
+ user_id: Optional[str] = None,
441
+ ) -> List[UserMemory]:
442
+ """Search through user memories using the specified retrieval method.
443
+
105
444
  Args:
106
- id (str): The id of the memory to be updated.
107
- memory (str): The updated memory.
445
+ query: The search query for agentic search. Required if retrieval_method is "agentic".
446
+ limit: Maximum number of memories to return. Defaults to self.retrieval_limit if not specified. Optional.
447
+ retrieval_method: The method to use for retrieving memories. Defaults to self.retrieval if not specified.
448
+ - "last_n": Return the most recent memories
449
+ - "first_n": Return the oldest memories
450
+ - "agentic": Return memories most similar to the query, but using an agentic approach
451
+ user_id: The user to search for. Optional.
452
+
108
453
  Returns:
109
- str: A message indicating if the memory was updated successfully or not.
454
+ A list of UserMemory objects matching the search criteria.
110
455
  """
111
- try:
112
- if self.db:
113
- self.db.upsert_memory(
114
- MemoryRow(
115
- id=id, user_id=self.user_id, memory=Memory(memory=memory, input=self.input_message).to_dict()
116
- )
117
- )
118
- return "Memory updated successfully"
119
- except Exception as e:
120
- logger.warning(f"Error updating memory in db: {e}")
121
- return f"Error updating memory: {e}"
122
456
 
123
- def clear_memory(self) -> str:
124
- """Use this function to clear all memories from the database.
457
+ if user_id is None:
458
+ user_id = "default"
459
+
460
+ self.set_log_level()
461
+
462
+ memories = self.read_from_db(user_id=user_id)
463
+ if memories is None:
464
+ memories = {}
465
+
466
+ if not memories:
467
+ return []
468
+
469
+ # Use default retrieval method if not specified
470
+ retrieval_method = retrieval_method
471
+ # Use default limit if not specified
472
+ limit = limit
473
+
474
+ # Handle different retrieval methods
475
+ if retrieval_method == "agentic":
476
+ if not query:
477
+ raise ValueError("Query is required for agentic search")
478
+
479
+ return self._search_user_memories_agentic(user_id=user_id, query=query, limit=limit)
480
+
481
+ elif retrieval_method == "first_n":
482
+ return self._get_first_n_memories(user_id=user_id, limit=limit)
483
+
484
+ else: # Default to last_n
485
+ return self._get_last_n_memories(user_id=user_id, limit=limit)
486
+
487
+ def _get_response_format(self) -> Union[Dict[str, Any], Type[BaseModel]]:
488
+ model = self.get_model()
489
+ if model.supports_native_structured_outputs:
490
+ return MemorySearchResponse
491
+
492
+ elif model.supports_json_schema_outputs:
493
+ return {
494
+ "type": "json_schema",
495
+ "json_schema": {
496
+ "name": MemorySearchResponse.__name__,
497
+ "schema": MemorySearchResponse.model_json_schema(),
498
+ },
499
+ }
500
+ else:
501
+ return {"type": "json_object"}
502
+
503
+ def _search_user_memories_agentic(self, user_id: str, query: str, limit: Optional[int] = None) -> List[UserMemory]:
504
+ """Search through user memories using agentic search."""
505
+ memories = self.read_from_db(user_id=user_id)
506
+ if memories is None:
507
+ memories = {}
508
+
509
+ if not memories:
510
+ return []
511
+
512
+ model = self.get_model()
513
+
514
+ response_format = self._get_response_format()
515
+
516
+ log_debug("Searching for memories", center=True)
517
+
518
+ # Get all memories as a list
519
+ user_memories: List[UserMemory] = memories[user_id]
520
+ 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"
521
+ system_message_str += "\n<user_memories>\n"
522
+ for memory in user_memories:
523
+ system_message_str += f"ID: {memory.memory_id}\n"
524
+ system_message_str += f"Memory: {memory.memory}\n"
525
+ if memory.topics:
526
+ system_message_str += f"Topics: {','.join(memory.topics)}\n"
527
+ system_message_str += "\n"
528
+ system_message_str = system_message_str.strip()
529
+ system_message_str += "\n</user_memories>\n\n"
530
+ system_message_str += "REMEMBER: Only return the IDs of the memories that are related to the query."
531
+
532
+ if response_format == {"type": "json_object"}:
533
+ system_message_str += "\n" + get_json_output_prompt(MemorySearchResponse) # type: ignore
534
+
535
+ messages_for_model = [
536
+ Message(role="system", content=system_message_str),
537
+ Message(
538
+ role="user",
539
+ content=f"Return the IDs of the memories related to the following query: {query}",
540
+ ),
541
+ ]
542
+
543
+ # Generate a response from the Model (includes running function calls)
544
+ response = model.response(messages=messages_for_model, response_format=response_format)
545
+ log_debug("Search for memories complete", center=True)
546
+
547
+ memory_search: Optional[MemorySearchResponse] = None
548
+ # If the model natively supports structured outputs, the parsed value is already in the structured format
549
+ if (
550
+ model.supports_native_structured_outputs
551
+ and response.parsed is not None
552
+ and isinstance(response.parsed, MemorySearchResponse)
553
+ ):
554
+ memory_search = response.parsed
555
+
556
+ # Otherwise convert the response to the structured format
557
+ if isinstance(response.content, str):
558
+ try:
559
+ memory_search = parse_response_model_str(response.content, MemorySearchResponse) # type: ignore
560
+
561
+ # Update RunOutput
562
+ if memory_search is None:
563
+ log_warning("Failed to convert memory_search response to MemorySearchResponse")
564
+ return []
565
+ except Exception as e:
566
+ log_warning(f"Failed to convert memory_search response to MemorySearchResponse: {e}")
567
+ return []
568
+
569
+ memories_to_return = []
570
+ if memory_search:
571
+ for memory_id in memory_search.memory_ids:
572
+ for memory in user_memories:
573
+ if memory.memory_id == memory_id:
574
+ memories_to_return.append(memory)
575
+ return memories_to_return[:limit]
576
+
577
+ def _get_last_n_memories(self, user_id: str, limit: Optional[int] = None) -> List[UserMemory]:
578
+ """Get the most recent user memories.
579
+
580
+ Args:
581
+ limit: Maximum number of memories to return.
125
582
 
126
583
  Returns:
127
- str: A message indicating if the memory was cleared successfully or not.
584
+ A list of the most recent UserMemory objects.
128
585
  """
129
- try:
130
- if self.db:
131
- self.db.clear()
132
- return "Memory cleared successfully"
133
- except Exception as e:
134
- logger.warning(f"Error clearing memory in db: {e}")
135
- return f"Error clearing memory: {e}"
586
+ memories = self.read_from_db(user_id=user_id)
587
+ if memories is None:
588
+ memories = {}
589
+
590
+ memories_list = memories.get(user_id, [])
591
+
592
+ # Sort memories by updated_at timestamp if available
593
+ if memories_list:
594
+ # Sort memories by updated_at timestamp (newest first)
595
+ # If updated_at is None, place at the beginning of the list
596
+ sorted_memories_list = sorted(
597
+ memories_list,
598
+ key=lambda memory: memory.updated_at or datetime.min,
599
+ )
600
+ else:
601
+ sorted_memories_list = []
602
+
603
+ if limit is not None and limit > 0:
604
+ sorted_memories_list = sorted_memories_list[-limit:]
605
+
606
+ return sorted_memories_list
607
+
608
+ def _get_first_n_memories(self, user_id: str, limit: Optional[int] = None) -> List[UserMemory]:
609
+ """Get the oldest user memories.
610
+
611
+ Args:
612
+ limit: Maximum number of memories to return.
613
+
614
+ Returns:
615
+ A list of the oldest UserMemory objects.
616
+ """
617
+ memories = self.read_from_db(user_id=user_id)
618
+ if memories is None:
619
+ memories = {}
620
+
621
+ memories_list = memories.get(user_id, [])
622
+ # Sort memories by updated_at timestamp if available
623
+ if memories_list:
624
+ # Sort memories by updated_at timestamp (oldest first)
625
+ # If updated_at is None, place at the end of the list
626
+ sorted_memories_list = sorted(
627
+ memories_list,
628
+ key=lambda memory: memory.updated_at or datetime.max,
629
+ )
630
+
631
+ else:
632
+ sorted_memories_list = []
633
+
634
+ if limit is not None and limit > 0:
635
+ sorted_memories_list = sorted_memories_list[:limit]
636
+
637
+ return sorted_memories_list
638
+
639
+ # --Memory Manager Functions--
640
+ def determine_tools_for_model(self, tools: List[Callable]) -> None:
641
+ # Have to reset each time, because of different user IDs
642
+ self._tools_for_model = []
643
+ self._functions_for_model = {}
644
+
645
+ for tool in tools:
646
+ try:
647
+ function_name = tool.__name__
648
+ if function_name not in self._functions_for_model:
649
+ func = Function.from_callable(tool, strict=True) # type: ignore
650
+ func.strict = True
651
+ self._functions_for_model[func.name] = func
652
+ self._tools_for_model.append({"type": "function", "function": func.to_dict()})
653
+ log_debug(f"Added function {func.name}")
654
+ except Exception as e:
655
+ log_warning(f"Could not add function {tool}: {e}")
656
+
657
+ def get_system_message(
658
+ self,
659
+ existing_memories: Optional[List[Dict[str, Any]]] = None,
660
+ enable_delete_memory: bool = True,
661
+ enable_clear_memory: bool = True,
662
+ enable_update_memory: bool = True,
663
+ enable_add_memory: bool = True,
664
+ ) -> Message:
665
+ if self.system_message is not None:
666
+ return Message(role="system", content=self.system_message)
667
+
668
+ memory_capture_instructions = self.memory_capture_instructions or dedent("""\
669
+ Memories should include details that could personalize ongoing interactions with the user, such as:
670
+ - Personal facts: name, age, occupation, location, interests, preferences, etc.
671
+ - Significant life events or experiences shared by the user
672
+ - Important context about the user's current situation, challenges or goals
673
+ - What the user likes or dislikes, their opinions, beliefs, values, etc.
674
+ - Any other details that provide valuable insights into the user's personality, perspective or needs\
675
+ """)
136
676
 
137
- def get_system_message(self) -> Message:
138
677
  # -*- Return a system message for the memory manager
139
678
  system_prompt_lines = [
140
- "Your task is to generate a concise memory for the user's message. "
141
- "Create a memory that captures the key information provided by the user, as if you were storing it for future reference. "
142
- "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. "
143
- "This memory will be used to enhance the user's experience in subsequent conversations.",
144
- "You will also be provided with a list of existing memories. You may:",
145
- " 1. Add a new memory using the `add_memory` tool.",
146
- " 2. Update a memory using the `update_memory` tool.",
147
- " 3. Delete a memory using the `delete_memory` tool.",
148
- " 4. Clear all memories using the `clear_memory` tool. Use this with extreme caution, as it will remove all memories from the database.",
679
+ "You are a MemoryConnector that is responsible for manging key information about the user. "
680
+ "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.",
681
+ "",
682
+ "## When to add or update memories",
683
+ "- 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.",
684
+ "- 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.",
685
+ "- If the users messages does not meet the criteria in the <memories_to_capture> section, no memory updates are needed.",
686
+ "- If the existing memories in the <existing_memories> section capture all relevant information, no memory updates are needed.",
687
+ "",
688
+ "## How to add or update memories",
689
+ "- If you decide to add a new memory, create memories that captures key information, as if you were storing it for future reference.",
690
+ "- Memories should be a brief, third-person statements that encapsulate the most important aspect of the user's input, without adding any extraneous information.",
691
+ " - Example: If the user's message is 'I'm going to the gym', a memory could be `John Doe goes to the gym regularly`.",
692
+ " - Example: If the user's message is 'My name is John Doe', a memory could be `User's name is John Doe`.",
693
+ "- Don't make a single memory too long or complex, create multiple memories if needed to capture all the information.",
694
+ "- Don't repeat the same information in multiple memories. Rather update existing memories if needed.",
695
+ "- 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 ...`",
696
+ "- When updating a memory, append the existing memory with new information rather than completely overwriting it.",
697
+ "- 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.",
698
+ "",
699
+ "## Criteria for creating memories",
700
+ "Use the following criteria to determine if a user's message should be captured as a memory.",
701
+ "",
702
+ "<memories_to_capture>",
703
+ memory_capture_instructions,
704
+ "</memories_to_capture>",
705
+ "",
706
+ "## Updating memories",
707
+ "You will also be provided with a list of existing memories in the <existing_memories> section. You can:",
708
+ " 1. Decide to make no changes.",
709
+ ]
710
+ if enable_add_memory:
711
+ system_prompt_lines.append(" 2. Decide to add a new memory, using the `add_memory` tool.")
712
+ if enable_update_memory:
713
+ system_prompt_lines.append(" 3. Decide to update an existing memory, using the `update_memory` tool.")
714
+ if enable_delete_memory:
715
+ system_prompt_lines.append(" 4. Decide to delete an existing memory, using the `delete_memory` tool.")
716
+ if enable_clear_memory:
717
+ system_prompt_lines.append(" 5. Decide to clear all memories, using the `clear_memory` tool.")
718
+
719
+ system_prompt_lines += [
720
+ "You can call multiple tools in a single response if needed. ",
721
+ "Only add or update memories if it is necessary to capture key information provided by the user.",
149
722
  ]
150
- existing_memories = self.get_existing_memories()
723
+
151
724
  if existing_memories and len(existing_memories) > 0:
152
- system_prompt_lines.extend(
153
- [
154
- "\nExisting memories:",
155
- "<existing_memories>\n"
156
- + "\n".join([f" - id: {m.id} | memory: {m.memory}" for m in existing_memories])
157
- + "\n</existing_memories>",
158
- ]
159
- )
725
+ system_prompt_lines.append("\n<existing_memories>")
726
+ for existing_memory in existing_memories:
727
+ system_prompt_lines.append(f"ID: {existing_memory['memory_id']}")
728
+ system_prompt_lines.append(f"Memory: {existing_memory['memory']}")
729
+ system_prompt_lines.append("")
730
+ system_prompt_lines.append("</existing_memories>")
731
+
732
+ if self.additional_instructions:
733
+ system_prompt_lines.append(self.additional_instructions)
734
+
160
735
  return Message(role="system", content="\n".join(system_prompt_lines))
161
736
 
162
- def run(
737
+ def create_or_update_memories(
163
738
  self,
164
- message: Optional[str] = None,
165
- **kwargs: Any,
166
- ) -> Optional[str]:
167
- log_debug("*********** MemoryManager Start ***********")
739
+ messages: List[Message],
740
+ existing_memories: List[Dict[str, Any]],
741
+ user_id: str,
742
+ db: BaseDb,
743
+ agent_id: Optional[str] = None,
744
+ team_id: Optional[str] = None,
745
+ update_memories: bool = True,
746
+ add_memories: bool = True,
747
+ ) -> str:
748
+ if self.model is None:
749
+ log_error("No model provided for memory manager")
750
+ return "No model provided for memory manager"
751
+
752
+ log_debug("MemoryManager Start", center=True)
753
+
754
+ if len(messages) == 1:
755
+ input_string = messages[0].get_content_string()
756
+ else:
757
+ input_string = f"{', '.join([m.get_content_string() for m in messages if m.role == 'user' and m.content])}"
168
758
 
759
+ model_copy = deepcopy(self.model)
169
760
  # Update the Model (set defaults, add logit etc.)
170
- self.update_model()
171
- self.determine_tools_for_model()
761
+ self.determine_tools_for_model(
762
+ self._get_db_tools(
763
+ user_id,
764
+ db,
765
+ input_string,
766
+ agent_id=agent_id,
767
+ team_id=team_id,
768
+ enable_add_memory=add_memories,
769
+ enable_update_memory=update_memories,
770
+ enable_delete_memory=False,
771
+ enable_clear_memory=False,
772
+ ),
773
+ )
172
774
 
173
775
  # Prepare the List of messages to send to the Model
174
- messages_for_model: List[Message] = [self.get_system_message()]
776
+ messages_for_model: List[Message] = [
777
+ self.get_system_message(
778
+ existing_memories=existing_memories,
779
+ enable_update_memory=update_memories,
780
+ enable_add_memory=add_memories,
781
+ enable_delete_memory=False,
782
+ enable_clear_memory=False,
783
+ ),
784
+ *messages,
785
+ ]
786
+
787
+ # Generate a response from the Model (includes running function calls)
788
+ response = model_copy.response(
789
+ messages=messages_for_model, tools=self._tools_for_model, functions=self._functions_for_model
790
+ )
791
+
792
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
793
+ self.memories_updated = True
794
+ log_debug("MemoryManager End", center=True)
795
+
796
+ return response.content or "No response from model"
797
+
798
+ async def acreate_or_update_memories(
799
+ self,
800
+ messages: List[Message],
801
+ existing_memories: List[Dict[str, Any]],
802
+ user_id: str,
803
+ db: BaseDb,
804
+ agent_id: Optional[str] = None,
805
+ team_id: Optional[str] = None,
806
+ update_memories: bool = True,
807
+ add_memories: bool = True,
808
+ ) -> str:
809
+ if self.model is None:
810
+ log_error("No model provided for memory manager")
811
+ return "No model provided for memory manager"
175
812
 
176
- # Add the user prompt message
177
- user_prompt_message = Message(role="user", content=message, **kwargs) if message else None
178
- if user_prompt_message is not None:
179
- messages_for_model += [user_prompt_message]
813
+ log_debug("MemoryManager Start", center=True)
180
814
 
181
- # Set input message added with the memory
182
- self.input_message = message
815
+ if len(messages) == 1:
816
+ input_string = messages[0].get_content_string()
817
+ else:
818
+ input_string = f"{', '.join([m.get_content_string() for m in messages if m.role == 'user' and m.content])}"
819
+
820
+ model_copy = deepcopy(self.model)
821
+ # Update the Model (set defaults, add logit etc.)
822
+ self.determine_tools_for_model(
823
+ self._get_db_tools(
824
+ user_id,
825
+ db,
826
+ input_string,
827
+ agent_id=agent_id,
828
+ team_id=team_id,
829
+ enable_add_memory=add_memories,
830
+ enable_update_memory=update_memories,
831
+ enable_delete_memory=False,
832
+ enable_clear_memory=False,
833
+ ),
834
+ )
835
+
836
+ # Prepare the List of messages to send to the Model
837
+ messages_for_model: List[Message] = [
838
+ self.get_system_message(
839
+ existing_memories=existing_memories,
840
+ enable_update_memory=update_memories,
841
+ enable_add_memory=add_memories,
842
+ enable_delete_memory=False,
843
+ enable_clear_memory=False,
844
+ ),
845
+ *messages,
846
+ ]
183
847
 
184
848
  # Generate a response from the Model (includes running function calls)
185
- self.model = cast(Model, self.model)
186
- response = self.model.response(
849
+ response = await model_copy.aresponse(
187
850
  messages=messages_for_model, tools=self._tools_for_model, functions=self._functions_for_model
188
851
  )
189
- log_debug("*********** MemoryManager End ***********")
190
- return response.content
191
852
 
192
- async def arun(
853
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
854
+ self.memories_updated = True
855
+ log_debug("MemoryManager End", center=True)
856
+
857
+ return response.content or "No response from model"
858
+
859
+ def run_memory_task(
193
860
  self,
194
- message: Optional[str] = None,
195
- **kwargs: Any,
196
- ) -> Optional[str]:
197
- log_debug("*********** Async MemoryManager Start ***********")
861
+ task: str,
862
+ existing_memories: List[Dict[str, Any]],
863
+ user_id: str,
864
+ db: BaseDb,
865
+ delete_memories: bool = True,
866
+ update_memories: bool = True,
867
+ add_memories: bool = True,
868
+ clear_memories: bool = True,
869
+ ) -> str:
870
+ if self.model is None:
871
+ log_error("No model provided for memory manager")
872
+ return "No model provided for memory manager"
198
873
 
874
+ log_debug("MemoryManager Start", center=True)
875
+
876
+ model_copy = deepcopy(self.model)
199
877
  # Update the Model (set defaults, add logit etc.)
200
- self.update_model()
878
+ self.determine_tools_for_model(
879
+ self._get_db_tools(
880
+ user_id,
881
+ db,
882
+ task,
883
+ enable_delete_memory=delete_memories,
884
+ enable_clear_memory=clear_memories,
885
+ enable_update_memory=update_memories,
886
+ enable_add_memory=add_memories,
887
+ ),
888
+ )
201
889
 
202
890
  # Prepare the List of messages to send to the Model
203
- messages_for_model: List[Message] = [self.get_system_message()]
204
- # Add the user prompt message
205
- user_prompt_message = Message(role="user", content=message, **kwargs) if message else None
206
- if user_prompt_message is not None:
207
- messages_for_model += [user_prompt_message]
891
+ messages_for_model: List[Message] = [
892
+ self.get_system_message(
893
+ existing_memories,
894
+ enable_delete_memory=delete_memories,
895
+ enable_clear_memory=clear_memories,
896
+ enable_update_memory=update_memories,
897
+ enable_add_memory=add_memories,
898
+ ),
899
+ # For models that require a non-system message
900
+ Message(role="user", content=task),
901
+ ]
902
+
903
+ # Generate a response from the Model (includes running function calls)
904
+ response = model_copy.response(
905
+ messages=messages_for_model, tools=self._tools_for_model, functions=self._functions_for_model
906
+ )
907
+
908
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
909
+ self.memories_updated = True
910
+ log_debug("MemoryManager End", center=True)
911
+
912
+ return response.content or "No response from model"
913
+
914
+ async def arun_memory_task(
915
+ self,
916
+ task: str,
917
+ existing_memories: List[Dict[str, Any]],
918
+ user_id: str,
919
+ db: BaseDb,
920
+ delete_memories: bool = True,
921
+ clear_memories: bool = True,
922
+ update_memories: bool = True,
923
+ add_memories: bool = True,
924
+ ) -> str:
925
+ if self.model is None:
926
+ log_error("No model provided for memory manager")
927
+ return "No model provided for memory manager"
928
+
929
+ log_debug("MemoryManager Start", center=True)
930
+
931
+ model_copy = deepcopy(self.model)
932
+ # Update the Model (set defaults, add logit etc.)
933
+ self.determine_tools_for_model(
934
+ self._get_db_tools(
935
+ user_id,
936
+ db,
937
+ task,
938
+ enable_delete_memory=delete_memories,
939
+ enable_clear_memory=clear_memories,
940
+ enable_update_memory=update_memories,
941
+ enable_add_memory=add_memories,
942
+ ),
943
+ )
208
944
 
209
- # Set input message added with the memory
210
- self.input_message = message
945
+ # Prepare the List of messages to send to the Model
946
+ messages_for_model: List[Message] = [
947
+ self.get_system_message(
948
+ existing_memories,
949
+ enable_delete_memory=delete_memories,
950
+ enable_clear_memory=clear_memories,
951
+ enable_update_memory=update_memories,
952
+ enable_add_memory=add_memories,
953
+ ),
954
+ # For models that require a non-system message
955
+ Message(role="user", content=task),
956
+ ]
211
957
 
212
958
  # Generate a response from the Model (includes running function calls)
213
- self.model = cast(Model, self.model)
214
- response = await self.model.aresponse(
959
+ response = await model_copy.aresponse(
215
960
  messages=messages_for_model, tools=self._tools_for_model, functions=self._functions_for_model
216
961
  )
217
- log_debug("*********** Async MemoryManager End ***********")
218
- return response.content
962
+
963
+ if response.tool_calls is not None and len(response.tool_calls) > 0:
964
+ self.memories_updated = True
965
+ log_debug("MemoryManager End", center=True)
966
+
967
+ return response.content or "No response from model"
968
+
969
+ # -*- DB Functions
970
+ def _get_db_tools(
971
+ self,
972
+ user_id: str,
973
+ db: BaseDb,
974
+ input_string: str,
975
+ enable_add_memory: bool = True,
976
+ enable_update_memory: bool = True,
977
+ enable_delete_memory: bool = True,
978
+ enable_clear_memory: bool = True,
979
+ agent_id: Optional[str] = None,
980
+ team_id: Optional[str] = None,
981
+ ) -> List[Callable]:
982
+ def add_memory(memory: str, topics: Optional[List[str]] = None) -> str:
983
+ """Use this function to add a memory to the database.
984
+ Args:
985
+ memory (str): The memory to be added.
986
+ topics (Optional[List[str]]): The topics of the memory (e.g. ["name", "hobbies", "location"]).
987
+ Returns:
988
+ str: A message indicating if the memory was added successfully or not.
989
+ """
990
+ from uuid import uuid4
991
+
992
+ from agno.db.base import UserMemory
993
+
994
+ try:
995
+ memory_id = str(uuid4())
996
+ db.upsert_user_memory(
997
+ UserMemory(
998
+ memory_id=memory_id,
999
+ user_id=user_id,
1000
+ agent_id=agent_id,
1001
+ team_id=team_id,
1002
+ memory=memory,
1003
+ topics=topics,
1004
+ input=input_string,
1005
+ )
1006
+ )
1007
+ log_debug(f"Memory added: {memory_id}")
1008
+ return "Memory added successfully"
1009
+ except Exception as e:
1010
+ log_warning(f"Error storing memory in db: {e}")
1011
+ return f"Error adding memory: {e}"
1012
+
1013
+ def update_memory(memory_id: str, memory: str, topics: Optional[List[str]] = None) -> str:
1014
+ """Use this function to update an existing memory in the database.
1015
+ Args:
1016
+ memory_id (str): The id of the memory to be updated.
1017
+ memory (str): The updated memory.
1018
+ topics (Optional[List[str]]): The topics of the memory (e.g. ["name", "hobbies", "location"]).
1019
+ Returns:
1020
+ str: A message indicating if the memory was updated successfully or not.
1021
+ """
1022
+ from agno.db.base import UserMemory
1023
+
1024
+ try:
1025
+ db.upsert_user_memory(
1026
+ UserMemory(
1027
+ memory_id=memory_id,
1028
+ memory=memory,
1029
+ topics=topics,
1030
+ input=input_string,
1031
+ )
1032
+ )
1033
+ log_debug("Memory updated")
1034
+ return "Memory updated successfully"
1035
+ except Exception as e:
1036
+ log_warning(f"Error storing memory in db: {e}")
1037
+ return f"Error adding memory: {e}"
1038
+
1039
+ def delete_memory(memory_id: str) -> str:
1040
+ """Use this function to delete a single memory from the database.
1041
+ Args:
1042
+ memory_id (str): The id of the memory to be deleted.
1043
+ Returns:
1044
+ str: A message indicating if the memory was deleted successfully or not.
1045
+ """
1046
+ try:
1047
+ db.delete_user_memory(memory_id=memory_id)
1048
+ log_debug("Memory deleted")
1049
+ return "Memory deleted successfully"
1050
+ except Exception as e:
1051
+ log_warning(f"Error deleting memory in db: {e}")
1052
+ return f"Error deleting memory: {e}"
1053
+
1054
+ def clear_memory() -> str:
1055
+ """Use this function to remove all (or clear all) memories from the database.
1056
+
1057
+ Returns:
1058
+ str: A message indicating if the memory was cleared successfully or not.
1059
+ """
1060
+ db.clear_memories()
1061
+ log_debug("Memory cleared")
1062
+ return "Memory cleared successfully"
1063
+
1064
+ functions: List[Callable] = []
1065
+ if enable_add_memory:
1066
+ functions.append(add_memory)
1067
+ if enable_update_memory:
1068
+ functions.append(update_memory)
1069
+ if enable_delete_memory:
1070
+ functions.append(delete_memory)
1071
+ if enable_clear_memory:
1072
+ functions.append(clear_memory)
1073
+ return functions