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.
- agno/agent/agent.py +5540 -2273
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +689 -6
- agno/db/dynamo/dynamo.py +933 -37
- agno/db/dynamo/schemas.py +174 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +831 -9
- agno/db/firestore/schemas.py +51 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +660 -12
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +287 -14
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +590 -14
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +43 -13
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +15 -1
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +879 -11
- agno/db/mongo/schemas.py +42 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +946 -68
- agno/db/mysql/schemas.py +72 -10
- agno/db/mysql/utils.py +198 -7
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +942 -57
- agno/db/postgres/schemas.py +81 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +671 -7
- agno/db/redis/schemas.py +50 -0
- agno/db/redis/utils.py +65 -7
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +17 -2
- agno/db/singlestore/schemas.py +63 -0
- agno/db/singlestore/singlestore.py +949 -83
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +62 -0
- agno/db/sqlite/sqlite.py +965 -46
- agno/db/sqlite/utils.py +169 -8
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +2 -0
- agno/eval/__init__.py +10 -0
- agno/eval/accuracy.py +75 -55
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +16 -7
- agno/eval/reliability.py +28 -16
- agno/eval/utils.py +35 -17
- agno/exceptions.py +27 -2
- agno/filters.py +354 -0
- agno/guardrails/prompt_injection.py +1 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +1 -1
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/semantic.py +9 -4
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +8 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/knowledge.py +1618 -318
- agno/knowledge/reader/base.py +6 -2
- agno/knowledge/reader/csv_reader.py +8 -10
- agno/knowledge/reader/docx_reader.py +5 -6
- agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
- agno/knowledge/reader/json_reader.py +5 -4
- agno/knowledge/reader/markdown_reader.py +8 -8
- agno/knowledge/reader/pdf_reader.py +17 -19
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +32 -3
- agno/knowledge/reader/s3_reader.py +3 -3
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +22 -10
- agno/knowledge/reader/web_search_reader.py +1 -48
- agno/knowledge/reader/website_reader.py +10 -10
- agno/knowledge/reader/wikipedia_reader.py +33 -1
- agno/knowledge/types.py +1 -0
- agno/knowledge/utils.py +72 -7
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +544 -83
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +515 -40
- agno/models/aws/bedrock.py +102 -21
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +41 -19
- agno/models/azure/openai_chat.py +39 -8
- agno/models/base.py +1249 -525
- agno/models/cerebras/cerebras.py +91 -21
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +40 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +877 -80
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +51 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +44 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +28 -5
- agno/models/meta/llama.py +47 -14
- agno/models/meta/llama_openai.py +22 -17
- agno/models/mistral/mistral.py +8 -4
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/chat.py +24 -8
- agno/models/openai/chat.py +104 -29
- agno/models/openai/responses.py +101 -81
- agno/models/openrouter/openrouter.py +60 -3
- agno/models/perplexity/perplexity.py +17 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +24 -4
- agno/models/response.py +73 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +549 -152
- agno/os/auth.py +190 -3
- agno/os/config.py +23 -0
- agno/os/interfaces/a2a/router.py +8 -11
- agno/os/interfaces/a2a/utils.py +1 -1
- agno/os/interfaces/agui/router.py +18 -3
- agno/os/interfaces/agui/utils.py +152 -39
- agno/os/interfaces/slack/router.py +55 -37
- agno/os/interfaces/slack/slack.py +9 -1
- agno/os/interfaces/whatsapp/router.py +0 -1
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/mcp.py +110 -52
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/jwt.py +676 -112
- agno/os/router.py +40 -1478
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +96 -39
- agno/os/routers/evals/schemas.py +65 -33
- agno/os/routers/evals/utils.py +80 -10
- agno/os/routers/health.py +10 -4
- agno/os/routers/knowledge/knowledge.py +196 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +279 -52
- agno/os/routers/memory/schemas.py +46 -17
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +462 -34
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +256 -693
- agno/os/scopes.py +469 -0
- agno/os/utils.py +514 -36
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/openai.py +5 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +155 -32
- agno/run/base.py +55 -3
- agno/run/requirement.py +181 -0
- agno/run/team.py +125 -38
- agno/run/workflow.py +72 -18
- agno/session/agent.py +102 -89
- agno/session/summary.py +56 -15
- agno/session/team.py +164 -90
- agno/session/workflow.py +405 -40
- agno/table.py +10 -0
- agno/team/team.py +3974 -1903
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +16 -10
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +193 -38
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +271 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +3 -3
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +76 -36
- agno/tools/redshift.py +406 -0
- agno/tools/scrapegraph.py +1 -1
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +18 -3
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +146 -0
- agno/tools/toolkit.py +25 -0
- agno/tools/workflow.py +8 -1
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +151 -3
- agno/utils/gemini.py +15 -5
- agno/utils/hooks.py +118 -4
- agno/utils/http.py +113 -2
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +187 -1
- agno/utils/merge_dict.py +3 -3
- agno/utils/message.py +60 -0
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +49 -14
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +109 -16
- agno/utils/print_response/team.py +223 -30
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/streamlit.py +1 -1
- agno/utils/team.py +98 -9
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +39 -7
- agno/vectordb/cassandra/cassandra.py +21 -5
- agno/vectordb/chroma/chromadb.py +43 -12
- agno/vectordb/clickhouse/clickhousedb.py +21 -5
- agno/vectordb/couchbase/couchbase.py +29 -5
- agno/vectordb/lancedb/lance_db.py +92 -181
- agno/vectordb/langchaindb/langchaindb.py +24 -4
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/llamaindexdb.py +25 -5
- agno/vectordb/milvus/milvus.py +50 -37
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +36 -30
- agno/vectordb/pgvector/pgvector.py +201 -77
- agno/vectordb/pineconedb/pineconedb.py +41 -23
- agno/vectordb/qdrant/qdrant.py +67 -54
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/singlestore.py +50 -29
- agno/vectordb/surrealdb/surrealdb.py +31 -41
- agno/vectordb/upstashdb/upstashdb.py +34 -6
- agno/vectordb/weaviate/weaviate.py +53 -14
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +120 -18
- agno/workflow/loop.py +77 -10
- agno/workflow/parallel.py +231 -143
- agno/workflow/router.py +118 -17
- agno/workflow/step.py +609 -170
- agno/workflow/steps.py +73 -6
- agno/workflow/types.py +96 -21
- agno/workflow/workflow.py +2039 -262
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
- agno-2.3.13.dist-info/RECORD +613 -0
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -679
- agno/tools/memori.py +0 -339
- agno-2.1.2.dist-info/RECORD +0 -543
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
- {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.
|
|
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
|
-
...,
|
|
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,
|
|
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,
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
86
|
-
self.
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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]) ->
|
|
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
|
-
|
|
650
|
-
|
|
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
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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
|
|
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
|
-
"
|
|
1004
|
+
" - Decide to make no changes.",
|
|
716
1005
|
]
|
|
717
1006
|
if enable_add_memory:
|
|
718
|
-
system_prompt_lines.append("
|
|
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("
|
|
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("
|
|
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("
|
|
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=
|
|
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=
|
|
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,
|
|
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
|
-
|
|
830
|
-
self.
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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=
|
|
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,
|
|
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,
|
|
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
|
-
|
|
941
|
-
self.
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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,
|
|
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
|