agno 2.0.1__py3-none-any.whl → 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/agent/agent.py +6015 -2823
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +385 -6
- agno/db/dynamo/dynamo.py +388 -81
- agno/db/dynamo/schemas.py +47 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +435 -64
- agno/db/firestore/schemas.py +11 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +384 -42
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +351 -66
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +339 -48
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +510 -37
- 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 +2036 -0
- agno/db/mongo/mongo.py +653 -76
- agno/db/mongo/schemas.py +13 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/mysql.py +687 -25
- agno/db/mysql/schemas.py +61 -37
- agno/db/mysql/utils.py +60 -2
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2001 -0
- agno/db/postgres/postgres.py +676 -57
- agno/db/postgres/schemas.py +43 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +344 -38
- agno/db/redis/schemas.py +18 -0
- agno/db/redis/utils.py +60 -2
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/memory.py +13 -0
- agno/db/singlestore/schemas.py +26 -1
- agno/db/singlestore/singlestore.py +687 -53
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2371 -0
- agno/db/sqlite/schemas.py +24 -0
- agno/db/sqlite/sqlite.py +774 -85
- agno/db/sqlite/utils.py +168 -5
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1361 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +50 -22
- agno/eval/accuracy.py +50 -43
- agno/eval/performance.py +6 -3
- agno/eval/reliability.py +6 -3
- agno/eval/utils.py +33 -16
- agno/exceptions.py +68 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/integrations/discord/client.py +1 -0
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +1 -1
- agno/knowledge/chunking/semantic.py +40 -8
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/aws_bedrock.py +9 -4
- agno/knowledge/embedder/azure_openai.py +54 -0
- agno/knowledge/embedder/base.py +2 -0
- agno/knowledge/embedder/cohere.py +184 -5
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/google.py +79 -1
- agno/knowledge/embedder/huggingface.py +9 -4
- agno/knowledge/embedder/jina.py +63 -0
- agno/knowledge/embedder/mistral.py +78 -11
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +13 -0
- agno/knowledge/embedder/openai.py +37 -65
- agno/knowledge/embedder/sentence_transformer.py +8 -4
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +69 -16
- agno/knowledge/knowledge.py +594 -186
- agno/knowledge/reader/base.py +9 -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 +290 -0
- agno/knowledge/reader/json_reader.py +6 -5
- agno/knowledge/reader/markdown_reader.py +13 -13
- agno/knowledge/reader/pdf_reader.py +43 -68
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +51 -6
- agno/knowledge/reader/s3_reader.py +3 -15
- agno/knowledge/reader/tavily_reader.py +194 -0
- agno/knowledge/reader/text_reader.py +13 -13
- agno/knowledge/reader/web_search_reader.py +2 -43
- agno/knowledge/reader/website_reader.py +43 -25
- agno/knowledge/reranker/__init__.py +2 -8
- agno/knowledge/types.py +9 -0
- agno/knowledge/utils.py +20 -0
- agno/media.py +72 -0
- agno/memory/manager.py +336 -82
- agno/models/aimlapi/aimlapi.py +2 -2
- agno/models/anthropic/claude.py +183 -37
- agno/models/aws/bedrock.py +52 -112
- agno/models/aws/claude.py +33 -1
- agno/models/azure/ai_foundry.py +33 -15
- agno/models/azure/openai_chat.py +25 -8
- agno/models/base.py +999 -519
- agno/models/cerebras/cerebras.py +19 -13
- agno/models/cerebras/cerebras_openai.py +8 -5
- agno/models/cohere/chat.py +27 -1
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/dashscope.py +1 -0
- agno/models/deepinfra/deepinfra.py +2 -2
- agno/models/deepseek/deepseek.py +2 -2
- agno/models/fireworks/fireworks.py +2 -2
- agno/models/google/gemini.py +103 -31
- agno/models/groq/groq.py +28 -11
- agno/models/huggingface/huggingface.py +2 -1
- agno/models/internlm/internlm.py +2 -2
- agno/models/langdb/langdb.py +4 -4
- agno/models/litellm/chat.py +18 -1
- agno/models/litellm/litellm_openai.py +2 -2
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/message.py +139 -0
- agno/models/meta/llama.py +27 -10
- agno/models/meta/llama_openai.py +5 -17
- agno/models/nebius/nebius.py +6 -6
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/nvidia.py +2 -2
- agno/models/ollama/chat.py +59 -5
- agno/models/openai/chat.py +69 -29
- agno/models/openai/responses.py +103 -106
- agno/models/openrouter/openrouter.py +41 -3
- agno/models/perplexity/perplexity.py +4 -5
- agno/models/portkey/portkey.py +3 -3
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +77 -1
- agno/models/sambanova/sambanova.py +2 -2
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/together.py +2 -2
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +2 -2
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +96 -0
- agno/models/vllm/vllm.py +1 -0
- agno/models/xai/xai.py +3 -2
- agno/os/app.py +543 -178
- agno/os/auth.py +24 -14
- agno/os/config.py +1 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +250 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/agui.py +23 -7
- agno/os/interfaces/agui/router.py +27 -3
- agno/os/interfaces/agui/utils.py +242 -142
- agno/os/interfaces/base.py +6 -2
- agno/os/interfaces/slack/router.py +81 -23
- agno/os/interfaces/slack/slack.py +29 -14
- agno/os/interfaces/whatsapp/router.py +11 -4
- agno/os/interfaces/whatsapp/whatsapp.py +14 -7
- agno/os/mcp.py +111 -54
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +556 -139
- agno/os/routers/evals/evals.py +71 -34
- agno/os/routers/evals/schemas.py +31 -31
- agno/os/routers/evals/utils.py +6 -5
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/knowledge.py +185 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +158 -53
- agno/os/routers/memory/schemas.py +20 -16
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +499 -38
- agno/os/schema.py +308 -198
- agno/os/utils.py +401 -41
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +2 -2
- agno/reasoning/deepseek.py +2 -2
- agno/reasoning/default.py +3 -1
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +2 -2
- agno/reasoning/ollama.py +2 -2
- agno/reasoning/openai.py +7 -2
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +248 -94
- agno/run/base.py +44 -5
- agno/run/team.py +238 -97
- agno/run/workflow.py +144 -33
- agno/session/agent.py +105 -89
- agno/session/summary.py +65 -25
- agno/session/team.py +176 -96
- agno/session/workflow.py +406 -40
- agno/team/team.py +3854 -1610
- agno/tools/dalle.py +2 -4
- agno/tools/decorator.py +4 -2
- agno/tools/duckduckgo.py +15 -11
- agno/tools/e2b.py +14 -7
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +350 -0
- agno/tools/firecrawl.py +4 -4
- agno/tools/function.py +250 -30
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +270 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/knowledge.py +3 -3
- 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 +284 -0
- agno/tools/mem0.py +11 -17
- agno/tools/memori.py +1 -53
- agno/tools/memory.py +419 -0
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/scrapegraph.py +58 -31
- agno/tools/searxng.py +2 -2
- agno/tools/serper.py +2 -2
- agno/tools/slack.py +18 -3
- agno/tools/spider.py +2 -2
- agno/tools/tavily.py +146 -0
- agno/tools/whatsapp.py +1 -1
- agno/tools/workflow.py +278 -0
- agno/tools/yfinance.py +12 -11
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +27 -0
- agno/utils/common.py +90 -1
- agno/utils/events.py +217 -2
- agno/utils/gemini.py +180 -22
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +111 -0
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +188 -10
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +60 -0
- agno/utils/models/claude.py +40 -11
- agno/utils/print_response/agent.py +105 -21
- agno/utils/print_response/team.py +103 -38
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/reasoning.py +22 -1
- agno/utils/serialize.py +32 -0
- agno/utils/streamlit.py +16 -10
- agno/utils/string.py +41 -0
- agno/utils/team.py +98 -9
- agno/utils/tools.py +1 -1
- agno/vectordb/base.py +23 -4
- agno/vectordb/cassandra/cassandra.py +65 -9
- agno/vectordb/chroma/chromadb.py +182 -38
- agno/vectordb/clickhouse/clickhousedb.py +64 -11
- agno/vectordb/couchbase/couchbase.py +105 -10
- agno/vectordb/lancedb/lance_db.py +124 -133
- agno/vectordb/langchaindb/langchaindb.py +25 -7
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +46 -7
- agno/vectordb/milvus/milvus.py +126 -9
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +112 -7
- agno/vectordb/pgvector/pgvector.py +142 -21
- agno/vectordb/pineconedb/pineconedb.py +80 -8
- agno/vectordb/qdrant/qdrant.py +125 -39
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/singlestore/singlestore.py +111 -25
- agno/vectordb/surrealdb/surrealdb.py +31 -5
- agno/vectordb/upstashdb/upstashdb.py +76 -8
- agno/vectordb/weaviate/weaviate.py +86 -15
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +112 -18
- agno/workflow/loop.py +69 -10
- agno/workflow/parallel.py +266 -118
- agno/workflow/router.py +110 -17
- agno/workflow/step.py +638 -129
- agno/workflow/steps.py +65 -6
- agno/workflow/types.py +61 -23
- agno/workflow/workflow.py +2085 -272
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/METADATA +182 -58
- agno-2.3.0.dist-info/RECORD +577 -0
- agno/knowledge/reader/url_reader.py +0 -128
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -610
- agno/utils/models/aws_claude.py +0 -170
- agno-2.0.1.dist-info/RECORD +0 -515
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/licenses/LICENSE +0 -0
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
agno/db/mongo/mongo.py
CHANGED
|
@@ -10,15 +10,19 @@ from agno.db.mongo.utils import (
|
|
|
10
10
|
bulk_upsert_metrics,
|
|
11
11
|
calculate_date_metrics,
|
|
12
12
|
create_collection_indexes,
|
|
13
|
+
deserialize_cultural_knowledge_from_db,
|
|
13
14
|
fetch_all_sessions_data,
|
|
14
15
|
get_dates_to_calculate_metrics_for,
|
|
16
|
+
serialize_cultural_knowledge_for_db,
|
|
15
17
|
)
|
|
18
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
16
19
|
from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
|
|
17
20
|
from agno.db.schemas.knowledge import KnowledgeRow
|
|
18
21
|
from agno.db.schemas.memory import UserMemory
|
|
19
|
-
from agno.db.utils import deserialize_session_json_fields
|
|
22
|
+
from agno.db.utils import deserialize_session_json_fields
|
|
20
23
|
from agno.session import AgentSession, Session, TeamSession, WorkflowSession
|
|
21
24
|
from agno.utils.log import log_debug, log_error, log_info
|
|
25
|
+
from agno.utils.string import generate_id
|
|
22
26
|
|
|
23
27
|
try:
|
|
24
28
|
from pymongo import MongoClient, ReturnDocument
|
|
@@ -40,6 +44,8 @@ class MongoDb(BaseDb):
|
|
|
40
44
|
metrics_collection: Optional[str] = None,
|
|
41
45
|
eval_collection: Optional[str] = None,
|
|
42
46
|
knowledge_collection: Optional[str] = None,
|
|
47
|
+
culture_collection: Optional[str] = None,
|
|
48
|
+
id: Optional[str] = None,
|
|
43
49
|
):
|
|
44
50
|
"""
|
|
45
51
|
Interface for interacting with a MongoDB database.
|
|
@@ -53,16 +59,26 @@ class MongoDb(BaseDb):
|
|
|
53
59
|
metrics_collection (Optional[str]): Name of the collection to store metrics.
|
|
54
60
|
eval_collection (Optional[str]): Name of the collection to store evaluation runs.
|
|
55
61
|
knowledge_collection (Optional[str]): Name of the collection to store knowledge documents.
|
|
62
|
+
culture_collection (Optional[str]): Name of the collection to store cultural knowledge.
|
|
63
|
+
id (Optional[str]): ID of the database.
|
|
56
64
|
|
|
57
65
|
Raises:
|
|
58
66
|
ValueError: If neither db_url nor db_client is provided.
|
|
59
67
|
"""
|
|
68
|
+
if id is None:
|
|
69
|
+
base_seed = db_url or str(db_client)
|
|
70
|
+
db_name_suffix = db_name if db_name is not None else "agno"
|
|
71
|
+
seed = f"{base_seed}#{db_name_suffix}"
|
|
72
|
+
id = generate_id(seed)
|
|
73
|
+
|
|
60
74
|
super().__init__(
|
|
75
|
+
id=id,
|
|
61
76
|
session_table=session_collection,
|
|
62
77
|
memory_table=memory_collection,
|
|
63
78
|
metrics_table=metrics_collection,
|
|
64
79
|
eval_table=eval_collection,
|
|
65
80
|
knowledge_table=knowledge_collection,
|
|
81
|
+
culture_table=culture_collection,
|
|
66
82
|
)
|
|
67
83
|
|
|
68
84
|
_client: Optional[MongoClient] = db_client
|
|
@@ -84,6 +100,31 @@ class MongoDb(BaseDb):
|
|
|
84
100
|
return self._database
|
|
85
101
|
|
|
86
102
|
# -- DB methods --
|
|
103
|
+
def table_exists(self, table_name: str) -> bool:
|
|
104
|
+
"""Check if a collection with the given name exists in the MongoDB database.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
table_name: Name of the collection to check
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
bool: True if the collection exists in the database, False otherwise
|
|
111
|
+
"""
|
|
112
|
+
return table_name in self.database.list_collection_names()
|
|
113
|
+
|
|
114
|
+
def _create_all_tables(self):
|
|
115
|
+
"""Create all configured MongoDB collections if they don't exist."""
|
|
116
|
+
collections_to_create = [
|
|
117
|
+
("sessions", self.session_table_name),
|
|
118
|
+
("memories", self.memory_table_name),
|
|
119
|
+
("metrics", self.metrics_table_name),
|
|
120
|
+
("evals", self.eval_table_name),
|
|
121
|
+
("knowledge", self.knowledge_table_name),
|
|
122
|
+
("culture", self.culture_table_name),
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
for collection_type, collection_name in collections_to_create:
|
|
126
|
+
if collection_name and not self.table_exists(collection_name):
|
|
127
|
+
self._get_collection(collection_type, create_collection_if_not_found=True)
|
|
87
128
|
|
|
88
129
|
def _get_collection(
|
|
89
130
|
self, table_type: str, create_collection_if_not_found: Optional[bool] = True
|
|
@@ -151,6 +192,17 @@ class MongoDb(BaseDb):
|
|
|
151
192
|
)
|
|
152
193
|
return self.knowledge_collection
|
|
153
194
|
|
|
195
|
+
if table_type == "culture":
|
|
196
|
+
if not hasattr(self, "culture_collection"):
|
|
197
|
+
if self.culture_table_name is None:
|
|
198
|
+
raise ValueError("Culture collection was not provided on initialization")
|
|
199
|
+
self.culture_collection = self._get_or_create_collection(
|
|
200
|
+
collection_name=self.culture_table_name,
|
|
201
|
+
collection_type="culture",
|
|
202
|
+
create_collection_if_not_found=create_collection_if_not_found,
|
|
203
|
+
)
|
|
204
|
+
return self.culture_collection
|
|
205
|
+
|
|
154
206
|
raise ValueError(f"Unknown table type: {table_type}")
|
|
155
207
|
|
|
156
208
|
def _get_or_create_collection(
|
|
@@ -184,6 +236,14 @@ class MongoDb(BaseDb):
|
|
|
184
236
|
log_error(f"Error getting collection {collection_name}: {e}")
|
|
185
237
|
raise
|
|
186
238
|
|
|
239
|
+
def get_latest_schema_version(self):
|
|
240
|
+
"""Get the latest version of the database schema."""
|
|
241
|
+
pass
|
|
242
|
+
|
|
243
|
+
def upsert_schema_version(self, version: str) -> None:
|
|
244
|
+
"""Upsert the schema version into the database."""
|
|
245
|
+
pass
|
|
246
|
+
|
|
187
247
|
# -- Session methods --
|
|
188
248
|
|
|
189
249
|
def delete_session(self, session_id: str) -> bool:
|
|
@@ -213,7 +273,7 @@ class MongoDb(BaseDb):
|
|
|
213
273
|
|
|
214
274
|
except Exception as e:
|
|
215
275
|
log_error(f"Error deleting session: {e}")
|
|
216
|
-
|
|
276
|
+
raise e
|
|
217
277
|
|
|
218
278
|
def delete_sessions(self, session_ids: List[str]) -> None:
|
|
219
279
|
"""Delete multiple sessions from the database.
|
|
@@ -231,6 +291,7 @@ class MongoDb(BaseDb):
|
|
|
231
291
|
|
|
232
292
|
except Exception as e:
|
|
233
293
|
log_error(f"Error deleting sessions: {e}")
|
|
294
|
+
raise e
|
|
234
295
|
|
|
235
296
|
def get_session(
|
|
236
297
|
self,
|
|
@@ -243,8 +304,8 @@ class MongoDb(BaseDb):
|
|
|
243
304
|
|
|
244
305
|
Args:
|
|
245
306
|
session_id (str): The ID of the session to get.
|
|
307
|
+
session_type (SessionType): The type of session to get.
|
|
246
308
|
user_id (Optional[str]): The ID of the user to get the session for.
|
|
247
|
-
session_type (Optional[SessionType]): The type of session to get.
|
|
248
309
|
deserialize (Optional[bool]): Whether to serialize the session. Defaults to True.
|
|
249
310
|
|
|
250
311
|
Returns:
|
|
@@ -263,28 +324,27 @@ class MongoDb(BaseDb):
|
|
|
263
324
|
query = {"session_id": session_id}
|
|
264
325
|
if user_id is not None:
|
|
265
326
|
query["user_id"] = user_id
|
|
266
|
-
if session_type is not None:
|
|
267
|
-
query["session_type"] = session_type
|
|
268
327
|
|
|
269
328
|
result = collection.find_one(query)
|
|
270
329
|
if result is None:
|
|
271
330
|
return None
|
|
272
331
|
|
|
273
332
|
session = deserialize_session_json_fields(result)
|
|
274
|
-
|
|
275
333
|
if not deserialize:
|
|
276
334
|
return session
|
|
277
335
|
|
|
278
|
-
if session_type == SessionType.AGENT
|
|
336
|
+
if session_type == SessionType.AGENT:
|
|
279
337
|
return AgentSession.from_dict(session)
|
|
280
|
-
elif session_type == SessionType.TEAM
|
|
338
|
+
elif session_type == SessionType.TEAM:
|
|
281
339
|
return TeamSession.from_dict(session)
|
|
282
|
-
|
|
340
|
+
elif session_type == SessionType.WORKFLOW:
|
|
283
341
|
return WorkflowSession.from_dict(session)
|
|
342
|
+
else:
|
|
343
|
+
raise ValueError(f"Invalid session type: {session_type}")
|
|
284
344
|
|
|
285
345
|
except Exception as e:
|
|
286
346
|
log_error(f"Exception reading session: {e}")
|
|
287
|
-
|
|
347
|
+
raise e
|
|
288
348
|
|
|
289
349
|
def get_sessions(
|
|
290
350
|
self,
|
|
@@ -372,7 +432,6 @@ class MongoDb(BaseDb):
|
|
|
372
432
|
records = list(cursor)
|
|
373
433
|
if records is None:
|
|
374
434
|
return [] if deserialize else ([], 0)
|
|
375
|
-
|
|
376
435
|
sessions_raw = [deserialize_session_json_fields(record) for record in records]
|
|
377
436
|
|
|
378
437
|
if not deserialize:
|
|
@@ -397,7 +456,7 @@ class MongoDb(BaseDb):
|
|
|
397
456
|
|
|
398
457
|
except Exception as e:
|
|
399
458
|
log_error(f"Exception reading sessions: {e}")
|
|
400
|
-
|
|
459
|
+
raise e
|
|
401
460
|
|
|
402
461
|
def rename_session(
|
|
403
462
|
self, session_id: str, session_type: SessionType, session_name: str, deserialize: Optional[bool] = True
|
|
@@ -455,7 +514,7 @@ class MongoDb(BaseDb):
|
|
|
455
514
|
|
|
456
515
|
except Exception as e:
|
|
457
516
|
log_error(f"Exception renaming session: {e}")
|
|
458
|
-
|
|
517
|
+
raise e
|
|
459
518
|
|
|
460
519
|
def upsert_session(
|
|
461
520
|
self, session: Session, deserialize: Optional[bool] = True
|
|
@@ -476,25 +535,25 @@ class MongoDb(BaseDb):
|
|
|
476
535
|
if collection is None:
|
|
477
536
|
return None
|
|
478
537
|
|
|
479
|
-
|
|
538
|
+
session_dict = session.to_dict()
|
|
480
539
|
|
|
481
540
|
if isinstance(session, AgentSession):
|
|
482
541
|
record = {
|
|
483
|
-
"session_id":
|
|
542
|
+
"session_id": session_dict.get("session_id"),
|
|
484
543
|
"session_type": SessionType.AGENT.value,
|
|
485
|
-
"agent_id":
|
|
486
|
-
"user_id":
|
|
487
|
-
"runs":
|
|
488
|
-
"agent_data":
|
|
489
|
-
"session_data":
|
|
490
|
-
"summary":
|
|
491
|
-
"metadata":
|
|
492
|
-
"created_at":
|
|
544
|
+
"agent_id": session_dict.get("agent_id"),
|
|
545
|
+
"user_id": session_dict.get("user_id"),
|
|
546
|
+
"runs": session_dict.get("runs"),
|
|
547
|
+
"agent_data": session_dict.get("agent_data"),
|
|
548
|
+
"session_data": session_dict.get("session_data"),
|
|
549
|
+
"summary": session_dict.get("summary"),
|
|
550
|
+
"metadata": session_dict.get("metadata"),
|
|
551
|
+
"created_at": session_dict.get("created_at"),
|
|
493
552
|
"updated_at": int(time.time()),
|
|
494
553
|
}
|
|
495
554
|
|
|
496
555
|
result = collection.find_one_and_replace(
|
|
497
|
-
filter={"session_id":
|
|
556
|
+
filter={"session_id": session_dict.get("session_id")},
|
|
498
557
|
replacement=record,
|
|
499
558
|
upsert=True,
|
|
500
559
|
return_document=ReturnDocument.AFTER,
|
|
@@ -502,7 +561,7 @@ class MongoDb(BaseDb):
|
|
|
502
561
|
if not result:
|
|
503
562
|
return None
|
|
504
563
|
|
|
505
|
-
session =
|
|
564
|
+
session = result # type: ignore
|
|
506
565
|
|
|
507
566
|
if not deserialize:
|
|
508
567
|
return session
|
|
@@ -511,21 +570,21 @@ class MongoDb(BaseDb):
|
|
|
511
570
|
|
|
512
571
|
elif isinstance(session, TeamSession):
|
|
513
572
|
record = {
|
|
514
|
-
"session_id":
|
|
573
|
+
"session_id": session_dict.get("session_id"),
|
|
515
574
|
"session_type": SessionType.TEAM.value,
|
|
516
|
-
"team_id":
|
|
517
|
-
"user_id":
|
|
518
|
-
"runs":
|
|
519
|
-
"team_data":
|
|
520
|
-
"session_data":
|
|
521
|
-
"summary":
|
|
522
|
-
"metadata":
|
|
523
|
-
"created_at":
|
|
575
|
+
"team_id": session_dict.get("team_id"),
|
|
576
|
+
"user_id": session_dict.get("user_id"),
|
|
577
|
+
"runs": session_dict.get("runs"),
|
|
578
|
+
"team_data": session_dict.get("team_data"),
|
|
579
|
+
"session_data": session_dict.get("session_data"),
|
|
580
|
+
"summary": session_dict.get("summary"),
|
|
581
|
+
"metadata": session_dict.get("metadata"),
|
|
582
|
+
"created_at": session_dict.get("created_at"),
|
|
524
583
|
"updated_at": int(time.time()),
|
|
525
584
|
}
|
|
526
585
|
|
|
527
586
|
result = collection.find_one_and_replace(
|
|
528
|
-
filter={"session_id":
|
|
587
|
+
filter={"session_id": session_dict.get("session_id")},
|
|
529
588
|
replacement=record,
|
|
530
589
|
upsert=True,
|
|
531
590
|
return_document=ReturnDocument.AFTER,
|
|
@@ -533,7 +592,8 @@ class MongoDb(BaseDb):
|
|
|
533
592
|
if not result:
|
|
534
593
|
return None
|
|
535
594
|
|
|
536
|
-
|
|
595
|
+
# MongoDB stores native objects, no deserialization needed for document fields
|
|
596
|
+
session = result # type: ignore
|
|
537
597
|
|
|
538
598
|
if not deserialize:
|
|
539
599
|
return session
|
|
@@ -542,21 +602,21 @@ class MongoDb(BaseDb):
|
|
|
542
602
|
|
|
543
603
|
else:
|
|
544
604
|
record = {
|
|
545
|
-
"session_id":
|
|
605
|
+
"session_id": session_dict.get("session_id"),
|
|
546
606
|
"session_type": SessionType.WORKFLOW.value,
|
|
547
|
-
"workflow_id":
|
|
548
|
-
"user_id":
|
|
549
|
-
"runs":
|
|
550
|
-
"workflow_data":
|
|
551
|
-
"session_data":
|
|
552
|
-
"summary":
|
|
553
|
-
"metadata":
|
|
554
|
-
"created_at":
|
|
607
|
+
"workflow_id": session_dict.get("workflow_id"),
|
|
608
|
+
"user_id": session_dict.get("user_id"),
|
|
609
|
+
"runs": session_dict.get("runs"),
|
|
610
|
+
"workflow_data": session_dict.get("workflow_data"),
|
|
611
|
+
"session_data": session_dict.get("session_data"),
|
|
612
|
+
"summary": session_dict.get("summary"),
|
|
613
|
+
"metadata": session_dict.get("metadata"),
|
|
614
|
+
"created_at": session_dict.get("created_at"),
|
|
555
615
|
"updated_at": int(time.time()),
|
|
556
616
|
}
|
|
557
617
|
|
|
558
618
|
result = collection.find_one_and_replace(
|
|
559
|
-
filter={"session_id":
|
|
619
|
+
filter={"session_id": session_dict.get("session_id")},
|
|
560
620
|
replacement=record,
|
|
561
621
|
upsert=True,
|
|
562
622
|
return_document=ReturnDocument.AFTER,
|
|
@@ -564,7 +624,7 @@ class MongoDb(BaseDb):
|
|
|
564
624
|
if not result:
|
|
565
625
|
return None
|
|
566
626
|
|
|
567
|
-
session =
|
|
627
|
+
session = result # type: ignore
|
|
568
628
|
|
|
569
629
|
if not deserialize:
|
|
570
630
|
return session
|
|
@@ -573,15 +633,158 @@ class MongoDb(BaseDb):
|
|
|
573
633
|
|
|
574
634
|
except Exception as e:
|
|
575
635
|
log_error(f"Exception upserting session: {e}")
|
|
576
|
-
|
|
636
|
+
raise e
|
|
637
|
+
|
|
638
|
+
def upsert_sessions(
|
|
639
|
+
self, sessions: List[Session], deserialize: Optional[bool] = True, preserve_updated_at: bool = False
|
|
640
|
+
) -> List[Union[Session, Dict[str, Any]]]:
|
|
641
|
+
"""
|
|
642
|
+
Bulk upsert multiple sessions for improved performance on large datasets.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
sessions (List[Session]): List of sessions to upsert.
|
|
646
|
+
deserialize (Optional[bool]): Whether to deserialize the sessions. Defaults to True.
|
|
647
|
+
preserve_updated_at (bool): If True, preserve the updated_at from the session object.
|
|
648
|
+
|
|
649
|
+
Returns:
|
|
650
|
+
List[Union[Session, Dict[str, Any]]]: List of upserted sessions.
|
|
651
|
+
|
|
652
|
+
Raises:
|
|
653
|
+
Exception: If an error occurs during bulk upsert.
|
|
654
|
+
"""
|
|
655
|
+
if not sessions:
|
|
656
|
+
return []
|
|
657
|
+
|
|
658
|
+
try:
|
|
659
|
+
collection = self._get_collection(table_type="sessions", create_collection_if_not_found=True)
|
|
660
|
+
if collection is None:
|
|
661
|
+
log_info("Sessions collection not available, falling back to individual upserts")
|
|
662
|
+
return [
|
|
663
|
+
result
|
|
664
|
+
for session in sessions
|
|
665
|
+
if session is not None
|
|
666
|
+
for result in [self.upsert_session(session, deserialize=deserialize)]
|
|
667
|
+
if result is not None
|
|
668
|
+
]
|
|
669
|
+
|
|
670
|
+
from pymongo import ReplaceOne
|
|
671
|
+
|
|
672
|
+
operations = []
|
|
673
|
+
results: List[Union[Session, Dict[str, Any]]] = []
|
|
674
|
+
|
|
675
|
+
for session in sessions:
|
|
676
|
+
if session is None:
|
|
677
|
+
continue
|
|
678
|
+
|
|
679
|
+
session_dict = session.to_dict()
|
|
680
|
+
|
|
681
|
+
# Use preserved updated_at if flag is set and value exists, otherwise use current time
|
|
682
|
+
updated_at = session_dict.get("updated_at") if preserve_updated_at else int(time.time())
|
|
683
|
+
|
|
684
|
+
if isinstance(session, AgentSession):
|
|
685
|
+
record = {
|
|
686
|
+
"session_id": session_dict.get("session_id"),
|
|
687
|
+
"session_type": SessionType.AGENT.value,
|
|
688
|
+
"agent_id": session_dict.get("agent_id"),
|
|
689
|
+
"user_id": session_dict.get("user_id"),
|
|
690
|
+
"runs": session_dict.get("runs"),
|
|
691
|
+
"agent_data": session_dict.get("agent_data"),
|
|
692
|
+
"session_data": session_dict.get("session_data"),
|
|
693
|
+
"summary": session_dict.get("summary"),
|
|
694
|
+
"metadata": session_dict.get("metadata"),
|
|
695
|
+
"created_at": session_dict.get("created_at"),
|
|
696
|
+
"updated_at": updated_at,
|
|
697
|
+
}
|
|
698
|
+
elif isinstance(session, TeamSession):
|
|
699
|
+
record = {
|
|
700
|
+
"session_id": session_dict.get("session_id"),
|
|
701
|
+
"session_type": SessionType.TEAM.value,
|
|
702
|
+
"team_id": session_dict.get("team_id"),
|
|
703
|
+
"user_id": session_dict.get("user_id"),
|
|
704
|
+
"runs": session_dict.get("runs"),
|
|
705
|
+
"team_data": session_dict.get("team_data"),
|
|
706
|
+
"session_data": session_dict.get("session_data"),
|
|
707
|
+
"summary": session_dict.get("summary"),
|
|
708
|
+
"metadata": session_dict.get("metadata"),
|
|
709
|
+
"created_at": session_dict.get("created_at"),
|
|
710
|
+
"updated_at": updated_at,
|
|
711
|
+
}
|
|
712
|
+
elif isinstance(session, WorkflowSession):
|
|
713
|
+
record = {
|
|
714
|
+
"session_id": session_dict.get("session_id"),
|
|
715
|
+
"session_type": SessionType.WORKFLOW.value,
|
|
716
|
+
"workflow_id": session_dict.get("workflow_id"),
|
|
717
|
+
"user_id": session_dict.get("user_id"),
|
|
718
|
+
"runs": session_dict.get("runs"),
|
|
719
|
+
"workflow_data": session_dict.get("workflow_data"),
|
|
720
|
+
"session_data": session_dict.get("session_data"),
|
|
721
|
+
"summary": session_dict.get("summary"),
|
|
722
|
+
"metadata": session_dict.get("metadata"),
|
|
723
|
+
"created_at": session_dict.get("created_at"),
|
|
724
|
+
"updated_at": updated_at,
|
|
725
|
+
}
|
|
726
|
+
else:
|
|
727
|
+
continue
|
|
728
|
+
|
|
729
|
+
operations.append(
|
|
730
|
+
ReplaceOne(filter={"session_id": record["session_id"]}, replacement=record, upsert=True)
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
if operations:
|
|
734
|
+
# Execute bulk write
|
|
735
|
+
collection.bulk_write(operations)
|
|
736
|
+
|
|
737
|
+
# Fetch the results
|
|
738
|
+
session_ids = [session.session_id for session in sessions if session and session.session_id]
|
|
739
|
+
cursor = collection.find({"session_id": {"$in": session_ids}})
|
|
740
|
+
|
|
741
|
+
for doc in cursor:
|
|
742
|
+
session_dict = doc
|
|
743
|
+
|
|
744
|
+
if deserialize:
|
|
745
|
+
session_type = doc.get("session_type")
|
|
746
|
+
if session_type == SessionType.AGENT.value:
|
|
747
|
+
deserialized_agent_session = AgentSession.from_dict(session_dict)
|
|
748
|
+
if deserialized_agent_session is None:
|
|
749
|
+
continue
|
|
750
|
+
results.append(deserialized_agent_session)
|
|
751
|
+
|
|
752
|
+
elif session_type == SessionType.TEAM.value:
|
|
753
|
+
deserialized_team_session = TeamSession.from_dict(session_dict)
|
|
754
|
+
if deserialized_team_session is None:
|
|
755
|
+
continue
|
|
756
|
+
results.append(deserialized_team_session)
|
|
757
|
+
|
|
758
|
+
elif session_type == SessionType.WORKFLOW.value:
|
|
759
|
+
deserialized_workflow_session = WorkflowSession.from_dict(session_dict)
|
|
760
|
+
if deserialized_workflow_session is None:
|
|
761
|
+
continue
|
|
762
|
+
results.append(deserialized_workflow_session)
|
|
763
|
+
else:
|
|
764
|
+
results.append(session_dict)
|
|
765
|
+
|
|
766
|
+
return results
|
|
767
|
+
|
|
768
|
+
except Exception as e:
|
|
769
|
+
log_error(f"Exception during bulk session upsert, falling back to individual upserts: {e}")
|
|
770
|
+
|
|
771
|
+
# Fallback to individual upserts
|
|
772
|
+
return [
|
|
773
|
+
result
|
|
774
|
+
for session in sessions
|
|
775
|
+
if session is not None
|
|
776
|
+
for result in [self.upsert_session(session, deserialize=deserialize)]
|
|
777
|
+
if result is not None
|
|
778
|
+
]
|
|
577
779
|
|
|
578
780
|
# -- Memory methods --
|
|
579
781
|
|
|
580
|
-
def delete_user_memory(self, memory_id: str):
|
|
782
|
+
def delete_user_memory(self, memory_id: str, user_id: Optional[str] = None):
|
|
581
783
|
"""Delete a user memory from the database.
|
|
582
784
|
|
|
583
785
|
Args:
|
|
584
786
|
memory_id (str): The ID of the memory to delete.
|
|
787
|
+
user_id (Optional[str]): The ID of the user to verify ownership. If provided, only delete if the memory belongs to this user.
|
|
585
788
|
|
|
586
789
|
Returns:
|
|
587
790
|
bool: True if the memory was deleted, False otherwise.
|
|
@@ -594,7 +797,11 @@ class MongoDb(BaseDb):
|
|
|
594
797
|
if collection is None:
|
|
595
798
|
return
|
|
596
799
|
|
|
597
|
-
|
|
800
|
+
query = {"memory_id": memory_id}
|
|
801
|
+
if user_id is not None:
|
|
802
|
+
query["user_id"] = user_id
|
|
803
|
+
|
|
804
|
+
result = collection.delete_one(query)
|
|
598
805
|
|
|
599
806
|
success = result.deleted_count > 0
|
|
600
807
|
if success:
|
|
@@ -604,12 +811,14 @@ class MongoDb(BaseDb):
|
|
|
604
811
|
|
|
605
812
|
except Exception as e:
|
|
606
813
|
log_error(f"Error deleting memory: {e}")
|
|
814
|
+
raise e
|
|
607
815
|
|
|
608
|
-
def delete_user_memories(self, memory_ids: List[str]) -> None:
|
|
816
|
+
def delete_user_memories(self, memory_ids: List[str], user_id: Optional[str] = None) -> None:
|
|
609
817
|
"""Delete user memories from the database.
|
|
610
818
|
|
|
611
819
|
Args:
|
|
612
820
|
memory_ids (List[str]): The IDs of the memories to delete.
|
|
821
|
+
user_id (Optional[str]): The ID of the user to verify ownership. If provided, only delete memories that belong to this user.
|
|
613
822
|
|
|
614
823
|
Raises:
|
|
615
824
|
Exception: If there is an error deleting the memories.
|
|
@@ -619,13 +828,18 @@ class MongoDb(BaseDb):
|
|
|
619
828
|
if collection is None:
|
|
620
829
|
return
|
|
621
830
|
|
|
622
|
-
|
|
831
|
+
query: Dict[str, Any] = {"memory_id": {"$in": memory_ids}}
|
|
832
|
+
if user_id is not None:
|
|
833
|
+
query["user_id"] = user_id
|
|
834
|
+
|
|
835
|
+
result = collection.delete_many(query)
|
|
623
836
|
|
|
624
837
|
if result.deleted_count == 0:
|
|
625
838
|
log_debug(f"No memories found with ids: {memory_ids}")
|
|
626
839
|
|
|
627
840
|
except Exception as e:
|
|
628
841
|
log_error(f"Error deleting memories: {e}")
|
|
842
|
+
raise e
|
|
629
843
|
|
|
630
844
|
def get_all_memory_topics(self) -> List[str]:
|
|
631
845
|
"""Get all memory topics from the database.
|
|
@@ -641,19 +855,22 @@ class MongoDb(BaseDb):
|
|
|
641
855
|
if collection is None:
|
|
642
856
|
return []
|
|
643
857
|
|
|
644
|
-
topics = collection.distinct("topics")
|
|
858
|
+
topics = collection.distinct("topics", {})
|
|
645
859
|
return [topic for topic in topics if topic]
|
|
646
860
|
|
|
647
861
|
except Exception as e:
|
|
648
862
|
log_error(f"Exception reading from collection: {e}")
|
|
649
|
-
|
|
863
|
+
raise e
|
|
650
864
|
|
|
651
|
-
def get_user_memory(
|
|
865
|
+
def get_user_memory(
|
|
866
|
+
self, memory_id: str, deserialize: Optional[bool] = True, user_id: Optional[str] = None
|
|
867
|
+
) -> Optional[UserMemory]:
|
|
652
868
|
"""Get a memory from the database.
|
|
653
869
|
|
|
654
870
|
Args:
|
|
655
871
|
memory_id (str): The ID of the memory to get.
|
|
656
872
|
deserialize (Optional[bool]): Whether to serialize the memory. Defaults to True.
|
|
873
|
+
user_id (Optional[str]): The ID of the user to verify ownership. If provided, only return the memory if it belongs to this user.
|
|
657
874
|
|
|
658
875
|
Returns:
|
|
659
876
|
Optional[UserMemory]:
|
|
@@ -668,7 +885,11 @@ class MongoDb(BaseDb):
|
|
|
668
885
|
if collection is None:
|
|
669
886
|
return None
|
|
670
887
|
|
|
671
|
-
|
|
888
|
+
query = {"memory_id": memory_id}
|
|
889
|
+
if user_id is not None:
|
|
890
|
+
query["user_id"] = user_id
|
|
891
|
+
|
|
892
|
+
result = collection.find_one(query)
|
|
672
893
|
if result is None or not deserialize:
|
|
673
894
|
return result
|
|
674
895
|
|
|
@@ -678,7 +899,7 @@ class MongoDb(BaseDb):
|
|
|
678
899
|
|
|
679
900
|
except Exception as e:
|
|
680
901
|
log_error(f"Exception reading from collection: {e}")
|
|
681
|
-
|
|
902
|
+
raise e
|
|
682
903
|
|
|
683
904
|
def get_user_memories(
|
|
684
905
|
self,
|
|
@@ -757,7 +978,7 @@ class MongoDb(BaseDb):
|
|
|
757
978
|
|
|
758
979
|
except Exception as e:
|
|
759
980
|
log_error(f"Exception reading from collection: {e}")
|
|
760
|
-
|
|
981
|
+
raise e
|
|
761
982
|
|
|
762
983
|
def get_user_memory_stats(
|
|
763
984
|
self,
|
|
@@ -781,8 +1002,10 @@ class MongoDb(BaseDb):
|
|
|
781
1002
|
if collection is None:
|
|
782
1003
|
return [], 0
|
|
783
1004
|
|
|
1005
|
+
match_stage = {"user_id": {"$ne": None}}
|
|
1006
|
+
|
|
784
1007
|
pipeline = [
|
|
785
|
-
{"$match":
|
|
1008
|
+
{"$match": match_stage},
|
|
786
1009
|
{
|
|
787
1010
|
"$group": {
|
|
788
1011
|
"_id": "$user_id",
|
|
@@ -819,7 +1042,7 @@ class MongoDb(BaseDb):
|
|
|
819
1042
|
|
|
820
1043
|
except Exception as e:
|
|
821
1044
|
log_error(f"Exception getting user memory stats: {e}")
|
|
822
|
-
|
|
1045
|
+
raise e
|
|
823
1046
|
|
|
824
1047
|
def upsert_user_memory(
|
|
825
1048
|
self, memory: UserMemory, deserialize: Optional[bool] = True
|
|
@@ -870,7 +1093,99 @@ class MongoDb(BaseDb):
|
|
|
870
1093
|
|
|
871
1094
|
except Exception as e:
|
|
872
1095
|
log_error(f"Exception upserting user memory: {e}")
|
|
873
|
-
|
|
1096
|
+
raise e
|
|
1097
|
+
|
|
1098
|
+
def upsert_memories(
|
|
1099
|
+
self, memories: List[UserMemory], deserialize: Optional[bool] = True, preserve_updated_at: bool = False
|
|
1100
|
+
) -> List[Union[UserMemory, Dict[str, Any]]]:
|
|
1101
|
+
"""
|
|
1102
|
+
Bulk upsert multiple user memories for improved performance on large datasets.
|
|
1103
|
+
|
|
1104
|
+
Args:
|
|
1105
|
+
memories (List[UserMemory]): List of memories to upsert.
|
|
1106
|
+
deserialize (Optional[bool]): Whether to deserialize the memories. Defaults to True.
|
|
1107
|
+
|
|
1108
|
+
Returns:
|
|
1109
|
+
List[Union[UserMemory, Dict[str, Any]]]: List of upserted memories.
|
|
1110
|
+
|
|
1111
|
+
Raises:
|
|
1112
|
+
Exception: If an error occurs during bulk upsert.
|
|
1113
|
+
"""
|
|
1114
|
+
if not memories:
|
|
1115
|
+
return []
|
|
1116
|
+
|
|
1117
|
+
try:
|
|
1118
|
+
collection = self._get_collection(table_type="memories", create_collection_if_not_found=True)
|
|
1119
|
+
if collection is None:
|
|
1120
|
+
log_info("Memories collection not available, falling back to individual upserts")
|
|
1121
|
+
return [
|
|
1122
|
+
result
|
|
1123
|
+
for memory in memories
|
|
1124
|
+
if memory is not None
|
|
1125
|
+
for result in [self.upsert_user_memory(memory, deserialize=deserialize)]
|
|
1126
|
+
if result is not None
|
|
1127
|
+
]
|
|
1128
|
+
|
|
1129
|
+
from pymongo import ReplaceOne
|
|
1130
|
+
|
|
1131
|
+
operations = []
|
|
1132
|
+
results: List[Union[UserMemory, Dict[str, Any]]] = []
|
|
1133
|
+
|
|
1134
|
+
current_time = int(time.time())
|
|
1135
|
+
for memory in memories:
|
|
1136
|
+
if memory is None:
|
|
1137
|
+
continue
|
|
1138
|
+
|
|
1139
|
+
if memory.memory_id is None:
|
|
1140
|
+
memory.memory_id = str(uuid4())
|
|
1141
|
+
|
|
1142
|
+
# Use preserved updated_at if flag is set and value exists, otherwise use current time
|
|
1143
|
+
updated_at = memory.updated_at if preserve_updated_at else current_time
|
|
1144
|
+
|
|
1145
|
+
record = {
|
|
1146
|
+
"user_id": memory.user_id,
|
|
1147
|
+
"agent_id": memory.agent_id,
|
|
1148
|
+
"team_id": memory.team_id,
|
|
1149
|
+
"memory_id": memory.memory_id,
|
|
1150
|
+
"memory": memory.memory,
|
|
1151
|
+
"input": memory.input,
|
|
1152
|
+
"feedback": memory.feedback,
|
|
1153
|
+
"topics": memory.topics,
|
|
1154
|
+
"created_at": memory.created_at,
|
|
1155
|
+
"updated_at": updated_at,
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
operations.append(ReplaceOne(filter={"memory_id": memory.memory_id}, replacement=record, upsert=True))
|
|
1159
|
+
|
|
1160
|
+
if operations:
|
|
1161
|
+
# Execute bulk write
|
|
1162
|
+
collection.bulk_write(operations)
|
|
1163
|
+
|
|
1164
|
+
# Fetch the results
|
|
1165
|
+
memory_ids = [memory.memory_id for memory in memories if memory and memory.memory_id]
|
|
1166
|
+
cursor = collection.find({"memory_id": {"$in": memory_ids}})
|
|
1167
|
+
|
|
1168
|
+
for doc in cursor:
|
|
1169
|
+
if deserialize:
|
|
1170
|
+
# Remove MongoDB's _id field before creating UserMemory object
|
|
1171
|
+
doc_filtered = {k: v for k, v in doc.items() if k != "_id"}
|
|
1172
|
+
results.append(UserMemory.from_dict(doc_filtered))
|
|
1173
|
+
else:
|
|
1174
|
+
results.append(doc)
|
|
1175
|
+
|
|
1176
|
+
return results
|
|
1177
|
+
|
|
1178
|
+
except Exception as e:
|
|
1179
|
+
log_error(f"Exception during bulk memory upsert, falling back to individual upserts: {e}")
|
|
1180
|
+
|
|
1181
|
+
# Fallback to individual upserts
|
|
1182
|
+
return [
|
|
1183
|
+
result
|
|
1184
|
+
for memory in memories
|
|
1185
|
+
if memory is not None
|
|
1186
|
+
for result in [self.upsert_user_memory(memory, deserialize=deserialize)]
|
|
1187
|
+
if result is not None
|
|
1188
|
+
]
|
|
874
1189
|
|
|
875
1190
|
def clear_memories(self) -> None:
|
|
876
1191
|
"""Delete all memories from the database.
|
|
@@ -886,9 +1201,213 @@ class MongoDb(BaseDb):
|
|
|
886
1201
|
collection.delete_many({})
|
|
887
1202
|
|
|
888
1203
|
except Exception as e:
|
|
889
|
-
|
|
1204
|
+
log_error(f"Exception deleting all memories: {e}")
|
|
1205
|
+
raise e
|
|
1206
|
+
|
|
1207
|
+
# -- Cultural Knowledge methods --
|
|
1208
|
+
def clear_cultural_knowledge(self) -> None:
|
|
1209
|
+
"""Delete all cultural knowledge from the database.
|
|
1210
|
+
|
|
1211
|
+
Raises:
|
|
1212
|
+
Exception: If an error occurs during deletion.
|
|
1213
|
+
"""
|
|
1214
|
+
try:
|
|
1215
|
+
collection = self._get_collection(table_type="culture")
|
|
1216
|
+
if collection is None:
|
|
1217
|
+
return
|
|
1218
|
+
|
|
1219
|
+
collection.delete_many({})
|
|
1220
|
+
|
|
1221
|
+
except Exception as e:
|
|
1222
|
+
log_error(f"Exception deleting all cultural knowledge: {e}")
|
|
1223
|
+
raise e
|
|
1224
|
+
|
|
1225
|
+
def delete_cultural_knowledge(self, id: str) -> None:
|
|
1226
|
+
"""Delete cultural knowledge by ID.
|
|
1227
|
+
|
|
1228
|
+
Args:
|
|
1229
|
+
id (str): The ID of the cultural knowledge to delete.
|
|
1230
|
+
|
|
1231
|
+
Raises:
|
|
1232
|
+
Exception: If an error occurs during deletion.
|
|
1233
|
+
"""
|
|
1234
|
+
try:
|
|
1235
|
+
collection = self._get_collection(table_type="culture")
|
|
1236
|
+
if collection is None:
|
|
1237
|
+
return
|
|
1238
|
+
|
|
1239
|
+
collection.delete_one({"id": id})
|
|
1240
|
+
log_debug(f"Deleted cultural knowledge with ID: {id}")
|
|
1241
|
+
|
|
1242
|
+
except Exception as e:
|
|
1243
|
+
log_error(f"Error deleting cultural knowledge: {e}")
|
|
1244
|
+
raise e
|
|
1245
|
+
|
|
1246
|
+
def get_cultural_knowledge(
|
|
1247
|
+
self, id: str, deserialize: Optional[bool] = True
|
|
1248
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1249
|
+
"""Get cultural knowledge by ID.
|
|
1250
|
+
|
|
1251
|
+
Args:
|
|
1252
|
+
id (str): The ID of the cultural knowledge to retrieve.
|
|
1253
|
+
deserialize (Optional[bool]): Whether to deserialize to CulturalKnowledge object. Defaults to True.
|
|
1254
|
+
|
|
1255
|
+
Returns:
|
|
1256
|
+
Optional[Union[CulturalKnowledge, Dict[str, Any]]]: The cultural knowledge if found, None otherwise.
|
|
1257
|
+
|
|
1258
|
+
Raises:
|
|
1259
|
+
Exception: If an error occurs during retrieval.
|
|
1260
|
+
"""
|
|
1261
|
+
try:
|
|
1262
|
+
collection = self._get_collection(table_type="culture")
|
|
1263
|
+
if collection is None:
|
|
1264
|
+
return None
|
|
1265
|
+
|
|
1266
|
+
result = collection.find_one({"id": id})
|
|
1267
|
+
if result is None:
|
|
1268
|
+
return None
|
|
1269
|
+
|
|
1270
|
+
# Remove MongoDB's _id field
|
|
1271
|
+
result_filtered = {k: v for k, v in result.items() if k != "_id"}
|
|
1272
|
+
|
|
1273
|
+
if not deserialize:
|
|
1274
|
+
return result_filtered
|
|
1275
|
+
|
|
1276
|
+
return deserialize_cultural_knowledge_from_db(result_filtered)
|
|
1277
|
+
|
|
1278
|
+
except Exception as e:
|
|
1279
|
+
log_error(f"Error getting cultural knowledge: {e}")
|
|
1280
|
+
raise e
|
|
1281
|
+
|
|
1282
|
+
def get_all_cultural_knowledge(
|
|
1283
|
+
self,
|
|
1284
|
+
agent_id: Optional[str] = None,
|
|
1285
|
+
team_id: Optional[str] = None,
|
|
1286
|
+
name: Optional[str] = None,
|
|
1287
|
+
limit: Optional[int] = None,
|
|
1288
|
+
page: Optional[int] = None,
|
|
1289
|
+
sort_by: Optional[str] = None,
|
|
1290
|
+
sort_order: Optional[str] = None,
|
|
1291
|
+
deserialize: Optional[bool] = True,
|
|
1292
|
+
) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
1293
|
+
"""Get all cultural knowledge with filtering and pagination.
|
|
1294
|
+
|
|
1295
|
+
Args:
|
|
1296
|
+
agent_id (Optional[str]): Filter by agent ID.
|
|
1297
|
+
team_id (Optional[str]): Filter by team ID.
|
|
1298
|
+
name (Optional[str]): Filter by name (case-insensitive partial match).
|
|
1299
|
+
limit (Optional[int]): Maximum number of results to return.
|
|
1300
|
+
page (Optional[int]): Page number for pagination.
|
|
1301
|
+
sort_by (Optional[str]): Field to sort by.
|
|
1302
|
+
sort_order (Optional[str]): Sort order ('asc' or 'desc').
|
|
1303
|
+
deserialize (Optional[bool]): Whether to deserialize to CulturalKnowledge objects. Defaults to True.
|
|
1304
|
+
|
|
1305
|
+
Returns:
|
|
1306
|
+
Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
1307
|
+
- When deserialize=True: List of CulturalKnowledge objects
|
|
1308
|
+
- When deserialize=False: Tuple with list of dictionaries and total count
|
|
1309
|
+
|
|
1310
|
+
Raises:
|
|
1311
|
+
Exception: If an error occurs during retrieval.
|
|
1312
|
+
"""
|
|
1313
|
+
try:
|
|
1314
|
+
collection = self._get_collection(table_type="culture")
|
|
1315
|
+
if collection is None:
|
|
1316
|
+
if not deserialize:
|
|
1317
|
+
return [], 0
|
|
1318
|
+
return []
|
|
890
1319
|
|
|
891
|
-
|
|
1320
|
+
# Build query
|
|
1321
|
+
query: Dict[str, Any] = {}
|
|
1322
|
+
if agent_id is not None:
|
|
1323
|
+
query["agent_id"] = agent_id
|
|
1324
|
+
if team_id is not None:
|
|
1325
|
+
query["team_id"] = team_id
|
|
1326
|
+
if name is not None:
|
|
1327
|
+
query["name"] = {"$regex": name, "$options": "i"}
|
|
1328
|
+
|
|
1329
|
+
# Get total count for pagination
|
|
1330
|
+
total_count = collection.count_documents(query)
|
|
1331
|
+
|
|
1332
|
+
# Apply sorting
|
|
1333
|
+
sort_criteria = apply_sorting({}, sort_by, sort_order)
|
|
1334
|
+
|
|
1335
|
+
# Apply pagination
|
|
1336
|
+
query_args = apply_pagination({}, limit, page)
|
|
1337
|
+
|
|
1338
|
+
cursor = collection.find(query)
|
|
1339
|
+
if sort_criteria:
|
|
1340
|
+
cursor = cursor.sort(sort_criteria)
|
|
1341
|
+
if query_args.get("skip"):
|
|
1342
|
+
cursor = cursor.skip(query_args["skip"])
|
|
1343
|
+
if query_args.get("limit"):
|
|
1344
|
+
cursor = cursor.limit(query_args["limit"])
|
|
1345
|
+
|
|
1346
|
+
# Remove MongoDB's _id field from all results
|
|
1347
|
+
results_filtered = [{k: v for k, v in item.items() if k != "_id"} for item in cursor]
|
|
1348
|
+
|
|
1349
|
+
if not deserialize:
|
|
1350
|
+
return results_filtered, total_count
|
|
1351
|
+
|
|
1352
|
+
return [deserialize_cultural_knowledge_from_db(item) for item in results_filtered]
|
|
1353
|
+
|
|
1354
|
+
except Exception as e:
|
|
1355
|
+
log_error(f"Error getting all cultural knowledge: {e}")
|
|
1356
|
+
raise e
|
|
1357
|
+
|
|
1358
|
+
def upsert_cultural_knowledge(
|
|
1359
|
+
self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
|
|
1360
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1361
|
+
"""Upsert cultural knowledge in MongoDB.
|
|
1362
|
+
|
|
1363
|
+
Args:
|
|
1364
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge to upsert.
|
|
1365
|
+
deserialize (Optional[bool]): Whether to deserialize the result. Defaults to True.
|
|
1366
|
+
|
|
1367
|
+
Returns:
|
|
1368
|
+
Optional[Union[CulturalKnowledge, Dict[str, Any]]]: The upserted cultural knowledge.
|
|
1369
|
+
|
|
1370
|
+
Raises:
|
|
1371
|
+
Exception: If an error occurs during upsert.
|
|
1372
|
+
"""
|
|
1373
|
+
try:
|
|
1374
|
+
collection = self._get_collection(table_type="culture", create_collection_if_not_found=True)
|
|
1375
|
+
if collection is None:
|
|
1376
|
+
return None
|
|
1377
|
+
|
|
1378
|
+
# Serialize content, categories, and notes into a dict for DB storage
|
|
1379
|
+
content_dict = serialize_cultural_knowledge_for_db(cultural_knowledge)
|
|
1380
|
+
|
|
1381
|
+
# Create the document with serialized content
|
|
1382
|
+
update_doc = {
|
|
1383
|
+
"id": cultural_knowledge.id,
|
|
1384
|
+
"name": cultural_knowledge.name,
|
|
1385
|
+
"summary": cultural_knowledge.summary,
|
|
1386
|
+
"content": content_dict if content_dict else None,
|
|
1387
|
+
"metadata": cultural_knowledge.metadata,
|
|
1388
|
+
"input": cultural_knowledge.input,
|
|
1389
|
+
"created_at": cultural_knowledge.created_at,
|
|
1390
|
+
"updated_at": int(time.time()),
|
|
1391
|
+
"agent_id": cultural_knowledge.agent_id,
|
|
1392
|
+
"team_id": cultural_knowledge.team_id,
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
result = collection.replace_one({"id": cultural_knowledge.id}, update_doc, upsert=True)
|
|
1396
|
+
|
|
1397
|
+
if result.upserted_id:
|
|
1398
|
+
update_doc["_id"] = result.upserted_id
|
|
1399
|
+
|
|
1400
|
+
# Remove MongoDB's _id field
|
|
1401
|
+
doc_filtered = {k: v for k, v in update_doc.items() if k != "_id"}
|
|
1402
|
+
|
|
1403
|
+
if not deserialize:
|
|
1404
|
+
return doc_filtered
|
|
1405
|
+
|
|
1406
|
+
return deserialize_cultural_knowledge_from_db(doc_filtered)
|
|
1407
|
+
|
|
1408
|
+
except Exception as e:
|
|
1409
|
+
log_error(f"Error upserting cultural knowledge: {e}")
|
|
1410
|
+
raise e
|
|
892
1411
|
|
|
893
1412
|
# -- Metrics methods --
|
|
894
1413
|
|
|
@@ -1040,7 +1559,7 @@ class MongoDb(BaseDb):
|
|
|
1040
1559
|
|
|
1041
1560
|
except Exception as e:
|
|
1042
1561
|
log_error(f"Error getting metrics: {e}")
|
|
1043
|
-
|
|
1562
|
+
raise e
|
|
1044
1563
|
|
|
1045
1564
|
# -- Knowledge methods --
|
|
1046
1565
|
|
|
@@ -1064,7 +1583,7 @@ class MongoDb(BaseDb):
|
|
|
1064
1583
|
|
|
1065
1584
|
except Exception as e:
|
|
1066
1585
|
log_error(f"Error deleting knowledge content: {e}")
|
|
1067
|
-
raise
|
|
1586
|
+
raise e
|
|
1068
1587
|
|
|
1069
1588
|
def get_knowledge_content(self, id: str) -> Optional[KnowledgeRow]:
|
|
1070
1589
|
"""Get a knowledge row from the database.
|
|
@@ -1091,7 +1610,7 @@ class MongoDb(BaseDb):
|
|
|
1091
1610
|
|
|
1092
1611
|
except Exception as e:
|
|
1093
1612
|
log_error(f"Error getting knowledge content: {e}")
|
|
1094
|
-
|
|
1613
|
+
raise e
|
|
1095
1614
|
|
|
1096
1615
|
def get_knowledge_contents(
|
|
1097
1616
|
self,
|
|
@@ -1146,7 +1665,7 @@ class MongoDb(BaseDb):
|
|
|
1146
1665
|
|
|
1147
1666
|
except Exception as e:
|
|
1148
1667
|
log_error(f"Error getting knowledge contents: {e}")
|
|
1149
|
-
|
|
1668
|
+
raise e
|
|
1150
1669
|
|
|
1151
1670
|
def upsert_knowledge_content(self, knowledge_row: KnowledgeRow):
|
|
1152
1671
|
"""Upsert knowledge content in the database.
|
|
@@ -1172,7 +1691,7 @@ class MongoDb(BaseDb):
|
|
|
1172
1691
|
|
|
1173
1692
|
except Exception as e:
|
|
1174
1693
|
log_error(f"Error upserting knowledge content: {e}")
|
|
1175
|
-
|
|
1694
|
+
raise e
|
|
1176
1695
|
|
|
1177
1696
|
# -- Eval methods --
|
|
1178
1697
|
|
|
@@ -1196,7 +1715,7 @@ class MongoDb(BaseDb):
|
|
|
1196
1715
|
|
|
1197
1716
|
except Exception as e:
|
|
1198
1717
|
log_error(f"Error creating eval run: {e}")
|
|
1199
|
-
|
|
1718
|
+
raise e
|
|
1200
1719
|
|
|
1201
1720
|
def delete_eval_run(self, eval_run_id: str) -> None:
|
|
1202
1721
|
"""Delete an eval run from the database."""
|
|
@@ -1214,7 +1733,7 @@ class MongoDb(BaseDb):
|
|
|
1214
1733
|
|
|
1215
1734
|
except Exception as e:
|
|
1216
1735
|
log_error(f"Error deleting eval run {eval_run_id}: {e}")
|
|
1217
|
-
raise
|
|
1736
|
+
raise e
|
|
1218
1737
|
|
|
1219
1738
|
def delete_eval_runs(self, eval_run_ids: List[str]) -> None:
|
|
1220
1739
|
"""Delete multiple eval runs from the database."""
|
|
@@ -1232,7 +1751,7 @@ class MongoDb(BaseDb):
|
|
|
1232
1751
|
|
|
1233
1752
|
except Exception as e:
|
|
1234
1753
|
log_error(f"Error deleting eval runs {eval_run_ids}: {e}")
|
|
1235
|
-
raise
|
|
1754
|
+
raise e
|
|
1236
1755
|
|
|
1237
1756
|
def get_eval_run_raw(self, eval_run_id: str) -> Optional[Dict[str, Any]]:
|
|
1238
1757
|
"""Get an eval run from the database as a raw dictionary."""
|
|
@@ -1246,7 +1765,7 @@ class MongoDb(BaseDb):
|
|
|
1246
1765
|
|
|
1247
1766
|
except Exception as e:
|
|
1248
1767
|
log_error(f"Exception getting eval run {eval_run_id}: {e}")
|
|
1249
|
-
|
|
1768
|
+
raise e
|
|
1250
1769
|
|
|
1251
1770
|
def get_eval_run(self, eval_run_id: str, deserialize: Optional[bool] = True) -> Optional[EvalRunRecord]:
|
|
1252
1771
|
"""Get an eval run from the database.
|
|
@@ -1280,7 +1799,7 @@ class MongoDb(BaseDb):
|
|
|
1280
1799
|
|
|
1281
1800
|
except Exception as e:
|
|
1282
1801
|
log_error(f"Exception getting eval run {eval_run_id}: {e}")
|
|
1283
|
-
|
|
1802
|
+
raise e
|
|
1284
1803
|
|
|
1285
1804
|
def get_eval_runs(
|
|
1286
1805
|
self,
|
|
@@ -1374,8 +1893,8 @@ class MongoDb(BaseDb):
|
|
|
1374
1893
|
return [EvalRunRecord.model_validate(row) for row in records]
|
|
1375
1894
|
|
|
1376
1895
|
except Exception as e:
|
|
1377
|
-
|
|
1378
|
-
|
|
1896
|
+
log_error(f"Exception getting eval runs: {e}")
|
|
1897
|
+
raise e
|
|
1379
1898
|
|
|
1380
1899
|
def rename_eval_run(
|
|
1381
1900
|
self, eval_run_id: str, name: str, deserialize: Optional[bool] = True
|
|
@@ -1413,4 +1932,62 @@ class MongoDb(BaseDb):
|
|
|
1413
1932
|
|
|
1414
1933
|
except Exception as e:
|
|
1415
1934
|
log_error(f"Error updating eval run name {eval_run_id}: {e}")
|
|
1416
|
-
raise
|
|
1935
|
+
raise e
|
|
1936
|
+
|
|
1937
|
+
def migrate_table_from_v1_to_v2(self, v1_db_schema: str, v1_table_name: str, v1_table_type: str):
|
|
1938
|
+
"""Migrate all content in the given collection to the right v2 collection"""
|
|
1939
|
+
|
|
1940
|
+
from typing import List, Sequence, Union
|
|
1941
|
+
|
|
1942
|
+
from agno.db.migrations.v1_to_v2 import (
|
|
1943
|
+
get_all_table_content,
|
|
1944
|
+
parse_agent_sessions,
|
|
1945
|
+
parse_memories,
|
|
1946
|
+
parse_team_sessions,
|
|
1947
|
+
parse_workflow_sessions,
|
|
1948
|
+
)
|
|
1949
|
+
|
|
1950
|
+
# Get all content from the old collection
|
|
1951
|
+
old_content: list[dict[str, Any]] = get_all_table_content(
|
|
1952
|
+
db=self,
|
|
1953
|
+
db_schema=v1_db_schema,
|
|
1954
|
+
table_name=v1_table_name,
|
|
1955
|
+
)
|
|
1956
|
+
if not old_content:
|
|
1957
|
+
log_info(f"No content to migrate from collection {v1_table_name}")
|
|
1958
|
+
return
|
|
1959
|
+
|
|
1960
|
+
# Parse the content into the new format
|
|
1961
|
+
memories: List[UserMemory] = []
|
|
1962
|
+
sessions: Sequence[Union[AgentSession, TeamSession, WorkflowSession]] = []
|
|
1963
|
+
if v1_table_type == "agent_sessions":
|
|
1964
|
+
sessions = parse_agent_sessions(old_content)
|
|
1965
|
+
elif v1_table_type == "team_sessions":
|
|
1966
|
+
sessions = parse_team_sessions(old_content)
|
|
1967
|
+
elif v1_table_type == "workflow_sessions":
|
|
1968
|
+
sessions = parse_workflow_sessions(old_content)
|
|
1969
|
+
elif v1_table_type == "memories":
|
|
1970
|
+
memories = parse_memories(old_content)
|
|
1971
|
+
else:
|
|
1972
|
+
raise ValueError(f"Invalid table type: {v1_table_type}")
|
|
1973
|
+
|
|
1974
|
+
# Insert the new content into the new collection
|
|
1975
|
+
if v1_table_type == "agent_sessions":
|
|
1976
|
+
for session in sessions:
|
|
1977
|
+
self.upsert_session(session)
|
|
1978
|
+
log_info(f"Migrated {len(sessions)} Agent sessions to collection: {self.session_table_name}")
|
|
1979
|
+
|
|
1980
|
+
elif v1_table_type == "team_sessions":
|
|
1981
|
+
for session in sessions:
|
|
1982
|
+
self.upsert_session(session)
|
|
1983
|
+
log_info(f"Migrated {len(sessions)} Team sessions to collection: {self.session_table_name}")
|
|
1984
|
+
|
|
1985
|
+
elif v1_table_type == "workflow_sessions":
|
|
1986
|
+
for session in sessions:
|
|
1987
|
+
self.upsert_session(session)
|
|
1988
|
+
log_info(f"Migrated {len(sessions)} Workflow sessions to collection: {self.session_table_name}")
|
|
1989
|
+
|
|
1990
|
+
elif v1_table_type == "memories":
|
|
1991
|
+
for memory in memories:
|
|
1992
|
+
self.upsert_user_memory(memory)
|
|
1993
|
+
log_info(f"Migrated {len(memories)} memories to collection: {self.memory_table_name}")
|