agno 2.1.9__py3-none-any.whl → 2.2.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 +2048 -1204
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +954 -0
- agno/db/async_postgres/async_postgres.py +232 -0
- agno/db/async_postgres/schemas.py +15 -0
- agno/db/async_postgres/utils.py +58 -0
- agno/db/base.py +83 -6
- agno/db/dynamo/dynamo.py +162 -0
- agno/db/dynamo/schemas.py +44 -0
- agno/db/dynamo/utils.py +59 -0
- agno/db/firestore/firestore.py +231 -0
- agno/db/firestore/schemas.py +10 -0
- agno/db/firestore/utils.py +96 -0
- agno/db/gcs_json/gcs_json_db.py +190 -0
- agno/db/gcs_json/utils.py +58 -0
- agno/db/in_memory/in_memory_db.py +118 -0
- agno/db/in_memory/utils.py +58 -0
- agno/db/json/json_db.py +129 -0
- agno/db/json/utils.py +58 -0
- agno/db/mongo/mongo.py +222 -0
- agno/db/mongo/schemas.py +10 -0
- agno/db/mongo/utils.py +59 -0
- agno/db/mysql/mysql.py +232 -1
- agno/db/mysql/schemas.py +14 -0
- agno/db/mysql/utils.py +58 -0
- agno/db/postgres/postgres.py +242 -0
- agno/db/postgres/schemas.py +15 -0
- agno/db/postgres/utils.py +58 -0
- agno/db/redis/redis.py +181 -0
- agno/db/redis/schemas.py +14 -0
- agno/db/redis/utils.py +58 -0
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/singlestore/schemas.py +14 -0
- agno/db/singlestore/singlestore.py +231 -0
- agno/db/singlestore/utils.py +58 -0
- agno/db/sqlite/schemas.py +14 -0
- agno/db/sqlite/sqlite.py +274 -7
- agno/db/sqlite/utils.py +62 -0
- agno/db/surrealdb/models.py +51 -1
- agno/db/surrealdb/surrealdb.py +154 -0
- agno/db/surrealdb/utils.py +61 -1
- agno/knowledge/reader/field_labeled_csv_reader.py +0 -2
- agno/memory/manager.py +28 -11
- agno/models/anthropic/claude.py +2 -2
- agno/models/message.py +0 -1
- agno/models/ollama/chat.py +7 -2
- agno/os/app.py +29 -7
- agno/os/interfaces/a2a/router.py +2 -2
- agno/os/interfaces/agui/router.py +2 -2
- agno/os/router.py +7 -7
- agno/os/routers/evals/schemas.py +31 -31
- agno/os/routers/health.py +6 -2
- agno/os/routers/knowledge/schemas.py +49 -47
- agno/os/routers/memory/schemas.py +16 -16
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +382 -7
- agno/os/schema.py +254 -231
- agno/os/utils.py +1 -1
- agno/run/agent.py +49 -1
- agno/run/team.py +43 -0
- agno/session/summary.py +45 -13
- agno/session/team.py +90 -5
- agno/team/team.py +1118 -857
- agno/tools/gmail.py +59 -14
- agno/utils/agent.py +372 -0
- agno/utils/events.py +144 -2
- agno/utils/print_response/agent.py +10 -6
- agno/utils/print_response/team.py +6 -4
- agno/utils/print_response/workflow.py +7 -5
- agno/utils/team.py +9 -8
- agno/workflow/condition.py +17 -9
- agno/workflow/loop.py +18 -10
- agno/workflow/parallel.py +14 -6
- agno/workflow/router.py +17 -9
- agno/workflow/step.py +14 -6
- agno/workflow/steps.py +14 -6
- agno/workflow/workflow.py +245 -122
- {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/METADATA +60 -23
- {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/RECORD +83 -79
- {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/WHEEL +0 -0
- {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/top_level.txt +0 -0
|
@@ -9,12 +9,15 @@ from agno.db.async_postgres.utils import (
|
|
|
9
9
|
bulk_upsert_metrics,
|
|
10
10
|
calculate_date_metrics,
|
|
11
11
|
create_schema,
|
|
12
|
+
deserialize_cultural_knowledge,
|
|
12
13
|
fetch_all_sessions_data,
|
|
13
14
|
get_dates_to_calculate_metrics_for,
|
|
14
15
|
is_table_available,
|
|
15
16
|
is_valid_table,
|
|
17
|
+
serialize_cultural_knowledge,
|
|
16
18
|
)
|
|
17
19
|
from agno.db.base import AsyncBaseDb, SessionType
|
|
20
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
18
21
|
from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
|
|
19
22
|
from agno.db.schemas.knowledge import KnowledgeRow
|
|
20
23
|
from agno.db.schemas.memory import UserMemory
|
|
@@ -43,6 +46,7 @@ class AsyncPostgresDb(AsyncBaseDb):
|
|
|
43
46
|
metrics_table: Optional[str] = None,
|
|
44
47
|
eval_table: Optional[str] = None,
|
|
45
48
|
knowledge_table: Optional[str] = None,
|
|
49
|
+
culture_table: Optional[str] = None,
|
|
46
50
|
):
|
|
47
51
|
"""
|
|
48
52
|
Async interface for interacting with a PostgreSQL database.
|
|
@@ -62,6 +66,7 @@ class AsyncPostgresDb(AsyncBaseDb):
|
|
|
62
66
|
metrics_table (Optional[str]): Name of the table to store metrics.
|
|
63
67
|
eval_table (Optional[str]): Name of the table to store evaluation runs data.
|
|
64
68
|
knowledge_table (Optional[str]): Name of the table to store knowledge content.
|
|
69
|
+
culture_table (Optional[str]): Name of the table to store cultural knowledge.
|
|
65
70
|
|
|
66
71
|
Raises:
|
|
67
72
|
ValueError: If neither db_url nor db_engine is provided.
|
|
@@ -74,6 +79,7 @@ class AsyncPostgresDb(AsyncBaseDb):
|
|
|
74
79
|
metrics_table=metrics_table,
|
|
75
80
|
eval_table=eval_table,
|
|
76
81
|
knowledge_table=knowledge_table,
|
|
82
|
+
culture_table=culture_table,
|
|
77
83
|
)
|
|
78
84
|
|
|
79
85
|
_engine: Optional[AsyncEngine] = db_engine
|
|
@@ -212,6 +218,13 @@ class AsyncPostgresDb(AsyncBaseDb):
|
|
|
212
218
|
)
|
|
213
219
|
return self.knowledge_table
|
|
214
220
|
|
|
221
|
+
if table_type == "culture":
|
|
222
|
+
if not hasattr(self, "culture_table"):
|
|
223
|
+
self.culture_table = await self._get_or_create_table(
|
|
224
|
+
table_name=self.culture_table_name, table_type="culture", db_schema=self.db_schema
|
|
225
|
+
)
|
|
226
|
+
return self.culture_table
|
|
227
|
+
|
|
215
228
|
raise ValueError(f"Unknown table type: {table_type}")
|
|
216
229
|
|
|
217
230
|
async def _get_or_create_table(self, table_name: str, table_type: str, db_schema: str) -> Table:
|
|
@@ -886,6 +899,225 @@ class AsyncPostgresDb(AsyncBaseDb):
|
|
|
886
899
|
except Exception as e:
|
|
887
900
|
log_warning(f"Exception deleting all memories: {e}")
|
|
888
901
|
|
|
902
|
+
# -- Cultural Knowledge methods --
|
|
903
|
+
async def clear_cultural_knowledge(self) -> None:
|
|
904
|
+
"""Delete all cultural knowledge from the database.
|
|
905
|
+
|
|
906
|
+
Raises:
|
|
907
|
+
Exception: If an error occurs during deletion.
|
|
908
|
+
"""
|
|
909
|
+
try:
|
|
910
|
+
table = await self._get_table(table_type="culture")
|
|
911
|
+
|
|
912
|
+
async with self.async_session_factory() as sess, sess.begin():
|
|
913
|
+
await sess.execute(table.delete())
|
|
914
|
+
|
|
915
|
+
except Exception as e:
|
|
916
|
+
log_warning(f"Exception deleting all cultural knowledge: {e}")
|
|
917
|
+
|
|
918
|
+
async def delete_cultural_knowledge(self, id: str) -> None:
|
|
919
|
+
"""Delete cultural knowledge by ID.
|
|
920
|
+
|
|
921
|
+
Args:
|
|
922
|
+
id (str): The ID of the cultural knowledge to delete.
|
|
923
|
+
|
|
924
|
+
Raises:
|
|
925
|
+
Exception: If an error occurs during deletion.
|
|
926
|
+
"""
|
|
927
|
+
try:
|
|
928
|
+
table = await self._get_table(table_type="culture")
|
|
929
|
+
|
|
930
|
+
async with self.async_session_factory() as sess, sess.begin():
|
|
931
|
+
stmt = table.delete().where(table.c.id == id)
|
|
932
|
+
await sess.execute(stmt)
|
|
933
|
+
|
|
934
|
+
except Exception as e:
|
|
935
|
+
log_warning(f"Exception deleting cultural knowledge: {e}")
|
|
936
|
+
raise e
|
|
937
|
+
|
|
938
|
+
async def get_cultural_knowledge(
|
|
939
|
+
self, id: str, deserialize: Optional[bool] = True
|
|
940
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
941
|
+
"""Get cultural knowledge by ID.
|
|
942
|
+
|
|
943
|
+
Args:
|
|
944
|
+
id (str): The ID of the cultural knowledge to retrieve.
|
|
945
|
+
deserialize (Optional[bool]): Whether to deserialize to CulturalKnowledge object. Defaults to True.
|
|
946
|
+
|
|
947
|
+
Returns:
|
|
948
|
+
Optional[Union[CulturalKnowledge, Dict[str, Any]]]: The cultural knowledge if found, None otherwise.
|
|
949
|
+
|
|
950
|
+
Raises:
|
|
951
|
+
Exception: If an error occurs during retrieval.
|
|
952
|
+
"""
|
|
953
|
+
try:
|
|
954
|
+
table = await self._get_table(table_type="culture")
|
|
955
|
+
|
|
956
|
+
async with self.async_session_factory() as sess:
|
|
957
|
+
stmt = select(table).where(table.c.id == id)
|
|
958
|
+
result = await sess.execute(stmt)
|
|
959
|
+
row = result.fetchone()
|
|
960
|
+
|
|
961
|
+
if row is None:
|
|
962
|
+
return None
|
|
963
|
+
|
|
964
|
+
db_row = dict(row._mapping)
|
|
965
|
+
|
|
966
|
+
if not deserialize:
|
|
967
|
+
return db_row
|
|
968
|
+
|
|
969
|
+
return deserialize_cultural_knowledge(db_row)
|
|
970
|
+
|
|
971
|
+
except Exception as e:
|
|
972
|
+
log_warning(f"Exception reading cultural knowledge: {e}")
|
|
973
|
+
raise e
|
|
974
|
+
|
|
975
|
+
async def get_all_cultural_knowledge(
|
|
976
|
+
self,
|
|
977
|
+
agent_id: Optional[str] = None,
|
|
978
|
+
team_id: Optional[str] = None,
|
|
979
|
+
name: Optional[str] = None,
|
|
980
|
+
limit: Optional[int] = None,
|
|
981
|
+
page: Optional[int] = None,
|
|
982
|
+
sort_by: Optional[str] = None,
|
|
983
|
+
sort_order: Optional[str] = None,
|
|
984
|
+
deserialize: Optional[bool] = True,
|
|
985
|
+
) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
986
|
+
"""Get all cultural knowledge with filtering and pagination.
|
|
987
|
+
|
|
988
|
+
Args:
|
|
989
|
+
agent_id (Optional[str]): Filter by agent ID.
|
|
990
|
+
team_id (Optional[str]): Filter by team ID.
|
|
991
|
+
name (Optional[str]): Filter by name (case-insensitive partial match).
|
|
992
|
+
limit (Optional[int]): Maximum number of results to return.
|
|
993
|
+
page (Optional[int]): Page number for pagination.
|
|
994
|
+
sort_by (Optional[str]): Field to sort by.
|
|
995
|
+
sort_order (Optional[str]): Sort order ('asc' or 'desc').
|
|
996
|
+
deserialize (Optional[bool]): Whether to deserialize to CulturalKnowledge objects. Defaults to True.
|
|
997
|
+
|
|
998
|
+
Returns:
|
|
999
|
+
Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
1000
|
+
- When deserialize=True: List of CulturalKnowledge objects
|
|
1001
|
+
- When deserialize=False: Tuple with list of dictionaries and total count
|
|
1002
|
+
|
|
1003
|
+
Raises:
|
|
1004
|
+
Exception: If an error occurs during retrieval.
|
|
1005
|
+
"""
|
|
1006
|
+
try:
|
|
1007
|
+
table = await self._get_table(table_type="culture")
|
|
1008
|
+
|
|
1009
|
+
async with self.async_session_factory() as sess:
|
|
1010
|
+
# Build query with filters
|
|
1011
|
+
stmt = select(table)
|
|
1012
|
+
if agent_id is not None:
|
|
1013
|
+
stmt = stmt.where(table.c.agent_id == agent_id)
|
|
1014
|
+
if team_id is not None:
|
|
1015
|
+
stmt = stmt.where(table.c.team_id == team_id)
|
|
1016
|
+
if name is not None:
|
|
1017
|
+
stmt = stmt.where(table.c.name.ilike(f"%{name}%"))
|
|
1018
|
+
|
|
1019
|
+
# Get total count
|
|
1020
|
+
count_stmt = select(func.count()).select_from(stmt.alias())
|
|
1021
|
+
total_count_result = await sess.execute(count_stmt)
|
|
1022
|
+
total_count = total_count_result.scalar() or 0
|
|
1023
|
+
|
|
1024
|
+
# Apply sorting
|
|
1025
|
+
stmt = apply_sorting(stmt, table, sort_by, sort_order)
|
|
1026
|
+
|
|
1027
|
+
# Apply pagination
|
|
1028
|
+
if limit is not None:
|
|
1029
|
+
stmt = stmt.limit(limit)
|
|
1030
|
+
if page is not None:
|
|
1031
|
+
stmt = stmt.offset((page - 1) * limit)
|
|
1032
|
+
|
|
1033
|
+
# Execute query
|
|
1034
|
+
result = await sess.execute(stmt)
|
|
1035
|
+
rows = result.fetchall()
|
|
1036
|
+
|
|
1037
|
+
db_rows = [dict(row._mapping) for row in rows]
|
|
1038
|
+
|
|
1039
|
+
if not deserialize:
|
|
1040
|
+
return db_rows, total_count
|
|
1041
|
+
|
|
1042
|
+
return [deserialize_cultural_knowledge(row) for row in db_rows]
|
|
1043
|
+
|
|
1044
|
+
except Exception as e:
|
|
1045
|
+
log_warning(f"Exception reading all cultural knowledge: {e}")
|
|
1046
|
+
raise e
|
|
1047
|
+
|
|
1048
|
+
async def upsert_cultural_knowledge(
|
|
1049
|
+
self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
|
|
1050
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1051
|
+
"""Upsert cultural knowledge in the database.
|
|
1052
|
+
|
|
1053
|
+
Args:
|
|
1054
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge to upsert.
|
|
1055
|
+
deserialize (Optional[bool]): Whether to deserialize the result. Defaults to True.
|
|
1056
|
+
|
|
1057
|
+
Returns:
|
|
1058
|
+
Optional[Union[CulturalKnowledge, Dict[str, Any]]]: The upserted cultural knowledge.
|
|
1059
|
+
|
|
1060
|
+
Raises:
|
|
1061
|
+
Exception: If an error occurs during upsert.
|
|
1062
|
+
"""
|
|
1063
|
+
try:
|
|
1064
|
+
table = await self._get_table(table_type="culture")
|
|
1065
|
+
|
|
1066
|
+
# Generate ID if not present
|
|
1067
|
+
if cultural_knowledge.id is None:
|
|
1068
|
+
cultural_knowledge.id = str(uuid4())
|
|
1069
|
+
|
|
1070
|
+
# Serialize content, categories, and notes into a JSON dict for DB storage
|
|
1071
|
+
content_dict = serialize_cultural_knowledge(cultural_knowledge)
|
|
1072
|
+
|
|
1073
|
+
async with self.async_session_factory() as sess, sess.begin():
|
|
1074
|
+
# Use PostgreSQL-specific insert with on_conflict_do_update
|
|
1075
|
+
insert_stmt = postgresql.insert(table).values(
|
|
1076
|
+
id=cultural_knowledge.id,
|
|
1077
|
+
name=cultural_knowledge.name,
|
|
1078
|
+
summary=cultural_knowledge.summary,
|
|
1079
|
+
content=content_dict if content_dict else None,
|
|
1080
|
+
metadata=cultural_knowledge.metadata,
|
|
1081
|
+
input=cultural_knowledge.input,
|
|
1082
|
+
created_at=cultural_knowledge.created_at,
|
|
1083
|
+
updated_at=int(time.time()),
|
|
1084
|
+
agent_id=cultural_knowledge.agent_id,
|
|
1085
|
+
team_id=cultural_knowledge.team_id,
|
|
1086
|
+
)
|
|
1087
|
+
|
|
1088
|
+
# Update all fields except id on conflict
|
|
1089
|
+
update_dict = {
|
|
1090
|
+
"name": cultural_knowledge.name,
|
|
1091
|
+
"summary": cultural_knowledge.summary,
|
|
1092
|
+
"content": content_dict if content_dict else None,
|
|
1093
|
+
"metadata": cultural_knowledge.metadata,
|
|
1094
|
+
"input": cultural_knowledge.input,
|
|
1095
|
+
"updated_at": int(time.time()),
|
|
1096
|
+
"agent_id": cultural_knowledge.agent_id,
|
|
1097
|
+
"team_id": cultural_knowledge.team_id,
|
|
1098
|
+
}
|
|
1099
|
+
upsert_stmt = insert_stmt.on_conflict_do_update(index_elements=["id"], set_=update_dict).returning(
|
|
1100
|
+
table
|
|
1101
|
+
)
|
|
1102
|
+
|
|
1103
|
+
result = await sess.execute(upsert_stmt)
|
|
1104
|
+
row = result.fetchone()
|
|
1105
|
+
|
|
1106
|
+
if row is None:
|
|
1107
|
+
return None
|
|
1108
|
+
|
|
1109
|
+
db_row = dict(row._mapping)
|
|
1110
|
+
|
|
1111
|
+
if not deserialize:
|
|
1112
|
+
return db_row
|
|
1113
|
+
|
|
1114
|
+
# Deserialize from DB format to model format
|
|
1115
|
+
return deserialize_cultural_knowledge(db_row)
|
|
1116
|
+
|
|
1117
|
+
except Exception as e:
|
|
1118
|
+
log_warning(f"Exception upserting cultural knowledge: {e}")
|
|
1119
|
+
raise e
|
|
1120
|
+
|
|
889
1121
|
async def get_user_memory_stats(
|
|
890
1122
|
self, limit: Optional[int] = None, page: Optional[int] = None
|
|
891
1123
|
) -> Tuple[List[Dict[str, Any]], int]:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
try:
|
|
6
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
6
7
|
from sqlalchemy.types import JSON, BigInteger, Boolean, Date, String
|
|
7
8
|
except ImportError:
|
|
8
9
|
raise ImportError("`sqlalchemy` not installed. Please install it using `pip install sqlalchemy`")
|
|
@@ -98,6 +99,19 @@ METRICS_TABLE_SCHEMA = {
|
|
|
98
99
|
],
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
CULTURAL_KNOWLEDGE_TABLE_SCHEMA = {
|
|
103
|
+
"id": {"type": String, "primary_key": True, "nullable": False},
|
|
104
|
+
"name": {"type": String, "nullable": False, "index": True},
|
|
105
|
+
"summary": {"type": String, "nullable": True},
|
|
106
|
+
"content": {"type": JSONB, "nullable": True},
|
|
107
|
+
"metadata": {"type": JSONB, "nullable": True},
|
|
108
|
+
"input": {"type": String, "nullable": True},
|
|
109
|
+
"created_at": {"type": BigInteger, "nullable": True},
|
|
110
|
+
"updated_at": {"type": BigInteger, "nullable": True},
|
|
111
|
+
"agent_id": {"type": String, "nullable": True, "index": True},
|
|
112
|
+
"team_id": {"type": String, "nullable": True, "index": True},
|
|
113
|
+
}
|
|
114
|
+
|
|
101
115
|
|
|
102
116
|
def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
103
117
|
"""
|
|
@@ -115,6 +129,7 @@ def get_table_schema_definition(table_type: str) -> dict[str, Any]:
|
|
|
115
129
|
"metrics": METRICS_TABLE_SCHEMA,
|
|
116
130
|
"memories": MEMORY_TABLE_SCHEMA,
|
|
117
131
|
"knowledge": KNOWLEDGE_TABLE_SCHEMA,
|
|
132
|
+
"culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
schema = schemas.get(table_type, {})
|
agno/db/async_postgres/utils.py
CHANGED
|
@@ -8,6 +8,7 @@ from uuid import uuid4
|
|
|
8
8
|
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
9
9
|
|
|
10
10
|
from agno.db.async_postgres.schemas import get_table_schema_definition
|
|
11
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
11
12
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
12
13
|
|
|
13
14
|
try:
|
|
@@ -287,3 +288,60 @@ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
|
287
288
|
if days_diff <= 0:
|
|
288
289
|
return []
|
|
289
290
|
return [starting_date + timedelta(days=x) for x in range(days_diff)]
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# -- Cultural Knowledge util methods --
|
|
294
|
+
def serialize_cultural_knowledge(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
|
|
295
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
296
|
+
|
|
297
|
+
Converts the model's separate content, categories, and notes fields
|
|
298
|
+
into a single JSON dict for the database content column.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
Dict[str, Any]: A dictionary with the content field as JSON containing content, categories, and notes.
|
|
305
|
+
"""
|
|
306
|
+
content_dict: Dict[str, Any] = {}
|
|
307
|
+
if cultural_knowledge.content is not None:
|
|
308
|
+
content_dict["content"] = cultural_knowledge.content
|
|
309
|
+
if cultural_knowledge.categories is not None:
|
|
310
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
311
|
+
if cultural_knowledge.notes is not None:
|
|
312
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
313
|
+
|
|
314
|
+
return content_dict if content_dict else {}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def deserialize_cultural_knowledge(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
318
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
319
|
+
|
|
320
|
+
The database stores content as a JSON dict containing content, categories, and notes.
|
|
321
|
+
This method extracts those fields and converts them back to the model format.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
328
|
+
"""
|
|
329
|
+
# Extract content, categories, and notes from the JSON content field
|
|
330
|
+
content_json = db_row.get("content", {}) or {}
|
|
331
|
+
|
|
332
|
+
return CulturalKnowledge.from_dict(
|
|
333
|
+
{
|
|
334
|
+
"id": db_row.get("id"),
|
|
335
|
+
"name": db_row.get("name"),
|
|
336
|
+
"summary": db_row.get("summary"),
|
|
337
|
+
"content": content_json.get("content"),
|
|
338
|
+
"categories": content_json.get("categories"),
|
|
339
|
+
"notes": content_json.get("notes"),
|
|
340
|
+
"metadata": db_row.get("metadata"),
|
|
341
|
+
"input": db_row.get("input"),
|
|
342
|
+
"created_at": db_row.get("created_at"),
|
|
343
|
+
"updated_at": db_row.get("updated_at"),
|
|
344
|
+
"agent_id": db_row.get("agent_id"),
|
|
345
|
+
"team_id": db_row.get("team_id"),
|
|
346
|
+
}
|
|
347
|
+
)
|
agno/db/base.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from agno.db.schemas import UserMemory
|
|
8
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
8
9
|
from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
|
|
9
10
|
from agno.db.schemas.knowledge import KnowledgeRow
|
|
10
11
|
from agno.session import Session
|
|
@@ -22,6 +23,7 @@ class BaseDb(ABC):
|
|
|
22
23
|
def __init__(
|
|
23
24
|
self,
|
|
24
25
|
session_table: Optional[str] = None,
|
|
26
|
+
culture_table: Optional[str] = None,
|
|
25
27
|
memory_table: Optional[str] = None,
|
|
26
28
|
metrics_table: Optional[str] = None,
|
|
27
29
|
eval_table: Optional[str] = None,
|
|
@@ -30,6 +32,7 @@ class BaseDb(ABC):
|
|
|
30
32
|
):
|
|
31
33
|
self.id = id or str(uuid4())
|
|
32
34
|
self.session_table_name = session_table or "agno_sessions"
|
|
35
|
+
self.culture_table_name = culture_table or "agno_culture"
|
|
33
36
|
self.memory_table_name = memory_table or "agno_memories"
|
|
34
37
|
self.metrics_table_name = metrics_table or "agno_metrics"
|
|
35
38
|
self.eval_table_name = eval_table or "agno_eval_runs"
|
|
@@ -73,7 +76,11 @@ class BaseDb(ABC):
|
|
|
73
76
|
|
|
74
77
|
@abstractmethod
|
|
75
78
|
def rename_session(
|
|
76
|
-
self,
|
|
79
|
+
self,
|
|
80
|
+
session_id: str,
|
|
81
|
+
session_type: SessionType,
|
|
82
|
+
session_name: str,
|
|
83
|
+
deserialize: Optional[bool] = True,
|
|
77
84
|
) -> Optional[Union[Session, Dict[str, Any]]]:
|
|
78
85
|
raise NotImplementedError
|
|
79
86
|
|
|
@@ -85,13 +92,15 @@ class BaseDb(ABC):
|
|
|
85
92
|
|
|
86
93
|
@abstractmethod
|
|
87
94
|
def upsert_sessions(
|
|
88
|
-
self,
|
|
95
|
+
self,
|
|
96
|
+
sessions: List[Session],
|
|
97
|
+
deserialize: Optional[bool] = True,
|
|
98
|
+
preserve_updated_at: bool = False,
|
|
89
99
|
) -> List[Union[Session, Dict[str, Any]]]:
|
|
90
100
|
"""Bulk upsert multiple sessions for improved performance on large datasets."""
|
|
91
101
|
raise NotImplementedError
|
|
92
102
|
|
|
93
103
|
# --- Memory ---
|
|
94
|
-
|
|
95
104
|
@abstractmethod
|
|
96
105
|
def clear_memories(self) -> None:
|
|
97
106
|
raise NotImplementedError
|
|
@@ -150,7 +159,10 @@ class BaseDb(ABC):
|
|
|
150
159
|
|
|
151
160
|
@abstractmethod
|
|
152
161
|
def upsert_memories(
|
|
153
|
-
self,
|
|
162
|
+
self,
|
|
163
|
+
memories: List[UserMemory],
|
|
164
|
+
deserialize: Optional[bool] = True,
|
|
165
|
+
preserve_updated_at: bool = False,
|
|
154
166
|
) -> List[Union[UserMemory, Dict[str, Any]]]:
|
|
155
167
|
"""Bulk upsert multiple memories for improved performance on large datasets."""
|
|
156
168
|
raise NotImplementedError
|
|
@@ -264,6 +276,36 @@ class BaseDb(ABC):
|
|
|
264
276
|
) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
|
|
265
277
|
raise NotImplementedError
|
|
266
278
|
|
|
279
|
+
# --- Cultural Knowledge ---
|
|
280
|
+
@abstractmethod
|
|
281
|
+
def clear_cultural_knowledge(self) -> None:
|
|
282
|
+
raise NotImplementedError
|
|
283
|
+
|
|
284
|
+
@abstractmethod
|
|
285
|
+
def delete_cultural_knowledge(self, id: str) -> None:
|
|
286
|
+
raise NotImplementedError
|
|
287
|
+
|
|
288
|
+
@abstractmethod
|
|
289
|
+
def get_cultural_knowledge(self, id: str) -> Optional[CulturalKnowledge]:
|
|
290
|
+
raise NotImplementedError
|
|
291
|
+
|
|
292
|
+
@abstractmethod
|
|
293
|
+
def get_all_cultural_knowledge(
|
|
294
|
+
self,
|
|
295
|
+
name: Optional[str] = None,
|
|
296
|
+
limit: Optional[int] = None,
|
|
297
|
+
page: Optional[int] = None,
|
|
298
|
+
sort_by: Optional[str] = None,
|
|
299
|
+
sort_order: Optional[str] = None,
|
|
300
|
+
agent_id: Optional[str] = None,
|
|
301
|
+
team_id: Optional[str] = None,
|
|
302
|
+
) -> Optional[List[CulturalKnowledge]]:
|
|
303
|
+
raise NotImplementedError
|
|
304
|
+
|
|
305
|
+
@abstractmethod
|
|
306
|
+
def upsert_cultural_knowledge(self, cultural_knowledge: CulturalKnowledge) -> Optional[CulturalKnowledge]:
|
|
307
|
+
raise NotImplementedError
|
|
308
|
+
|
|
267
309
|
|
|
268
310
|
class AsyncBaseDb(ABC):
|
|
269
311
|
"""Base abstract class for all our async database implementations."""
|
|
@@ -276,6 +318,7 @@ class AsyncBaseDb(ABC):
|
|
|
276
318
|
metrics_table: Optional[str] = None,
|
|
277
319
|
eval_table: Optional[str] = None,
|
|
278
320
|
knowledge_table: Optional[str] = None,
|
|
321
|
+
culture_table: Optional[str] = None,
|
|
279
322
|
):
|
|
280
323
|
self.id = id or str(uuid4())
|
|
281
324
|
self.session_table_name = session_table or "agno_sessions"
|
|
@@ -283,6 +326,7 @@ class AsyncBaseDb(ABC):
|
|
|
283
326
|
self.metrics_table_name = metrics_table or "agno_metrics"
|
|
284
327
|
self.eval_table_name = eval_table or "agno_eval_runs"
|
|
285
328
|
self.knowledge_table_name = knowledge_table or "agno_knowledge"
|
|
329
|
+
self.culture_table_name = culture_table or "agno_culture"
|
|
286
330
|
|
|
287
331
|
# --- Sessions ---
|
|
288
332
|
@abstractmethod
|
|
@@ -322,7 +366,11 @@ class AsyncBaseDb(ABC):
|
|
|
322
366
|
|
|
323
367
|
@abstractmethod
|
|
324
368
|
async def rename_session(
|
|
325
|
-
self,
|
|
369
|
+
self,
|
|
370
|
+
session_id: str,
|
|
371
|
+
session_type: SessionType,
|
|
372
|
+
session_name: str,
|
|
373
|
+
deserialize: Optional[bool] = True,
|
|
326
374
|
) -> Optional[Union[Session, Dict[str, Any]]]:
|
|
327
375
|
raise NotImplementedError
|
|
328
376
|
|
|
@@ -333,7 +381,6 @@ class AsyncBaseDb(ABC):
|
|
|
333
381
|
raise NotImplementedError
|
|
334
382
|
|
|
335
383
|
# --- Memory ---
|
|
336
|
-
|
|
337
384
|
@abstractmethod
|
|
338
385
|
async def clear_memories(self) -> None:
|
|
339
386
|
raise NotImplementedError
|
|
@@ -496,3 +543,33 @@ class AsyncBaseDb(ABC):
|
|
|
496
543
|
self, eval_run_id: str, name: str, deserialize: Optional[bool] = True
|
|
497
544
|
) -> Optional[Union[EvalRunRecord, Dict[str, Any]]]:
|
|
498
545
|
raise NotImplementedError
|
|
546
|
+
|
|
547
|
+
# --- Cultural Notions ---
|
|
548
|
+
@abstractmethod
|
|
549
|
+
async def clear_cultural_knowledge(self) -> None:
|
|
550
|
+
raise NotImplementedError
|
|
551
|
+
|
|
552
|
+
@abstractmethod
|
|
553
|
+
async def delete_cultural_knowledge(self, id: str) -> None:
|
|
554
|
+
raise NotImplementedError
|
|
555
|
+
|
|
556
|
+
@abstractmethod
|
|
557
|
+
async def get_cultural_knowledge(self, id: str) -> Optional[CulturalKnowledge]:
|
|
558
|
+
raise NotImplementedError
|
|
559
|
+
|
|
560
|
+
@abstractmethod
|
|
561
|
+
async def get_all_cultural_knowledge(
|
|
562
|
+
self,
|
|
563
|
+
name: Optional[str] = None,
|
|
564
|
+
limit: Optional[int] = None,
|
|
565
|
+
page: Optional[int] = None,
|
|
566
|
+
sort_by: Optional[str] = None,
|
|
567
|
+
sort_order: Optional[str] = None,
|
|
568
|
+
agent_id: Optional[str] = None,
|
|
569
|
+
team_id: Optional[str] = None,
|
|
570
|
+
) -> Optional[List[CulturalKnowledge]]:
|
|
571
|
+
raise NotImplementedError
|
|
572
|
+
|
|
573
|
+
@abstractmethod
|
|
574
|
+
async def upsert_cultural_knowledge(self, cultural_knowledge: CulturalKnowledge) -> Optional[CulturalKnowledge]:
|
|
575
|
+
raise NotImplementedError
|