agno 2.0.0rc2__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 +6009 -2874
- 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 +595 -187
- 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 +3 -0
- agno/knowledge/types.py +9 -0
- agno/knowledge/utils.py +20 -0
- agno/media.py +339 -266
- 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 +1011 -566
- 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 +110 -37
- 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 +143 -4
- 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 +60 -6
- agno/models/openai/chat.py +102 -43
- 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 +81 -5
- 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 -175
- 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 +266 -112
- agno/run/base.py +53 -24
- agno/run/team.py +252 -111
- agno/run/workflow.py +156 -45
- 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 -1692
- agno/tools/brightdata.py +3 -3
- agno/tools/cartesia.py +3 -5
- agno/tools/dalle.py +9 -8
- agno/tools/decorator.py +4 -2
- agno/tools/desi_vocal.py +2 -2
- agno/tools/duckduckgo.py +15 -11
- agno/tools/e2b.py +20 -13
- agno/tools/eleven_labs.py +26 -28
- agno/tools/exa.py +21 -16
- agno/tools/fal.py +4 -4
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +350 -0
- agno/tools/firecrawl.py +4 -4
- agno/tools/function.py +257 -37
- agno/tools/giphy.py +2 -2
- 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/lumalab.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/azure_openai.py +2 -2
- agno/tools/models/gemini.py +3 -3
- agno/tools/models/groq.py +3 -5
- agno/tools/models/nebius.py +7 -7
- agno/tools/models_labs.py +25 -15
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +4 -9
- agno/tools/opencv.py +3 -3
- agno/tools/parallel.py +314 -0
- agno/tools/replicate.py +7 -7
- 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 +222 -7
- agno/utils/gemini.py +181 -23
- 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 +95 -5
- 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/models/cohere.py +1 -1
- agno/utils/models/watsonx.py +1 -1
- agno/utils/openai.py +1 -1
- 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 +183 -135
- 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 +645 -136
- agno/workflow/steps.py +65 -6
- agno/workflow/types.py +71 -33
- agno/workflow/workflow.py +2113 -300
- agno-2.3.0.dist-info/METADATA +618 -0
- agno-2.3.0.dist-info/RECORD +577 -0
- agno-2.3.0.dist-info/licenses/LICENSE +201 -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.0rc2.dist-info/METADATA +0 -355
- agno-2.0.0rc2.dist-info/RECORD +0 -515
- agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
- {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
- {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import time
|
|
2
|
+
from copy import deepcopy
|
|
2
3
|
from datetime import date, datetime, timedelta, timezone
|
|
3
4
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
4
5
|
from uuid import uuid4
|
|
@@ -7,9 +8,12 @@ from agno.db.base import BaseDb, SessionType
|
|
|
7
8
|
from agno.db.in_memory.utils import (
|
|
8
9
|
apply_sorting,
|
|
9
10
|
calculate_date_metrics,
|
|
11
|
+
deserialize_cultural_knowledge_from_db,
|
|
10
12
|
fetch_all_sessions_data,
|
|
11
13
|
get_dates_to_calculate_metrics_for,
|
|
14
|
+
serialize_cultural_knowledge_for_db,
|
|
12
15
|
)
|
|
16
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
13
17
|
from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
|
|
14
18
|
from agno.db.schemas.knowledge import KnowledgeRow
|
|
15
19
|
from agno.db.schemas.memory import UserMemory
|
|
@@ -28,9 +32,21 @@ class InMemoryDb(BaseDb):
|
|
|
28
32
|
self._metrics: List[Dict[str, Any]] = []
|
|
29
33
|
self._eval_runs: List[Dict[str, Any]] = []
|
|
30
34
|
self._knowledge: List[Dict[str, Any]] = []
|
|
35
|
+
self._cultural_knowledge: List[Dict[str, Any]] = []
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
def table_exists(self, table_name: str) -> bool:
|
|
38
|
+
"""In-memory implementation, always returns True."""
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
def get_latest_schema_version(self):
|
|
42
|
+
"""Get the latest version of the database schema."""
|
|
43
|
+
pass
|
|
33
44
|
|
|
45
|
+
def upsert_schema_version(self, version: str) -> None:
|
|
46
|
+
"""Upsert the schema version into the database."""
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
# -- Session methods --
|
|
34
50
|
def delete_session(self, session_id: str) -> bool:
|
|
35
51
|
"""Delete a session from in-memory storage.
|
|
36
52
|
|
|
@@ -56,7 +72,7 @@ class InMemoryDb(BaseDb):
|
|
|
56
72
|
|
|
57
73
|
except Exception as e:
|
|
58
74
|
log_error(f"Error deleting session: {e}")
|
|
59
|
-
|
|
75
|
+
raise e
|
|
60
76
|
|
|
61
77
|
def delete_sessions(self, session_ids: List[str]) -> None:
|
|
62
78
|
"""Delete multiple sessions from in-memory storage.
|
|
@@ -73,6 +89,7 @@ class InMemoryDb(BaseDb):
|
|
|
73
89
|
|
|
74
90
|
except Exception as e:
|
|
75
91
|
log_error(f"Error deleting sessions: {e}")
|
|
92
|
+
raise e
|
|
76
93
|
|
|
77
94
|
def get_session(
|
|
78
95
|
self,
|
|
@@ -102,19 +119,18 @@ class InMemoryDb(BaseDb):
|
|
|
102
119
|
if session_data.get("session_id") == session_id:
|
|
103
120
|
if user_id is not None and session_data.get("user_id") != user_id:
|
|
104
121
|
continue
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
continue
|
|
122
|
+
|
|
123
|
+
session_data_copy = deepcopy(session_data)
|
|
108
124
|
|
|
109
125
|
if not deserialize:
|
|
110
|
-
return
|
|
126
|
+
return session_data_copy
|
|
111
127
|
|
|
112
128
|
if session_type == SessionType.AGENT:
|
|
113
|
-
return AgentSession.from_dict(
|
|
129
|
+
return AgentSession.from_dict(session_data_copy)
|
|
114
130
|
elif session_type == SessionType.TEAM:
|
|
115
|
-
return TeamSession.from_dict(
|
|
131
|
+
return TeamSession.from_dict(session_data_copy)
|
|
116
132
|
else:
|
|
117
|
-
return WorkflowSession.from_dict(
|
|
133
|
+
return WorkflowSession.from_dict(session_data_copy)
|
|
118
134
|
|
|
119
135
|
return None
|
|
120
136
|
|
|
@@ -123,7 +139,7 @@ class InMemoryDb(BaseDb):
|
|
|
123
139
|
|
|
124
140
|
traceback.print_exc()
|
|
125
141
|
log_error(f"Exception reading session: {e}")
|
|
126
|
-
|
|
142
|
+
raise e
|
|
127
143
|
|
|
128
144
|
def get_sessions(
|
|
129
145
|
self,
|
|
@@ -187,7 +203,7 @@ class InMemoryDb(BaseDb):
|
|
|
187
203
|
if session_data.get("session_type") != session_type_value:
|
|
188
204
|
continue
|
|
189
205
|
|
|
190
|
-
filtered_sessions.append(session_data)
|
|
206
|
+
filtered_sessions.append(deepcopy(session_data))
|
|
191
207
|
|
|
192
208
|
total_count = len(filtered_sessions)
|
|
193
209
|
|
|
@@ -215,7 +231,7 @@ class InMemoryDb(BaseDb):
|
|
|
215
231
|
|
|
216
232
|
except Exception as e:
|
|
217
233
|
log_error(f"Exception reading sessions: {e}")
|
|
218
|
-
|
|
234
|
+
raise e
|
|
219
235
|
|
|
220
236
|
def rename_session(
|
|
221
237
|
self, session_id: str, session_type: SessionType, session_name: str, deserialize: Optional[bool] = True
|
|
@@ -232,21 +248,22 @@ class InMemoryDb(BaseDb):
|
|
|
232
248
|
|
|
233
249
|
log_debug(f"Renamed session with id '{session_id}' to '{session_name}'")
|
|
234
250
|
|
|
251
|
+
session_copy = deepcopy(session)
|
|
235
252
|
if not deserialize:
|
|
236
|
-
return
|
|
253
|
+
return session_copy
|
|
237
254
|
|
|
238
255
|
if session_type == SessionType.AGENT:
|
|
239
|
-
return AgentSession.from_dict(
|
|
256
|
+
return AgentSession.from_dict(session_copy)
|
|
240
257
|
elif session_type == SessionType.TEAM:
|
|
241
|
-
return TeamSession.from_dict(
|
|
258
|
+
return TeamSession.from_dict(session_copy)
|
|
242
259
|
else:
|
|
243
|
-
return WorkflowSession.from_dict(
|
|
260
|
+
return WorkflowSession.from_dict(session_copy)
|
|
244
261
|
|
|
245
262
|
return None
|
|
246
263
|
|
|
247
264
|
except Exception as e:
|
|
248
265
|
log_error(f"Exception renaming session: {e}")
|
|
249
|
-
|
|
266
|
+
raise e
|
|
250
267
|
|
|
251
268
|
def upsert_session(
|
|
252
269
|
self, session: Session, deserialize: Optional[bool] = True
|
|
@@ -268,26 +285,30 @@ class InMemoryDb(BaseDb):
|
|
|
268
285
|
if existing_session.get("session_id") == session_dict.get("session_id") and self._matches_session_key(
|
|
269
286
|
existing_session, session
|
|
270
287
|
):
|
|
271
|
-
# Update existing session
|
|
272
288
|
session_dict["updated_at"] = int(time.time())
|
|
273
|
-
self._sessions[i] = session_dict
|
|
289
|
+
self._sessions[i] = deepcopy(session_dict)
|
|
274
290
|
session_updated = True
|
|
275
291
|
break
|
|
276
292
|
|
|
277
293
|
if not session_updated:
|
|
278
|
-
# Add new session
|
|
279
294
|
session_dict["created_at"] = session_dict.get("created_at", int(time.time()))
|
|
280
295
|
session_dict["updated_at"] = session_dict.get("created_at")
|
|
281
|
-
self._sessions.append(session_dict)
|
|
296
|
+
self._sessions.append(deepcopy(session_dict))
|
|
282
297
|
|
|
298
|
+
session_dict_copy = deepcopy(session_dict)
|
|
283
299
|
if not deserialize:
|
|
284
|
-
return
|
|
300
|
+
return session_dict_copy
|
|
285
301
|
|
|
286
|
-
|
|
302
|
+
if session_dict_copy["session_type"] == SessionType.AGENT:
|
|
303
|
+
return AgentSession.from_dict(session_dict_copy)
|
|
304
|
+
elif session_dict_copy["session_type"] == SessionType.TEAM:
|
|
305
|
+
return TeamSession.from_dict(session_dict_copy)
|
|
306
|
+
else:
|
|
307
|
+
return WorkflowSession.from_dict(session_dict_copy)
|
|
287
308
|
|
|
288
309
|
except Exception as e:
|
|
289
310
|
log_error(f"Exception upserting session: {e}")
|
|
290
|
-
|
|
311
|
+
raise e
|
|
291
312
|
|
|
292
313
|
def _matches_session_key(self, existing_session: Dict[str, Any], session: Session) -> bool:
|
|
293
314
|
"""Check if existing session matches the key for the session type."""
|
|
@@ -299,11 +320,61 @@ class InMemoryDb(BaseDb):
|
|
|
299
320
|
return existing_session.get("workflow_id") == session.workflow_id
|
|
300
321
|
return False
|
|
301
322
|
|
|
323
|
+
def upsert_sessions(
|
|
324
|
+
self, sessions: List[Session], deserialize: Optional[bool] = True, preserve_updated_at: bool = False
|
|
325
|
+
) -> List[Union[Session, Dict[str, Any]]]:
|
|
326
|
+
"""
|
|
327
|
+
Bulk upsert multiple sessions for improved performance on large datasets.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
sessions (List[Session]): List of sessions to upsert.
|
|
331
|
+
deserialize (Optional[bool]): Whether to deserialize the sessions. Defaults to True.
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
List[Union[Session, Dict[str, Any]]]: List of upserted sessions.
|
|
335
|
+
|
|
336
|
+
Raises:
|
|
337
|
+
Exception: If an error occurs during bulk upsert.
|
|
338
|
+
"""
|
|
339
|
+
if not sessions:
|
|
340
|
+
return []
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
log_info(f"In-memory database: processing {len(sessions)} sessions with individual upsert operations")
|
|
344
|
+
|
|
345
|
+
results = []
|
|
346
|
+
for session in sessions:
|
|
347
|
+
if session is not None:
|
|
348
|
+
result = self.upsert_session(session, deserialize=deserialize)
|
|
349
|
+
if result is not None:
|
|
350
|
+
results.append(result)
|
|
351
|
+
return results
|
|
352
|
+
|
|
353
|
+
except Exception as e:
|
|
354
|
+
log_error(f"Exception during bulk session upsert: {e}")
|
|
355
|
+
return []
|
|
356
|
+
|
|
302
357
|
# -- Memory methods --
|
|
303
|
-
def delete_user_memory(self, memory_id: str):
|
|
358
|
+
def delete_user_memory(self, memory_id: str, user_id: Optional[str] = None):
|
|
359
|
+
"""Delete a user memory from in-memory storage.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
memory_id (str): The ID of the memory to delete.
|
|
363
|
+
user_id (Optional[str]): The ID of the user. If provided, verifies the memory belongs to this user before deletion.
|
|
364
|
+
|
|
365
|
+
Raises:
|
|
366
|
+
Exception: If an error occurs during deletion.
|
|
367
|
+
"""
|
|
304
368
|
try:
|
|
305
369
|
original_count = len(self._memories)
|
|
306
|
-
|
|
370
|
+
|
|
371
|
+
# If user_id is provided, verify ownership before deleting
|
|
372
|
+
if user_id is not None:
|
|
373
|
+
self._memories = [
|
|
374
|
+
m for m in self._memories if not (m.get("memory_id") == memory_id and m.get("user_id") == user_id)
|
|
375
|
+
]
|
|
376
|
+
else:
|
|
377
|
+
self._memories = [m for m in self._memories if m.get("memory_id") != memory_id]
|
|
307
378
|
|
|
308
379
|
if len(self._memories) < original_count:
|
|
309
380
|
log_debug(f"Successfully deleted user memory id: {memory_id}")
|
|
@@ -312,17 +383,41 @@ class InMemoryDb(BaseDb):
|
|
|
312
383
|
|
|
313
384
|
except Exception as e:
|
|
314
385
|
log_error(f"Error deleting memory: {e}")
|
|
386
|
+
raise e
|
|
315
387
|
|
|
316
|
-
def delete_user_memories(self, memory_ids: List[str]) -> None:
|
|
317
|
-
"""Delete multiple user memories from in-memory storage.
|
|
388
|
+
def delete_user_memories(self, memory_ids: List[str], user_id: Optional[str] = None) -> None:
|
|
389
|
+
"""Delete multiple user memories from in-memory storage.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
memory_ids (List[str]): The IDs of the memories to delete.
|
|
393
|
+
user_id (Optional[str]): The ID of the user. If provided, only deletes memories belonging to this user.
|
|
394
|
+
|
|
395
|
+
Raises:
|
|
396
|
+
Exception: If an error occurs during deletion.
|
|
397
|
+
"""
|
|
318
398
|
try:
|
|
319
|
-
|
|
399
|
+
# If user_id is provided, verify ownership before deleting
|
|
400
|
+
if user_id is not None:
|
|
401
|
+
self._memories = [
|
|
402
|
+
m for m in self._memories if not (m.get("memory_id") in memory_ids and m.get("user_id") == user_id)
|
|
403
|
+
]
|
|
404
|
+
else:
|
|
405
|
+
self._memories = [m for m in self._memories if m.get("memory_id") not in memory_ids]
|
|
320
406
|
log_debug(f"Successfully deleted {len(memory_ids)} user memories")
|
|
321
407
|
|
|
322
408
|
except Exception as e:
|
|
323
409
|
log_error(f"Error deleting memories: {e}")
|
|
410
|
+
raise e
|
|
324
411
|
|
|
325
412
|
def get_all_memory_topics(self) -> List[str]:
|
|
413
|
+
"""Get all memory topics from in-memory storage.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
List[str]: List of unique topics.
|
|
417
|
+
|
|
418
|
+
Raises:
|
|
419
|
+
Exception: If an error occurs while reading topics.
|
|
420
|
+
"""
|
|
326
421
|
try:
|
|
327
422
|
topics = set()
|
|
328
423
|
for memory in self._memories:
|
|
@@ -333,23 +428,41 @@ class InMemoryDb(BaseDb):
|
|
|
333
428
|
|
|
334
429
|
except Exception as e:
|
|
335
430
|
log_error(f"Exception reading from memory storage: {e}")
|
|
336
|
-
|
|
431
|
+
raise e
|
|
337
432
|
|
|
338
433
|
def get_user_memory(
|
|
339
|
-
self, memory_id: str, deserialize: Optional[bool] = True
|
|
434
|
+
self, memory_id: str, deserialize: Optional[bool] = True, user_id: Optional[str] = None
|
|
340
435
|
) -> Optional[Union[UserMemory, Dict[str, Any]]]:
|
|
436
|
+
"""Get a user memory from in-memory storage.
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
memory_id (str): The ID of the memory to retrieve.
|
|
440
|
+
deserialize (Optional[bool]): Whether to deserialize the memory. Defaults to True.
|
|
441
|
+
user_id (Optional[str]): The ID of the user. If provided, only returns the memory if it belongs to this user.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
Optional[Union[UserMemory, Dict[str, Any]]]: The memory object or dictionary, or None if not found.
|
|
445
|
+
|
|
446
|
+
Raises:
|
|
447
|
+
Exception: If an error occurs while reading the memory.
|
|
448
|
+
"""
|
|
341
449
|
try:
|
|
342
450
|
for memory_data in self._memories:
|
|
343
451
|
if memory_data.get("memory_id") == memory_id:
|
|
452
|
+
# Filter by user_id if provided
|
|
453
|
+
if user_id is not None and memory_data.get("user_id") != user_id:
|
|
454
|
+
continue
|
|
455
|
+
|
|
456
|
+
memory_data_copy = deepcopy(memory_data)
|
|
344
457
|
if not deserialize:
|
|
345
|
-
return
|
|
346
|
-
return UserMemory.from_dict(
|
|
458
|
+
return memory_data_copy
|
|
459
|
+
return UserMemory.from_dict(memory_data_copy)
|
|
347
460
|
|
|
348
461
|
return None
|
|
349
462
|
|
|
350
463
|
except Exception as e:
|
|
351
464
|
log_error(f"Exception reading from memory storage: {e}")
|
|
352
|
-
|
|
465
|
+
raise e
|
|
353
466
|
|
|
354
467
|
def get_user_memories(
|
|
355
468
|
self,
|
|
@@ -383,7 +496,7 @@ class InMemoryDb(BaseDb):
|
|
|
383
496
|
if search_content.lower() not in memory_content.lower():
|
|
384
497
|
continue
|
|
385
498
|
|
|
386
|
-
filtered_memories.append(memory_data)
|
|
499
|
+
filtered_memories.append(deepcopy(memory_data))
|
|
387
500
|
|
|
388
501
|
total_count = len(filtered_memories)
|
|
389
502
|
|
|
@@ -404,24 +517,40 @@ class InMemoryDb(BaseDb):
|
|
|
404
517
|
|
|
405
518
|
except Exception as e:
|
|
406
519
|
log_error(f"Exception reading from memory storage: {e}")
|
|
407
|
-
|
|
520
|
+
raise e
|
|
408
521
|
|
|
409
522
|
def get_user_memory_stats(
|
|
410
523
|
self, limit: Optional[int] = None, page: Optional[int] = None
|
|
411
524
|
) -> Tuple[List[Dict[str, Any]], int]:
|
|
412
|
-
"""Get user memory statistics.
|
|
525
|
+
"""Get user memory statistics.
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
limit (Optional[int]): Maximum number of stats to return.
|
|
529
|
+
page (Optional[int]): Page number for pagination.
|
|
530
|
+
|
|
531
|
+
Returns:
|
|
532
|
+
Tuple[List[Dict[str, Any]], int]: List of user memory statistics and total count.
|
|
533
|
+
|
|
534
|
+
Raises:
|
|
535
|
+
Exception: If an error occurs while getting stats.
|
|
536
|
+
"""
|
|
413
537
|
try:
|
|
414
538
|
user_stats = {}
|
|
415
539
|
|
|
416
540
|
for memory in self._memories:
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
541
|
+
memory_user_id = memory.get("user_id")
|
|
542
|
+
|
|
543
|
+
if memory_user_id:
|
|
544
|
+
if memory_user_id not in user_stats:
|
|
545
|
+
user_stats[memory_user_id] = {
|
|
546
|
+
"user_id": memory_user_id,
|
|
547
|
+
"total_memories": 0,
|
|
548
|
+
"last_memory_updated_at": 0,
|
|
549
|
+
}
|
|
550
|
+
user_stats[memory_user_id]["total_memories"] += 1
|
|
422
551
|
updated_at = memory.get("updated_at", 0)
|
|
423
|
-
if updated_at > user_stats[
|
|
424
|
-
user_stats[
|
|
552
|
+
if updated_at > user_stats[memory_user_id]["last_memory_updated_at"]:
|
|
553
|
+
user_stats[memory_user_id]["last_memory_updated_at"] = updated_at
|
|
425
554
|
|
|
426
555
|
stats_list = list(user_stats.values())
|
|
427
556
|
stats_list.sort(key=lambda x: x["last_memory_updated_at"], reverse=True)
|
|
@@ -439,7 +568,7 @@ class InMemoryDb(BaseDb):
|
|
|
439
568
|
|
|
440
569
|
except Exception as e:
|
|
441
570
|
log_error(f"Exception getting user memory stats: {e}")
|
|
442
|
-
|
|
571
|
+
raise e
|
|
443
572
|
|
|
444
573
|
def upsert_user_memory(
|
|
445
574
|
self, memory: UserMemory, deserialize: Optional[bool] = True
|
|
@@ -462,13 +591,50 @@ class InMemoryDb(BaseDb):
|
|
|
462
591
|
if not memory_updated:
|
|
463
592
|
self._memories.append(memory_dict)
|
|
464
593
|
|
|
594
|
+
memory_dict_copy = deepcopy(memory_dict)
|
|
465
595
|
if not deserialize:
|
|
466
|
-
return
|
|
467
|
-
|
|
596
|
+
return memory_dict_copy
|
|
597
|
+
|
|
598
|
+
return UserMemory.from_dict(memory_dict_copy)
|
|
468
599
|
|
|
469
600
|
except Exception as e:
|
|
470
601
|
log_warning(f"Exception upserting user memory: {e}")
|
|
471
|
-
|
|
602
|
+
raise e
|
|
603
|
+
|
|
604
|
+
def upsert_memories(
|
|
605
|
+
self, memories: List[UserMemory], deserialize: Optional[bool] = True, preserve_updated_at: bool = False
|
|
606
|
+
) -> List[Union[UserMemory, Dict[str, Any]]]:
|
|
607
|
+
"""
|
|
608
|
+
Bulk upsert multiple user memories for improved performance on large datasets.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
memories (List[UserMemory]): List of memories to upsert.
|
|
612
|
+
deserialize (Optional[bool]): Whether to deserialize the memories. Defaults to True.
|
|
613
|
+
|
|
614
|
+
Returns:
|
|
615
|
+
List[Union[UserMemory, Dict[str, Any]]]: List of upserted memories.
|
|
616
|
+
|
|
617
|
+
Raises:
|
|
618
|
+
Exception: If an error occurs during bulk upsert.
|
|
619
|
+
"""
|
|
620
|
+
if not memories:
|
|
621
|
+
return []
|
|
622
|
+
|
|
623
|
+
try:
|
|
624
|
+
log_info(f"In-memory database: processing {len(memories)} memories with individual upsert operations")
|
|
625
|
+
# For in-memory database, individual upserts are actually efficient
|
|
626
|
+
# since we're just manipulating Python lists and dictionaries
|
|
627
|
+
results = []
|
|
628
|
+
for memory in memories:
|
|
629
|
+
if memory is not None:
|
|
630
|
+
result = self.upsert_user_memory(memory, deserialize=deserialize)
|
|
631
|
+
if result is not None:
|
|
632
|
+
results.append(result)
|
|
633
|
+
return results
|
|
634
|
+
|
|
635
|
+
except Exception as e:
|
|
636
|
+
log_error(f"Exception during bulk memory upsert: {e}")
|
|
637
|
+
return []
|
|
472
638
|
|
|
473
639
|
def clear_memories(self) -> None:
|
|
474
640
|
"""Delete all memories.
|
|
@@ -481,6 +647,7 @@ class InMemoryDb(BaseDb):
|
|
|
481
647
|
|
|
482
648
|
except Exception as e:
|
|
483
649
|
log_warning(f"Exception deleting all memories: {e}")
|
|
650
|
+
raise e
|
|
484
651
|
|
|
485
652
|
# -- Metrics methods --
|
|
486
653
|
def calculate_metrics(self) -> Optional[list[dict]]:
|
|
@@ -544,7 +711,7 @@ class InMemoryDb(BaseDb):
|
|
|
544
711
|
|
|
545
712
|
except Exception as e:
|
|
546
713
|
log_warning(f"Exception refreshing metrics: {e}")
|
|
547
|
-
|
|
714
|
+
raise e
|
|
548
715
|
|
|
549
716
|
def _get_metrics_calculation_starting_date(self, metrics: List[Dict[str, Any]]) -> Optional[date]:
|
|
550
717
|
"""Get the first date for which metrics calculation is needed."""
|
|
@@ -584,8 +751,8 @@ class InMemoryDb(BaseDb):
|
|
|
584
751
|
# Only include necessary fields for metrics
|
|
585
752
|
filtered_session = {
|
|
586
753
|
"user_id": session.get("user_id"),
|
|
587
|
-
"session_data": session.get("session_data"),
|
|
588
|
-
"runs": session.get("runs"),
|
|
754
|
+
"session_data": deepcopy(session.get("session_data")),
|
|
755
|
+
"runs": deepcopy(session.get("runs")),
|
|
589
756
|
"created_at": session.get("created_at"),
|
|
590
757
|
"session_type": session.get("session_type"),
|
|
591
758
|
}
|
|
@@ -595,7 +762,7 @@ class InMemoryDb(BaseDb):
|
|
|
595
762
|
|
|
596
763
|
except Exception as e:
|
|
597
764
|
log_error(f"Exception reading sessions for metrics: {e}")
|
|
598
|
-
|
|
765
|
+
raise e
|
|
599
766
|
|
|
600
767
|
def get_metrics(
|
|
601
768
|
self,
|
|
@@ -615,7 +782,7 @@ class InMemoryDb(BaseDb):
|
|
|
615
782
|
if ending_date and metric_date > ending_date:
|
|
616
783
|
continue
|
|
617
784
|
|
|
618
|
-
filtered_metrics.append(metric)
|
|
785
|
+
filtered_metrics.append(deepcopy(metric))
|
|
619
786
|
|
|
620
787
|
updated_at = metric.get("updated_at")
|
|
621
788
|
if updated_at and (latest_updated_at is None or updated_at > latest_updated_at):
|
|
@@ -625,7 +792,7 @@ class InMemoryDb(BaseDb):
|
|
|
625
792
|
|
|
626
793
|
except Exception as e:
|
|
627
794
|
log_error(f"Exception getting metrics: {e}")
|
|
628
|
-
|
|
795
|
+
raise e
|
|
629
796
|
|
|
630
797
|
# -- Knowledge methods --
|
|
631
798
|
|
|
@@ -643,6 +810,7 @@ class InMemoryDb(BaseDb):
|
|
|
643
810
|
|
|
644
811
|
except Exception as e:
|
|
645
812
|
log_error(f"Error deleting knowledge content: {e}")
|
|
813
|
+
raise e
|
|
646
814
|
|
|
647
815
|
def get_knowledge_content(self, id: str) -> Optional[KnowledgeRow]:
|
|
648
816
|
"""Get a knowledge row from in-memory storage.
|
|
@@ -665,7 +833,7 @@ class InMemoryDb(BaseDb):
|
|
|
665
833
|
|
|
666
834
|
except Exception as e:
|
|
667
835
|
log_error(f"Error getting knowledge content: {e}")
|
|
668
|
-
|
|
836
|
+
raise e
|
|
669
837
|
|
|
670
838
|
def get_knowledge_contents(
|
|
671
839
|
self,
|
|
@@ -689,7 +857,7 @@ class InMemoryDb(BaseDb):
|
|
|
689
857
|
Exception: If an error occurs during retrieval.
|
|
690
858
|
"""
|
|
691
859
|
try:
|
|
692
|
-
knowledge_items = self._knowledge
|
|
860
|
+
knowledge_items = [deepcopy(item) for item in self._knowledge]
|
|
693
861
|
|
|
694
862
|
total_count = len(knowledge_items)
|
|
695
863
|
|
|
@@ -707,7 +875,7 @@ class InMemoryDb(BaseDb):
|
|
|
707
875
|
|
|
708
876
|
except Exception as e:
|
|
709
877
|
log_error(f"Error getting knowledge contents: {e}")
|
|
710
|
-
|
|
878
|
+
raise e
|
|
711
879
|
|
|
712
880
|
def upsert_knowledge_content(self, knowledge_row: KnowledgeRow):
|
|
713
881
|
"""Upsert knowledge content.
|
|
@@ -739,7 +907,7 @@ class InMemoryDb(BaseDb):
|
|
|
739
907
|
|
|
740
908
|
except Exception as e:
|
|
741
909
|
log_error(f"Error upserting knowledge row: {e}")
|
|
742
|
-
|
|
910
|
+
raise e
|
|
743
911
|
|
|
744
912
|
# -- Eval methods --
|
|
745
913
|
|
|
@@ -759,7 +927,7 @@ class InMemoryDb(BaseDb):
|
|
|
759
927
|
|
|
760
928
|
except Exception as e:
|
|
761
929
|
log_error(f"Error creating eval run: {e}")
|
|
762
|
-
|
|
930
|
+
raise e
|
|
763
931
|
|
|
764
932
|
def delete_eval_runs(self, eval_run_ids: List[str]) -> None:
|
|
765
933
|
"""Delete multiple eval runs from in-memory storage."""
|
|
@@ -775,6 +943,7 @@ class InMemoryDb(BaseDb):
|
|
|
775
943
|
|
|
776
944
|
except Exception as e:
|
|
777
945
|
log_error(f"Error deleting eval runs {eval_run_ids}: {e}")
|
|
946
|
+
raise e
|
|
778
947
|
|
|
779
948
|
def get_eval_run(
|
|
780
949
|
self, eval_run_id: str, deserialize: Optional[bool] = True
|
|
@@ -783,15 +952,16 @@ class InMemoryDb(BaseDb):
|
|
|
783
952
|
try:
|
|
784
953
|
for run_data in self._eval_runs:
|
|
785
954
|
if run_data.get("run_id") == eval_run_id:
|
|
955
|
+
run_data_copy = deepcopy(run_data)
|
|
786
956
|
if not deserialize:
|
|
787
|
-
return
|
|
788
|
-
return EvalRunRecord.model_validate(
|
|
957
|
+
return run_data_copy
|
|
958
|
+
return EvalRunRecord.model_validate(run_data_copy)
|
|
789
959
|
|
|
790
960
|
return None
|
|
791
961
|
|
|
792
962
|
except Exception as e:
|
|
793
963
|
log_error(f"Exception getting eval run {eval_run_id}: {e}")
|
|
794
|
-
|
|
964
|
+
raise e
|
|
795
965
|
|
|
796
966
|
def get_eval_runs(
|
|
797
967
|
self,
|
|
@@ -831,7 +1001,7 @@ class InMemoryDb(BaseDb):
|
|
|
831
1001
|
elif filter_type == EvalFilterType.WORKFLOW and run_data.get("workflow_id") is None:
|
|
832
1002
|
continue
|
|
833
1003
|
|
|
834
|
-
filtered_runs.append(run_data)
|
|
1004
|
+
filtered_runs.append(deepcopy(run_data))
|
|
835
1005
|
|
|
836
1006
|
total_count = len(filtered_runs)
|
|
837
1007
|
|
|
@@ -855,7 +1025,7 @@ class InMemoryDb(BaseDb):
|
|
|
855
1025
|
|
|
856
1026
|
except Exception as e:
|
|
857
1027
|
log_error(f"Exception getting eval runs: {e}")
|
|
858
|
-
|
|
1028
|
+
raise e
|
|
859
1029
|
|
|
860
1030
|
def rename_eval_run(
|
|
861
1031
|
self, eval_run_id: str, name: str, deserialize: Optional[bool] = True
|
|
@@ -870,13 +1040,128 @@ class InMemoryDb(BaseDb):
|
|
|
870
1040
|
|
|
871
1041
|
log_debug(f"Renamed eval run with id '{eval_run_id}' to '{name}'")
|
|
872
1042
|
|
|
1043
|
+
run_data_copy = deepcopy(run_data)
|
|
873
1044
|
if not deserialize:
|
|
874
|
-
return
|
|
1045
|
+
return run_data_copy
|
|
875
1046
|
|
|
876
|
-
return EvalRunRecord.model_validate(
|
|
1047
|
+
return EvalRunRecord.model_validate(run_data_copy)
|
|
877
1048
|
|
|
878
1049
|
return None
|
|
879
1050
|
|
|
880
1051
|
except Exception as e:
|
|
881
1052
|
log_error(f"Error renaming eval run {eval_run_id}: {e}")
|
|
1053
|
+
raise e
|
|
1054
|
+
|
|
1055
|
+
# -- Culture methods --
|
|
1056
|
+
|
|
1057
|
+
def clear_cultural_knowledge(self) -> None:
|
|
1058
|
+
"""Delete all cultural knowledge from in-memory storage."""
|
|
1059
|
+
try:
|
|
1060
|
+
self._cultural_knowledge = []
|
|
1061
|
+
except Exception as e:
|
|
1062
|
+
log_error(f"Error clearing cultural knowledge: {e}")
|
|
1063
|
+
raise e
|
|
1064
|
+
|
|
1065
|
+
def delete_cultural_knowledge(self, id: str) -> None:
|
|
1066
|
+
"""Delete a cultural knowledge entry from in-memory storage."""
|
|
1067
|
+
try:
|
|
1068
|
+
self._cultural_knowledge = [ck for ck in self._cultural_knowledge if ck.get("id") != id]
|
|
1069
|
+
except Exception as e:
|
|
1070
|
+
log_error(f"Error deleting cultural knowledge: {e}")
|
|
1071
|
+
raise e
|
|
1072
|
+
|
|
1073
|
+
def get_cultural_knowledge(
|
|
1074
|
+
self, id: str, deserialize: Optional[bool] = True
|
|
1075
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1076
|
+
"""Get a cultural knowledge entry from in-memory storage."""
|
|
1077
|
+
try:
|
|
1078
|
+
for ck_data in self._cultural_knowledge:
|
|
1079
|
+
if ck_data.get("id") == id:
|
|
1080
|
+
ck_data_copy = deepcopy(ck_data)
|
|
1081
|
+
if not deserialize:
|
|
1082
|
+
return ck_data_copy
|
|
1083
|
+
return deserialize_cultural_knowledge_from_db(ck_data_copy)
|
|
882
1084
|
return None
|
|
1085
|
+
except Exception as e:
|
|
1086
|
+
log_error(f"Error getting cultural knowledge: {e}")
|
|
1087
|
+
raise e
|
|
1088
|
+
|
|
1089
|
+
def get_all_cultural_knowledge(
|
|
1090
|
+
self,
|
|
1091
|
+
name: Optional[str] = None,
|
|
1092
|
+
agent_id: Optional[str] = None,
|
|
1093
|
+
team_id: Optional[str] = None,
|
|
1094
|
+
limit: Optional[int] = None,
|
|
1095
|
+
page: Optional[int] = None,
|
|
1096
|
+
sort_by: Optional[str] = None,
|
|
1097
|
+
sort_order: Optional[str] = None,
|
|
1098
|
+
deserialize: Optional[bool] = True,
|
|
1099
|
+
) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
1100
|
+
"""Get all cultural knowledge from in-memory storage."""
|
|
1101
|
+
try:
|
|
1102
|
+
filtered_ck = []
|
|
1103
|
+
for ck_data in self._cultural_knowledge:
|
|
1104
|
+
if name and ck_data.get("name") != name:
|
|
1105
|
+
continue
|
|
1106
|
+
if agent_id and ck_data.get("agent_id") != agent_id:
|
|
1107
|
+
continue
|
|
1108
|
+
if team_id and ck_data.get("team_id") != team_id:
|
|
1109
|
+
continue
|
|
1110
|
+
filtered_ck.append(ck_data)
|
|
1111
|
+
|
|
1112
|
+
# Apply sorting
|
|
1113
|
+
if sort_by:
|
|
1114
|
+
filtered_ck = apply_sorting(filtered_ck, sort_by, sort_order)
|
|
1115
|
+
|
|
1116
|
+
total_count = len(filtered_ck)
|
|
1117
|
+
|
|
1118
|
+
# Apply pagination
|
|
1119
|
+
if limit and page:
|
|
1120
|
+
start = (page - 1) * limit
|
|
1121
|
+
filtered_ck = filtered_ck[start : start + limit]
|
|
1122
|
+
elif limit:
|
|
1123
|
+
filtered_ck = filtered_ck[:limit]
|
|
1124
|
+
|
|
1125
|
+
if not deserialize:
|
|
1126
|
+
return [deepcopy(ck) for ck in filtered_ck], total_count
|
|
1127
|
+
|
|
1128
|
+
return [deserialize_cultural_knowledge_from_db(deepcopy(ck)) for ck in filtered_ck]
|
|
1129
|
+
except Exception as e:
|
|
1130
|
+
log_error(f"Error getting all cultural knowledge: {e}")
|
|
1131
|
+
raise e
|
|
1132
|
+
|
|
1133
|
+
def upsert_cultural_knowledge(
|
|
1134
|
+
self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
|
|
1135
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1136
|
+
"""Upsert a cultural knowledge entry into in-memory storage."""
|
|
1137
|
+
try:
|
|
1138
|
+
if not cultural_knowledge.id:
|
|
1139
|
+
cultural_knowledge.id = str(uuid4())
|
|
1140
|
+
|
|
1141
|
+
# Serialize content, categories, and notes into a dict for DB storage
|
|
1142
|
+
content_dict = serialize_cultural_knowledge_for_db(cultural_knowledge)
|
|
1143
|
+
|
|
1144
|
+
# Create the item dict with serialized content
|
|
1145
|
+
ck_dict = {
|
|
1146
|
+
"id": cultural_knowledge.id,
|
|
1147
|
+
"name": cultural_knowledge.name,
|
|
1148
|
+
"summary": cultural_knowledge.summary,
|
|
1149
|
+
"content": content_dict if content_dict else None,
|
|
1150
|
+
"metadata": cultural_knowledge.metadata,
|
|
1151
|
+
"input": cultural_knowledge.input,
|
|
1152
|
+
"created_at": cultural_knowledge.created_at,
|
|
1153
|
+
"updated_at": int(time.time()),
|
|
1154
|
+
"agent_id": cultural_knowledge.agent_id,
|
|
1155
|
+
"team_id": cultural_knowledge.team_id,
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
# Remove existing entry with same id
|
|
1159
|
+
self._cultural_knowledge = [ck for ck in self._cultural_knowledge if ck.get("id") != cultural_knowledge.id]
|
|
1160
|
+
|
|
1161
|
+
# Add new entry
|
|
1162
|
+
self._cultural_knowledge.append(ck_dict)
|
|
1163
|
+
|
|
1164
|
+
return self.get_cultural_knowledge(cultural_knowledge.id, deserialize=deserialize)
|
|
1165
|
+
except Exception as e:
|
|
1166
|
+
log_error(f"Error upserting cultural knowledge: {e}")
|
|
1167
|
+
raise e
|