agno 2.1.8__py3-none-any.whl → 2.1.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. agno/agent/agent.py +646 -133
  2. agno/culture/__init__.py +3 -0
  3. agno/culture/manager.py +954 -0
  4. agno/db/async_postgres/async_postgres.py +232 -0
  5. agno/db/async_postgres/schemas.py +15 -0
  6. agno/db/async_postgres/utils.py +58 -0
  7. agno/db/base.py +83 -6
  8. agno/db/dynamo/dynamo.py +162 -0
  9. agno/db/dynamo/schemas.py +44 -0
  10. agno/db/dynamo/utils.py +59 -0
  11. agno/db/firestore/firestore.py +231 -0
  12. agno/db/firestore/schemas.py +10 -0
  13. agno/db/firestore/utils.py +96 -0
  14. agno/db/gcs_json/gcs_json_db.py +190 -0
  15. agno/db/gcs_json/utils.py +58 -0
  16. agno/db/in_memory/in_memory_db.py +118 -0
  17. agno/db/in_memory/utils.py +58 -0
  18. agno/db/json/json_db.py +129 -0
  19. agno/db/json/utils.py +58 -0
  20. agno/db/mongo/mongo.py +222 -0
  21. agno/db/mongo/schemas.py +10 -0
  22. agno/db/mongo/utils.py +59 -0
  23. agno/db/mysql/mysql.py +232 -1
  24. agno/db/mysql/schemas.py +14 -0
  25. agno/db/mysql/utils.py +58 -0
  26. agno/db/postgres/postgres.py +242 -0
  27. agno/db/postgres/schemas.py +15 -0
  28. agno/db/postgres/utils.py +58 -0
  29. agno/db/redis/redis.py +181 -0
  30. agno/db/redis/schemas.py +14 -0
  31. agno/db/redis/utils.py +58 -0
  32. agno/db/schemas/__init__.py +2 -1
  33. agno/db/schemas/culture.py +120 -0
  34. agno/db/singlestore/schemas.py +14 -0
  35. agno/db/singlestore/singlestore.py +231 -0
  36. agno/db/singlestore/utils.py +58 -0
  37. agno/db/sqlite/schemas.py +14 -0
  38. agno/db/sqlite/sqlite.py +274 -7
  39. agno/db/sqlite/utils.py +62 -0
  40. agno/db/surrealdb/models.py +51 -1
  41. agno/db/surrealdb/surrealdb.py +154 -0
  42. agno/db/surrealdb/utils.py +61 -1
  43. agno/knowledge/knowledge.py +4 -0
  44. agno/knowledge/reader/field_labeled_csv_reader.py +0 -2
  45. agno/memory/manager.py +28 -11
  46. agno/models/message.py +4 -0
  47. agno/os/app.py +28 -6
  48. agno/team/team.py +9 -9
  49. agno/tools/gmail.py +59 -14
  50. agno/tools/googlecalendar.py +13 -20
  51. agno/workflow/condition.py +31 -9
  52. agno/workflow/router.py +31 -9
  53. {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/METADATA +1 -1
  54. {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/RECORD +57 -54
  55. {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/WHEEL +0 -0
  56. {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/licenses/LICENSE +0 -0
  57. {agno-2.1.8.dist-info → agno-2.1.10.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, {})
@@ -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, session_id: str, session_type: SessionType, session_name: str, deserialize: Optional[bool] = True
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, sessions: List[Session], deserialize: Optional[bool] = True, preserve_updated_at: bool = False
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, memories: List[UserMemory], deserialize: Optional[bool] = True, preserve_updated_at: bool = False
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, session_id: str, session_type: SessionType, session_name: str, deserialize: Optional[bool] = True
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