agno 2.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 (314) hide show
  1. agno/agent/agent.py +5540 -2273
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/compression/__init__.py +3 -0
  5. agno/compression/manager.py +247 -0
  6. agno/culture/__init__.py +3 -0
  7. agno/culture/manager.py +956 -0
  8. agno/db/async_postgres/__init__.py +3 -0
  9. agno/db/base.py +689 -6
  10. agno/db/dynamo/dynamo.py +933 -37
  11. agno/db/dynamo/schemas.py +174 -10
  12. agno/db/dynamo/utils.py +63 -4
  13. agno/db/firestore/firestore.py +831 -9
  14. agno/db/firestore/schemas.py +51 -0
  15. agno/db/firestore/utils.py +102 -4
  16. agno/db/gcs_json/gcs_json_db.py +660 -12
  17. agno/db/gcs_json/utils.py +60 -26
  18. agno/db/in_memory/in_memory_db.py +287 -14
  19. agno/db/in_memory/utils.py +60 -2
  20. agno/db/json/json_db.py +590 -14
  21. agno/db/json/utils.py +60 -26
  22. agno/db/migrations/manager.py +199 -0
  23. agno/db/migrations/v1_to_v2.py +43 -13
  24. agno/db/migrations/versions/__init__.py +0 -0
  25. agno/db/migrations/versions/v2_3_0.py +938 -0
  26. agno/db/mongo/__init__.py +15 -1
  27. agno/db/mongo/async_mongo.py +2760 -0
  28. agno/db/mongo/mongo.py +879 -11
  29. agno/db/mongo/schemas.py +42 -0
  30. agno/db/mongo/utils.py +80 -8
  31. agno/db/mysql/__init__.py +2 -1
  32. agno/db/mysql/async_mysql.py +2912 -0
  33. agno/db/mysql/mysql.py +946 -68
  34. agno/db/mysql/schemas.py +72 -10
  35. agno/db/mysql/utils.py +198 -7
  36. agno/db/postgres/__init__.py +2 -1
  37. agno/db/postgres/async_postgres.py +2579 -0
  38. agno/db/postgres/postgres.py +942 -57
  39. agno/db/postgres/schemas.py +81 -18
  40. agno/db/postgres/utils.py +164 -2
  41. agno/db/redis/redis.py +671 -7
  42. agno/db/redis/schemas.py +50 -0
  43. agno/db/redis/utils.py +65 -7
  44. agno/db/schemas/__init__.py +2 -1
  45. agno/db/schemas/culture.py +120 -0
  46. agno/db/schemas/evals.py +1 -0
  47. agno/db/schemas/memory.py +17 -2
  48. agno/db/singlestore/schemas.py +63 -0
  49. agno/db/singlestore/singlestore.py +949 -83
  50. agno/db/singlestore/utils.py +60 -2
  51. agno/db/sqlite/__init__.py +2 -1
  52. agno/db/sqlite/async_sqlite.py +2911 -0
  53. agno/db/sqlite/schemas.py +62 -0
  54. agno/db/sqlite/sqlite.py +965 -46
  55. agno/db/sqlite/utils.py +169 -8
  56. agno/db/surrealdb/__init__.py +3 -0
  57. agno/db/surrealdb/metrics.py +292 -0
  58. agno/db/surrealdb/models.py +334 -0
  59. agno/db/surrealdb/queries.py +71 -0
  60. agno/db/surrealdb/surrealdb.py +1908 -0
  61. agno/db/surrealdb/utils.py +147 -0
  62. agno/db/utils.py +2 -0
  63. agno/eval/__init__.py +10 -0
  64. agno/eval/accuracy.py +75 -55
  65. agno/eval/agent_as_judge.py +861 -0
  66. agno/eval/base.py +29 -0
  67. agno/eval/performance.py +16 -7
  68. agno/eval/reliability.py +28 -16
  69. agno/eval/utils.py +35 -17
  70. agno/exceptions.py +27 -2
  71. agno/filters.py +354 -0
  72. agno/guardrails/prompt_injection.py +1 -0
  73. agno/hooks/__init__.py +3 -0
  74. agno/hooks/decorator.py +164 -0
  75. agno/integrations/discord/client.py +1 -1
  76. agno/knowledge/chunking/agentic.py +13 -10
  77. agno/knowledge/chunking/fixed.py +4 -1
  78. agno/knowledge/chunking/semantic.py +9 -4
  79. agno/knowledge/chunking/strategy.py +59 -15
  80. agno/knowledge/embedder/fastembed.py +1 -1
  81. agno/knowledge/embedder/nebius.py +1 -1
  82. agno/knowledge/embedder/ollama.py +8 -0
  83. agno/knowledge/embedder/openai.py +8 -8
  84. agno/knowledge/embedder/sentence_transformer.py +6 -2
  85. agno/knowledge/embedder/vllm.py +262 -0
  86. agno/knowledge/knowledge.py +1618 -318
  87. agno/knowledge/reader/base.py +6 -2
  88. agno/knowledge/reader/csv_reader.py +8 -10
  89. agno/knowledge/reader/docx_reader.py +5 -6
  90. agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
  91. agno/knowledge/reader/json_reader.py +5 -4
  92. agno/knowledge/reader/markdown_reader.py +8 -8
  93. agno/knowledge/reader/pdf_reader.py +17 -19
  94. agno/knowledge/reader/pptx_reader.py +101 -0
  95. agno/knowledge/reader/reader_factory.py +32 -3
  96. agno/knowledge/reader/s3_reader.py +3 -3
  97. agno/knowledge/reader/tavily_reader.py +193 -0
  98. agno/knowledge/reader/text_reader.py +22 -10
  99. agno/knowledge/reader/web_search_reader.py +1 -48
  100. agno/knowledge/reader/website_reader.py +10 -10
  101. agno/knowledge/reader/wikipedia_reader.py +33 -1
  102. agno/knowledge/types.py +1 -0
  103. agno/knowledge/utils.py +72 -7
  104. agno/media.py +22 -6
  105. agno/memory/__init__.py +14 -1
  106. agno/memory/manager.py +544 -83
  107. agno/memory/strategies/__init__.py +15 -0
  108. agno/memory/strategies/base.py +66 -0
  109. agno/memory/strategies/summarize.py +196 -0
  110. agno/memory/strategies/types.py +37 -0
  111. agno/models/aimlapi/aimlapi.py +17 -0
  112. agno/models/anthropic/claude.py +515 -40
  113. agno/models/aws/bedrock.py +102 -21
  114. agno/models/aws/claude.py +131 -274
  115. agno/models/azure/ai_foundry.py +41 -19
  116. agno/models/azure/openai_chat.py +39 -8
  117. agno/models/base.py +1249 -525
  118. agno/models/cerebras/cerebras.py +91 -21
  119. agno/models/cerebras/cerebras_openai.py +21 -2
  120. agno/models/cohere/chat.py +40 -6
  121. agno/models/cometapi/cometapi.py +18 -1
  122. agno/models/dashscope/dashscope.py +2 -3
  123. agno/models/deepinfra/deepinfra.py +18 -1
  124. agno/models/deepseek/deepseek.py +69 -3
  125. agno/models/fireworks/fireworks.py +18 -1
  126. agno/models/google/gemini.py +877 -80
  127. agno/models/google/utils.py +22 -0
  128. agno/models/groq/groq.py +51 -18
  129. agno/models/huggingface/huggingface.py +17 -6
  130. agno/models/ibm/watsonx.py +16 -6
  131. agno/models/internlm/internlm.py +18 -1
  132. agno/models/langdb/langdb.py +13 -1
  133. agno/models/litellm/chat.py +44 -9
  134. agno/models/litellm/litellm_openai.py +18 -1
  135. agno/models/message.py +28 -5
  136. agno/models/meta/llama.py +47 -14
  137. agno/models/meta/llama_openai.py +22 -17
  138. agno/models/mistral/mistral.py +8 -4
  139. agno/models/nebius/nebius.py +6 -7
  140. agno/models/nvidia/nvidia.py +20 -3
  141. agno/models/ollama/chat.py +24 -8
  142. agno/models/openai/chat.py +104 -29
  143. agno/models/openai/responses.py +101 -81
  144. agno/models/openrouter/openrouter.py +60 -3
  145. agno/models/perplexity/perplexity.py +17 -1
  146. agno/models/portkey/portkey.py +7 -6
  147. agno/models/requesty/requesty.py +24 -4
  148. agno/models/response.py +73 -2
  149. agno/models/sambanova/sambanova.py +20 -3
  150. agno/models/siliconflow/siliconflow.py +19 -2
  151. agno/models/together/together.py +20 -3
  152. agno/models/utils.py +254 -8
  153. agno/models/vercel/v0.py +20 -3
  154. agno/models/vertexai/__init__.py +0 -0
  155. agno/models/vertexai/claude.py +190 -0
  156. agno/models/vllm/vllm.py +19 -14
  157. agno/models/xai/xai.py +19 -2
  158. agno/os/app.py +549 -152
  159. agno/os/auth.py +190 -3
  160. agno/os/config.py +23 -0
  161. agno/os/interfaces/a2a/router.py +8 -11
  162. agno/os/interfaces/a2a/utils.py +1 -1
  163. agno/os/interfaces/agui/router.py +18 -3
  164. agno/os/interfaces/agui/utils.py +152 -39
  165. agno/os/interfaces/slack/router.py +55 -37
  166. agno/os/interfaces/slack/slack.py +9 -1
  167. agno/os/interfaces/whatsapp/router.py +0 -1
  168. agno/os/interfaces/whatsapp/security.py +3 -1
  169. agno/os/mcp.py +110 -52
  170. agno/os/middleware/__init__.py +2 -0
  171. agno/os/middleware/jwt.py +676 -112
  172. agno/os/router.py +40 -1478
  173. agno/os/routers/agents/__init__.py +3 -0
  174. agno/os/routers/agents/router.py +599 -0
  175. agno/os/routers/agents/schema.py +261 -0
  176. agno/os/routers/evals/evals.py +96 -39
  177. agno/os/routers/evals/schemas.py +65 -33
  178. agno/os/routers/evals/utils.py +80 -10
  179. agno/os/routers/health.py +10 -4
  180. agno/os/routers/knowledge/knowledge.py +196 -38
  181. agno/os/routers/knowledge/schemas.py +82 -22
  182. agno/os/routers/memory/memory.py +279 -52
  183. agno/os/routers/memory/schemas.py +46 -17
  184. agno/os/routers/metrics/metrics.py +20 -8
  185. agno/os/routers/metrics/schemas.py +16 -16
  186. agno/os/routers/session/session.py +462 -34
  187. agno/os/routers/teams/__init__.py +3 -0
  188. agno/os/routers/teams/router.py +512 -0
  189. agno/os/routers/teams/schema.py +257 -0
  190. agno/os/routers/traces/__init__.py +3 -0
  191. agno/os/routers/traces/schemas.py +414 -0
  192. agno/os/routers/traces/traces.py +499 -0
  193. agno/os/routers/workflows/__init__.py +3 -0
  194. agno/os/routers/workflows/router.py +624 -0
  195. agno/os/routers/workflows/schema.py +75 -0
  196. agno/os/schema.py +256 -693
  197. agno/os/scopes.py +469 -0
  198. agno/os/utils.py +514 -36
  199. agno/reasoning/anthropic.py +80 -0
  200. agno/reasoning/gemini.py +73 -0
  201. agno/reasoning/openai.py +5 -0
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +155 -32
  205. agno/run/base.py +55 -3
  206. agno/run/requirement.py +181 -0
  207. agno/run/team.py +125 -38
  208. agno/run/workflow.py +72 -18
  209. agno/session/agent.py +102 -89
  210. agno/session/summary.py +56 -15
  211. agno/session/team.py +164 -90
  212. agno/session/workflow.py +405 -40
  213. agno/table.py +10 -0
  214. agno/team/team.py +3974 -1903
  215. agno/tools/dalle.py +2 -4
  216. agno/tools/eleven_labs.py +23 -25
  217. agno/tools/exa.py +21 -16
  218. agno/tools/file.py +153 -23
  219. agno/tools/file_generation.py +16 -10
  220. agno/tools/firecrawl.py +15 -7
  221. agno/tools/function.py +193 -38
  222. agno/tools/gmail.py +238 -14
  223. agno/tools/google_drive.py +271 -0
  224. agno/tools/googlecalendar.py +36 -8
  225. agno/tools/googlesheets.py +20 -5
  226. agno/tools/jira.py +20 -0
  227. agno/tools/mcp/__init__.py +10 -0
  228. agno/tools/mcp/mcp.py +331 -0
  229. agno/tools/mcp/multi_mcp.py +347 -0
  230. agno/tools/mcp/params.py +24 -0
  231. agno/tools/mcp_toolbox.py +3 -3
  232. agno/tools/models/nebius.py +5 -5
  233. agno/tools/models_labs.py +20 -10
  234. agno/tools/nano_banana.py +151 -0
  235. agno/tools/notion.py +204 -0
  236. agno/tools/parallel.py +314 -0
  237. agno/tools/postgres.py +76 -36
  238. agno/tools/redshift.py +406 -0
  239. agno/tools/scrapegraph.py +1 -1
  240. agno/tools/shopify.py +1519 -0
  241. agno/tools/slack.py +18 -3
  242. agno/tools/spotify.py +919 -0
  243. agno/tools/tavily.py +146 -0
  244. agno/tools/toolkit.py +25 -0
  245. agno/tools/workflow.py +8 -1
  246. agno/tools/yfinance.py +12 -11
  247. agno/tracing/__init__.py +12 -0
  248. agno/tracing/exporter.py +157 -0
  249. agno/tracing/schemas.py +276 -0
  250. agno/tracing/setup.py +111 -0
  251. agno/utils/agent.py +938 -0
  252. agno/utils/cryptography.py +22 -0
  253. agno/utils/dttm.py +33 -0
  254. agno/utils/events.py +151 -3
  255. agno/utils/gemini.py +15 -5
  256. agno/utils/hooks.py +118 -4
  257. agno/utils/http.py +113 -2
  258. agno/utils/knowledge.py +12 -5
  259. agno/utils/log.py +1 -0
  260. agno/utils/mcp.py +92 -2
  261. agno/utils/media.py +187 -1
  262. agno/utils/merge_dict.py +3 -3
  263. agno/utils/message.py +60 -0
  264. agno/utils/models/ai_foundry.py +9 -2
  265. agno/utils/models/claude.py +49 -14
  266. agno/utils/models/cohere.py +9 -2
  267. agno/utils/models/llama.py +9 -2
  268. agno/utils/models/mistral.py +4 -2
  269. agno/utils/print_response/agent.py +109 -16
  270. agno/utils/print_response/team.py +223 -30
  271. agno/utils/print_response/workflow.py +251 -34
  272. agno/utils/streamlit.py +1 -1
  273. agno/utils/team.py +98 -9
  274. agno/utils/tokens.py +657 -0
  275. agno/vectordb/base.py +39 -7
  276. agno/vectordb/cassandra/cassandra.py +21 -5
  277. agno/vectordb/chroma/chromadb.py +43 -12
  278. agno/vectordb/clickhouse/clickhousedb.py +21 -5
  279. agno/vectordb/couchbase/couchbase.py +29 -5
  280. agno/vectordb/lancedb/lance_db.py +92 -181
  281. agno/vectordb/langchaindb/langchaindb.py +24 -4
  282. agno/vectordb/lightrag/lightrag.py +17 -3
  283. agno/vectordb/llamaindex/llamaindexdb.py +25 -5
  284. agno/vectordb/milvus/milvus.py +50 -37
  285. agno/vectordb/mongodb/__init__.py +7 -1
  286. agno/vectordb/mongodb/mongodb.py +36 -30
  287. agno/vectordb/pgvector/pgvector.py +201 -77
  288. agno/vectordb/pineconedb/pineconedb.py +41 -23
  289. agno/vectordb/qdrant/qdrant.py +67 -54
  290. agno/vectordb/redis/__init__.py +9 -0
  291. agno/vectordb/redis/redisdb.py +682 -0
  292. agno/vectordb/singlestore/singlestore.py +50 -29
  293. agno/vectordb/surrealdb/surrealdb.py +31 -41
  294. agno/vectordb/upstashdb/upstashdb.py +34 -6
  295. agno/vectordb/weaviate/weaviate.py +53 -14
  296. agno/workflow/__init__.py +2 -0
  297. agno/workflow/agent.py +299 -0
  298. agno/workflow/condition.py +120 -18
  299. agno/workflow/loop.py +77 -10
  300. agno/workflow/parallel.py +231 -143
  301. agno/workflow/router.py +118 -17
  302. agno/workflow/step.py +609 -170
  303. agno/workflow/steps.py +73 -6
  304. agno/workflow/types.py +96 -21
  305. agno/workflow/workflow.py +2039 -262
  306. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
  307. agno-2.3.13.dist-info/RECORD +613 -0
  308. agno/tools/googlesearch.py +0 -98
  309. agno/tools/mcp.py +0 -679
  310. agno/tools/memori.py +0 -339
  311. agno-2.1.2.dist-info/RECORD +0 -543
  312. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
  313. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
  314. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/memory/manager.py CHANGED
@@ -1,18 +1,30 @@
1
1
  from copy import deepcopy
2
2
  from dataclasses import dataclass
3
- from datetime import datetime
4
3
  from os import getenv
5
4
  from textwrap import dedent
6
5
  from typing import Any, Callable, Dict, List, Literal, Optional, Type, Union
7
6
 
8
7
  from pydantic import BaseModel, Field
9
8
 
10
- from agno.db.base import BaseDb
9
+ from agno.db.base import AsyncBaseDb, BaseDb
11
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
+ )
12
16
  from agno.models.base import Model
13
17
  from agno.models.message import Message
18
+ from agno.models.utils import get_model
14
19
  from agno.tools.function import Function
15
- from agno.utils.log import log_debug, log_error, log_warning, set_log_level_to_debug, set_log_level_to_info
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
+ )
16
28
  from agno.utils.prompts import get_json_output_prompt
17
29
  from agno.utils.string import parse_response_model_str
18
30
 
@@ -21,7 +33,8 @@ class MemorySearchResponse(BaseModel):
21
33
  """Model for Memory Search Response."""
22
34
 
23
35
  memory_ids: List[str] = Field(
24
- ..., description="The IDs of the memories that are most semantically similar to the query."
36
+ ...,
37
+ description="The IDs of the memories that are most semantically similar to the query.",
25
38
  )
26
39
 
27
40
 
@@ -32,11 +45,11 @@ class MemoryManager:
32
45
  # Model used for memory management
33
46
  model: Optional[Model] = None
34
47
 
35
- # Provide the system message for the manager as a string. If not provided, a default prompt will be used.
48
+ # Provide the system message for the manager as a string. If not provided, the default system message will be used.
36
49
  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.
50
+ # Provide the memory capture instructions for the manager as a string. If not provided, the default memory capture instructions will be used.
38
51
  memory_capture_instructions: Optional[str] = None
39
- # Additional instructions for the manager
52
+ # Additional instructions for the manager. These instructions are appended to the default system message.
40
53
  additional_instructions: Optional[str] = None
41
54
 
42
55
  # Whether memories were created in the last run
@@ -53,26 +66,24 @@ class MemoryManager:
53
66
  add_memories: bool = True
54
67
 
55
68
  # The database to store memories
56
- db: Optional[BaseDb] = None
69
+ db: Optional[Union[BaseDb, AsyncBaseDb]] = None
57
70
 
58
71
  debug_mode: bool = False
59
72
 
60
73
  def __init__(
61
74
  self,
62
- model: Optional[Model] = None,
75
+ model: Optional[Union[Model, str]] = None,
63
76
  system_message: Optional[str] = None,
64
77
  memory_capture_instructions: Optional[str] = None,
65
78
  additional_instructions: Optional[str] = None,
66
- db: Optional[BaseDb] = None,
67
- delete_memories: bool = True,
79
+ db: Optional[Union[BaseDb, AsyncBaseDb]] = None,
80
+ delete_memories: bool = False,
68
81
  update_memories: bool = True,
69
82
  add_memories: bool = True,
70
- clear_memories: bool = True,
83
+ clear_memories: bool = False,
71
84
  debug_mode: bool = False,
72
85
  ):
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")
86
+ self.model = model # type: ignore[assignment]
76
87
  self.system_message = system_message
77
88
  self.memory_capture_instructions = memory_capture_instructions
78
89
  self.additional_instructions = additional_instructions
@@ -82,8 +93,9 @@ class MemoryManager:
82
93
  self.add_memories = add_memories
83
94
  self.clear_memories = clear_memories
84
95
  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
96
+
97
+ if self.model is not None:
98
+ self.model = get_model(self.model)
87
99
 
88
100
  def get_model(self) -> Model:
89
101
  if self.model is None:
@@ -114,6 +126,28 @@ class MemoryManager:
114
126
  return memories
115
127
  return None
116
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
+
117
151
  def set_log_level(self):
118
152
  if self.debug_mode or getenv("AGNO_DEBUG", "false").lower() == "true":
119
153
  self.debug_mode = True
@@ -139,6 +173,20 @@ class MemoryManager:
139
173
  log_warning("Memory Db not provided.")
140
174
  return []
141
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
+
142
190
  def get_user_memory(self, memory_id: str, user_id: Optional[str] = None) -> Optional[UserMemory]:
143
191
  """Get the user memory for a given user id"""
144
192
  if self.db:
@@ -181,7 +229,7 @@ class MemoryManager:
181
229
  memory.user_id = user_id
182
230
 
183
231
  if not memory.updated_at:
184
- memory.updated_at = datetime.now()
232
+ memory.updated_at = now_epoch_s()
185
233
 
186
234
  self._upsert_db_memory(memory=memory)
187
235
  return memory.memory_id
@@ -209,7 +257,7 @@ class MemoryManager:
209
257
  user_id = "default"
210
258
 
211
259
  if not memory.updated_at:
212
- memory.updated_at = datetime.now()
260
+ memory.updated_at = now_epoch_s()
213
261
 
214
262
  memory.memory_id = memory_id
215
263
  memory.user_id = user_id
@@ -245,6 +293,74 @@ class MemoryManager:
245
293
  log_warning("Memory DB not provided.")
246
294
  return None
247
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
+
248
364
  # -*- Agent Functions
249
365
  def create_user_memories(
250
366
  self,
@@ -261,6 +377,11 @@ class MemoryManager:
261
377
  log_warning("MemoryDb not provided.")
262
378
  return "Please provide a db to store memories"
263
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
+
264
385
  if not messages and not message:
265
386
  raise ValueError("You must provide either a message or a list of messages")
266
387
 
@@ -321,7 +442,10 @@ class MemoryManager:
321
442
  if user_id is None:
322
443
  user_id = "default"
323
444
 
324
- memories = self.read_from_db(user_id=user_id)
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)
325
449
  if memories is None:
326
450
  memories = {}
327
451
 
@@ -340,7 +464,10 @@ class MemoryManager:
340
464
  )
341
465
 
342
466
  # We refresh from the DB
343
- self.read_from_db(user_id=user_id)
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)
344
471
 
345
472
  return response
346
473
 
@@ -351,6 +478,11 @@ class MemoryManager:
351
478
  log_warning("MemoryDb not provided.")
352
479
  return "Please provide a db to store memories"
353
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
+
354
486
  if user_id is None:
355
487
  user_id = "default"
356
488
 
@@ -388,7 +520,11 @@ class MemoryManager:
388
520
  if user_id is None:
389
521
  user_id = "default"
390
522
 
391
- memories = self.read_from_db(user_id=user_id)
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
+
392
528
  if memories is None:
393
529
  memories = {}
394
530
 
@@ -407,7 +543,10 @@ class MemoryManager:
407
543
  )
408
544
 
409
545
  # We refresh from the DB
410
- self.read_from_db(user_id=user_id)
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)
411
550
 
412
551
  return response
413
552
 
@@ -602,7 +741,7 @@ class MemoryManager:
602
741
  # If updated_at is None, place at the beginning of the list
603
742
  sorted_memories_list = sorted(
604
743
  memories_list,
605
- key=lambda memory: memory.updated_at or datetime.min,
744
+ key=lambda m: m.updated_at if m.updated_at is not None else 0,
606
745
  )
607
746
  else:
608
747
  sorted_memories_list = []
@@ -625,6 +764,7 @@ class MemoryManager:
625
764
  if memories is None:
626
765
  memories = {}
627
766
 
767
+ MAX_UNIX_TS = 2**63 - 1
628
768
  memories_list = memories.get(user_id, [])
629
769
  # Sort memories by updated_at timestamp if available
630
770
  if memories_list:
@@ -632,7 +772,7 @@ class MemoryManager:
632
772
  # If updated_at is None, place at the end of the list
633
773
  sorted_memories_list = sorted(
634
774
  memories_list,
635
- key=lambda memory: memory.updated_at or datetime.max,
775
+ key=lambda m: m.updated_at if m.updated_at is not None else MAX_UNIX_TS,
636
776
  )
637
777
 
638
778
  else:
@@ -643,24 +783,171 @@ class MemoryManager:
643
783
 
644
784
  return sorted_memories_list
645
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
+
646
930
  # --Memory Manager Functions--
647
- def determine_tools_for_model(self, tools: List[Callable]) -> None:
931
+ def determine_tools_for_model(self, tools: List[Callable]) -> List[Union[Function, dict]]:
648
932
  # Have to reset each time, because of different user IDs
649
- self._tools_for_model = []
650
- self._functions_for_model = {}
933
+ _function_names = []
934
+ _functions: List[Union[Function, dict]] = []
651
935
 
652
936
  for tool in tools:
653
937
  try:
654
938
  function_name = tool.__name__
655
- if function_name not in self._functions_for_model:
656
- func = Function.from_callable(tool, strict=True) # type: ignore
657
- func.strict = True
658
- self._functions_for_model[func.name] = func
659
- self._tools_for_model.append({"type": "function", "function": func.to_dict()})
660
- log_debug(f"Added function {func.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}")
661
946
  except Exception as e:
662
947
  log_warning(f"Could not add function {tool}: {e}")
663
948
 
949
+ return _functions
950
+
664
951
  def get_system_message(
665
952
  self,
666
953
  existing_memories: Optional[List[Dict[str, Any]]] = None,
@@ -672,18 +959,20 @@ class MemoryManager:
672
959
  if self.system_message is not None:
673
960
  return Message(role="system", content=self.system_message)
674
961
 
675
- memory_capture_instructions = self.memory_capture_instructions or dedent("""\
676
- Memories should include details that could personalize ongoing interactions with the user, such as:
677
- - Personal facts: name, age, occupation, location, interests, preferences, etc.
678
- - Significant life events or experiences shared by the user
679
- - Important context about the user's current situation, challenges or goals
680
- - What the user likes or dislikes, their opinions, beliefs, values, etc.
681
- - Any other details that provide valuable insights into the user's personality, perspective or needs\
682
- """)
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
+ )
683
972
 
684
973
  # -*- Return a system message for the memory manager
685
974
  system_prompt_lines = [
686
- "You are a MemoryConnector that is responsible for manging key information about the user. "
975
+ "You are a Memory Manager that is responsible for managing information and preferences about the user. "
687
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.",
688
977
  "",
689
978
  "## When to add or update memories",
@@ -712,16 +1001,16 @@ class MemoryManager:
712
1001
  "",
713
1002
  "## Updating memories",
714
1003
  "You will also be provided with a list of existing memories in the <existing_memories> section. You can:",
715
- " 1. Decide to make no changes.",
1004
+ " - Decide to make no changes.",
716
1005
  ]
717
1006
  if enable_add_memory:
718
- system_prompt_lines.append(" 2. Decide to add a new memory, using the `add_memory` tool.")
1007
+ system_prompt_lines.append(" - Decide to add a new memory, using the `add_memory` tool.")
719
1008
  if enable_update_memory:
720
- system_prompt_lines.append(" 3. Decide to update an existing memory, using the `update_memory` tool.")
1009
+ system_prompt_lines.append(" - Decide to update an existing memory, using the `update_memory` tool.")
721
1010
  if enable_delete_memory:
722
- system_prompt_lines.append(" 4. Decide to delete an existing memory, using the `delete_memory` tool.")
1011
+ system_prompt_lines.append(" - Decide to delete an existing memory, using the `delete_memory` tool.")
723
1012
  if enable_clear_memory:
724
- system_prompt_lines.append(" 5. Decide to clear all memories, using the `clear_memory` tool.")
1013
+ system_prompt_lines.append(" - Decide to clear all memories, using the `clear_memory` tool.")
725
1014
 
726
1015
  system_prompt_lines += [
727
1016
  "You can call multiple tools in a single response if needed. ",
@@ -765,7 +1054,7 @@ class MemoryManager:
765
1054
 
766
1055
  model_copy = deepcopy(self.model)
767
1056
  # Update the Model (set defaults, add logit etc.)
768
- self.determine_tools_for_model(
1057
+ _tools = self.determine_tools_for_model(
769
1058
  self._get_db_tools(
770
1059
  user_id,
771
1060
  db,
@@ -774,7 +1063,7 @@ class MemoryManager:
774
1063
  team_id=team_id,
775
1064
  enable_add_memory=add_memories,
776
1065
  enable_update_memory=update_memories,
777
- enable_delete_memory=False,
1066
+ enable_delete_memory=True,
778
1067
  enable_clear_memory=False,
779
1068
  ),
780
1069
  )
@@ -785,7 +1074,7 @@ class MemoryManager:
785
1074
  existing_memories=existing_memories,
786
1075
  enable_update_memory=update_memories,
787
1076
  enable_add_memory=add_memories,
788
- enable_delete_memory=False,
1077
+ enable_delete_memory=True,
789
1078
  enable_clear_memory=False,
790
1079
  ),
791
1080
  *messages,
@@ -793,7 +1082,8 @@ class MemoryManager:
793
1082
 
794
1083
  # Generate a response from the Model (includes running function calls)
795
1084
  response = model_copy.response(
796
- messages=messages_for_model, tools=self._tools_for_model, functions=self._functions_for_model
1085
+ messages=messages_for_model,
1086
+ tools=_tools,
797
1087
  )
798
1088
 
799
1089
  if response.tool_calls is not None and len(response.tool_calls) > 0:
@@ -807,7 +1097,7 @@ class MemoryManager:
807
1097
  messages: List[Message],
808
1098
  existing_memories: List[Dict[str, Any]],
809
1099
  user_id: str,
810
- db: BaseDb,
1100
+ db: Union[BaseDb, AsyncBaseDb],
811
1101
  agent_id: Optional[str] = None,
812
1102
  team_id: Optional[str] = None,
813
1103
  update_memories: bool = True,
@@ -826,19 +1116,34 @@ class MemoryManager:
826
1116
 
827
1117
  model_copy = deepcopy(self.model)
828
1118
  # Update the Model (set defaults, add logit etc.)
829
- self.determine_tools_for_model(
830
- self._get_db_tools(
831
- user_id,
832
- db,
833
- input_string,
834
- agent_id=agent_id,
835
- team_id=team_id,
836
- enable_add_memory=add_memories,
837
- enable_update_memory=update_memories,
838
- enable_delete_memory=False,
839
- enable_clear_memory=False,
840
- ),
841
- )
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
+ )
842
1147
 
843
1148
  # Prepare the List of messages to send to the Model
844
1149
  messages_for_model: List[Message] = [
@@ -846,7 +1151,7 @@ class MemoryManager:
846
1151
  existing_memories=existing_memories,
847
1152
  enable_update_memory=update_memories,
848
1153
  enable_add_memory=add_memories,
849
- enable_delete_memory=False,
1154
+ enable_delete_memory=True,
850
1155
  enable_clear_memory=False,
851
1156
  ),
852
1157
  *messages,
@@ -854,7 +1159,8 @@ class MemoryManager:
854
1159
 
855
1160
  # Generate a response from the Model (includes running function calls)
856
1161
  response = await model_copy.aresponse(
857
- messages=messages_for_model, tools=self._tools_for_model, functions=self._functions_for_model
1162
+ messages=messages_for_model,
1163
+ tools=_tools,
858
1164
  )
859
1165
 
860
1166
  if response.tool_calls is not None and len(response.tool_calls) > 0:
@@ -882,7 +1188,7 @@ class MemoryManager:
882
1188
 
883
1189
  model_copy = deepcopy(self.model)
884
1190
  # Update the Model (set defaults, add logit etc.)
885
- self.determine_tools_for_model(
1191
+ _tools = self.determine_tools_for_model(
886
1192
  self._get_db_tools(
887
1193
  user_id,
888
1194
  db,
@@ -909,7 +1215,8 @@ class MemoryManager:
909
1215
 
910
1216
  # Generate a response from the Model (includes running function calls)
911
1217
  response = model_copy.response(
912
- messages=messages_for_model, tools=self._tools_for_model, functions=self._functions_for_model
1218
+ messages=messages_for_model,
1219
+ tools=_tools,
913
1220
  )
914
1221
 
915
1222
  if response.tool_calls is not None and len(response.tool_calls) > 0:
@@ -923,7 +1230,7 @@ class MemoryManager:
923
1230
  task: str,
924
1231
  existing_memories: List[Dict[str, Any]],
925
1232
  user_id: str,
926
- db: BaseDb,
1233
+ db: Union[BaseDb, AsyncBaseDb],
927
1234
  delete_memories: bool = True,
928
1235
  clear_memories: bool = True,
929
1236
  update_memories: bool = True,
@@ -937,17 +1244,30 @@ class MemoryManager:
937
1244
 
938
1245
  model_copy = deepcopy(self.model)
939
1246
  # Update the Model (set defaults, add logit etc.)
940
- self.determine_tools_for_model(
941
- self._get_db_tools(
942
- user_id,
943
- db,
944
- task,
945
- enable_delete_memory=delete_memories,
946
- enable_clear_memory=clear_memories,
947
- enable_update_memory=update_memories,
948
- enable_add_memory=add_memories,
949
- ),
950
- )
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
+ )
951
1271
 
952
1272
  # Prepare the List of messages to send to the Model
953
1273
  messages_for_model: List[Message] = [
@@ -964,7 +1284,8 @@ class MemoryManager:
964
1284
 
965
1285
  # Generate a response from the Model (includes running function calls)
966
1286
  response = await model_copy.aresponse(
967
- messages=messages_for_model, tools=self._tools_for_model, functions=self._functions_for_model
1287
+ messages=messages_for_model,
1288
+ tools=_tools,
968
1289
  )
969
1290
 
970
1291
  if response.tool_calls is not None and len(response.tool_calls) > 0:
@@ -1028,6 +1349,9 @@ class MemoryManager:
1028
1349
  """
1029
1350
  from agno.db.base import UserMemory
1030
1351
 
1352
+ if memory == "":
1353
+ return "Can't update memory with empty string. Use the delete memory function if available."
1354
+
1031
1355
  try:
1032
1356
  db.upsert_user_memory(
1033
1357
  UserMemory(
@@ -1079,3 +1403,140 @@ class MemoryManager:
1079
1403
  if enable_clear_memory:
1080
1404
  functions.append(clear_memory)
1081
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