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.
- agno/agent/agent.py +646 -133
- 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/knowledge.py +4 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +0 -2
- agno/memory/manager.py +28 -11
- agno/models/message.py +4 -0
- agno/os/app.py +28 -6
- agno/team/team.py +9 -9
- agno/tools/gmail.py +59 -14
- agno/tools/googlecalendar.py +13 -20
- agno/workflow/condition.py +31 -9
- agno/workflow/router.py +31 -9
- {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/METADATA +1 -1
- {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/RECORD +57 -54
- {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/WHEEL +0 -0
- {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.8.dist-info → agno-2.1.10.dist-info}/top_level.txt +0 -0
agno/db/sqlite/sqlite.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional, Sequence, Tuple, Union, cast
|
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from agno.db.base import BaseDb, SessionType
|
|
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.db.schemas.memory import UserMemory
|
|
@@ -13,10 +14,12 @@ from agno.db.sqlite.utils import (
|
|
|
13
14
|
apply_sorting,
|
|
14
15
|
bulk_upsert_metrics,
|
|
15
16
|
calculate_date_metrics,
|
|
17
|
+
deserialize_cultural_knowledge_from_db,
|
|
16
18
|
fetch_all_sessions_data,
|
|
17
19
|
get_dates_to_calculate_metrics_for,
|
|
18
20
|
is_table_available,
|
|
19
21
|
is_valid_table,
|
|
22
|
+
serialize_cultural_knowledge_for_db,
|
|
20
23
|
)
|
|
21
24
|
from agno.db.utils import deserialize_session_json_fields, serialize_session_json_fields
|
|
22
25
|
from agno.session import AgentSession, Session, TeamSession, WorkflowSession
|
|
@@ -40,6 +43,7 @@ class SqliteDb(BaseDb):
|
|
|
40
43
|
db_engine: Optional[Engine] = None,
|
|
41
44
|
db_url: Optional[str] = None,
|
|
42
45
|
session_table: Optional[str] = None,
|
|
46
|
+
culture_table: Optional[str] = None,
|
|
43
47
|
memory_table: Optional[str] = None,
|
|
44
48
|
metrics_table: Optional[str] = None,
|
|
45
49
|
eval_table: Optional[str] = None,
|
|
@@ -60,6 +64,7 @@ class SqliteDb(BaseDb):
|
|
|
60
64
|
db_engine (Optional[Engine]): The SQLAlchemy database engine to use.
|
|
61
65
|
db_url (Optional[str]): The database URL to connect to.
|
|
62
66
|
session_table (Optional[str]): Name of the table to store Agent, Team and Workflow sessions.
|
|
67
|
+
culture_table (Optional[str]): Name of the table to store cultural notions.
|
|
63
68
|
memory_table (Optional[str]): Name of the table to store user memories.
|
|
64
69
|
metrics_table (Optional[str]): Name of the table to store metrics.
|
|
65
70
|
eval_table (Optional[str]): Name of the table to store evaluation runs data.
|
|
@@ -76,6 +81,7 @@ class SqliteDb(BaseDb):
|
|
|
76
81
|
super().__init__(
|
|
77
82
|
id=id,
|
|
78
83
|
session_table=session_table,
|
|
84
|
+
culture_table=culture_table,
|
|
79
85
|
memory_table=memory_table,
|
|
80
86
|
metrics_table=metrics_table,
|
|
81
87
|
eval_table=eval_table,
|
|
@@ -229,11 +235,22 @@ class SqliteDb(BaseDb):
|
|
|
229
235
|
)
|
|
230
236
|
return self.knowledge_table
|
|
231
237
|
|
|
238
|
+
elif table_type == "culture":
|
|
239
|
+
self.culture_table = self._get_or_create_table(
|
|
240
|
+
table_name=self.culture_table_name,
|
|
241
|
+
table_type="culture",
|
|
242
|
+
create_table_if_not_found=create_table_if_not_found,
|
|
243
|
+
)
|
|
244
|
+
return self.culture_table
|
|
245
|
+
|
|
232
246
|
else:
|
|
233
247
|
raise ValueError(f"Unknown table type: '{table_type}'")
|
|
234
248
|
|
|
235
249
|
def _get_or_create_table(
|
|
236
|
-
self,
|
|
250
|
+
self,
|
|
251
|
+
table_name: str,
|
|
252
|
+
table_type: str,
|
|
253
|
+
create_table_if_not_found: Optional[bool] = False,
|
|
237
254
|
) -> Optional[Table]:
|
|
238
255
|
"""
|
|
239
256
|
Check if the table exists and is valid, else create it.
|
|
@@ -483,7 +500,11 @@ class SqliteDb(BaseDb):
|
|
|
483
500
|
raise e
|
|
484
501
|
|
|
485
502
|
def rename_session(
|
|
486
|
-
self,
|
|
503
|
+
self,
|
|
504
|
+
session_id: str,
|
|
505
|
+
session_type: SessionType,
|
|
506
|
+
session_name: str,
|
|
507
|
+
deserialize: Optional[bool] = True,
|
|
487
508
|
) -> Optional[Union[Session, Dict[str, Any]]]:
|
|
488
509
|
"""
|
|
489
510
|
Rename a session in the database.
|
|
@@ -664,7 +685,10 @@ class SqliteDb(BaseDb):
|
|
|
664
685
|
raise e
|
|
665
686
|
|
|
666
687
|
def upsert_sessions(
|
|
667
|
-
self,
|
|
688
|
+
self,
|
|
689
|
+
sessions: List[Session],
|
|
690
|
+
deserialize: Optional[bool] = True,
|
|
691
|
+
preserve_updated_at: bool = False,
|
|
668
692
|
) -> List[Union[Session, Dict[str, Any]]]:
|
|
669
693
|
"""
|
|
670
694
|
Bulk upsert multiple sessions for improved performance on large datasets.
|
|
@@ -975,7 +999,10 @@ class SqliteDb(BaseDb):
|
|
|
975
999
|
raise e
|
|
976
1000
|
|
|
977
1001
|
def get_user_memory(
|
|
978
|
-
self,
|
|
1002
|
+
self,
|
|
1003
|
+
memory_id: str,
|
|
1004
|
+
deserialize: Optional[bool] = True,
|
|
1005
|
+
user_id: Optional[str] = None,
|
|
979
1006
|
) -> Optional[Union[UserMemory, Dict[str, Any]]]:
|
|
980
1007
|
"""Get a memory from the database.
|
|
981
1008
|
|
|
@@ -1231,7 +1258,10 @@ class SqliteDb(BaseDb):
|
|
|
1231
1258
|
raise e
|
|
1232
1259
|
|
|
1233
1260
|
def upsert_memories(
|
|
1234
|
-
self,
|
|
1261
|
+
self,
|
|
1262
|
+
memories: List[UserMemory],
|
|
1263
|
+
deserialize: Optional[bool] = True,
|
|
1264
|
+
preserve_updated_at: bool = False,
|
|
1235
1265
|
) -> List[Union[UserMemory, Dict[str, Any]]]:
|
|
1236
1266
|
"""
|
|
1237
1267
|
Bulk upsert multiple user memories for improved performance on large datasets.
|
|
@@ -1460,7 +1490,9 @@ class SqliteDb(BaseDb):
|
|
|
1460
1490
|
start_timestamp=start_timestamp, end_timestamp=end_timestamp
|
|
1461
1491
|
)
|
|
1462
1492
|
all_sessions_data = fetch_all_sessions_data(
|
|
1463
|
-
sessions=sessions,
|
|
1493
|
+
sessions=sessions,
|
|
1494
|
+
dates_to_process=dates_to_process,
|
|
1495
|
+
start_timestamp=start_timestamp,
|
|
1464
1496
|
)
|
|
1465
1497
|
if not all_sessions_data:
|
|
1466
1498
|
log_info("No new session data found. Won't calculate metrics.")
|
|
@@ -1707,7 +1739,11 @@ class SqliteDb(BaseDb):
|
|
|
1707
1739
|
with self.Session() as sess, sess.begin():
|
|
1708
1740
|
current_time = int(time.time())
|
|
1709
1741
|
stmt = sqlite.insert(table).values(
|
|
1710
|
-
{
|
|
1742
|
+
{
|
|
1743
|
+
"created_at": current_time,
|
|
1744
|
+
"updated_at": current_time,
|
|
1745
|
+
**eval_run.model_dump(),
|
|
1746
|
+
}
|
|
1711
1747
|
)
|
|
1712
1748
|
sess.execute(stmt)
|
|
1713
1749
|
sess.commit()
|
|
@@ -1997,3 +2033,234 @@ class SqliteDb(BaseDb):
|
|
|
1997
2033
|
for memory in memories:
|
|
1998
2034
|
self.upsert_user_memory(memory)
|
|
1999
2035
|
log_info(f"Migrated {len(memories)} memories to table: {self.memory_table}")
|
|
2036
|
+
|
|
2037
|
+
# -- Culture methods --
|
|
2038
|
+
|
|
2039
|
+
def clear_cultural_knowledge(self) -> None:
|
|
2040
|
+
"""Delete all cultural artifacts from the database.
|
|
2041
|
+
|
|
2042
|
+
Raises:
|
|
2043
|
+
Exception: If an error occurs during deletion.
|
|
2044
|
+
"""
|
|
2045
|
+
try:
|
|
2046
|
+
table = self._get_table(table_type="culture")
|
|
2047
|
+
if table is None:
|
|
2048
|
+
return
|
|
2049
|
+
|
|
2050
|
+
with self.Session() as sess, sess.begin():
|
|
2051
|
+
sess.execute(table.delete())
|
|
2052
|
+
|
|
2053
|
+
except Exception as e:
|
|
2054
|
+
from agno.utils.log import log_warning
|
|
2055
|
+
|
|
2056
|
+
log_warning(f"Exception deleting all cultural artifacts: {e}")
|
|
2057
|
+
raise e
|
|
2058
|
+
|
|
2059
|
+
def delete_cultural_knowledge(self, id: str) -> None:
|
|
2060
|
+
"""Delete a cultural artifact from the database.
|
|
2061
|
+
|
|
2062
|
+
Args:
|
|
2063
|
+
id (str): The ID of the cultural artifact to delete.
|
|
2064
|
+
|
|
2065
|
+
Raises:
|
|
2066
|
+
Exception: If an error occurs during deletion.
|
|
2067
|
+
"""
|
|
2068
|
+
try:
|
|
2069
|
+
table = self._get_table(table_type="culture")
|
|
2070
|
+
if table is None:
|
|
2071
|
+
return
|
|
2072
|
+
|
|
2073
|
+
with self.Session() as sess, sess.begin():
|
|
2074
|
+
delete_stmt = table.delete().where(table.c.id == id)
|
|
2075
|
+
result = sess.execute(delete_stmt)
|
|
2076
|
+
|
|
2077
|
+
success = result.rowcount > 0
|
|
2078
|
+
if success:
|
|
2079
|
+
log_debug(f"Successfully deleted cultural artifact id: {id}")
|
|
2080
|
+
else:
|
|
2081
|
+
log_debug(f"No cultural artifact found with id: {id}")
|
|
2082
|
+
|
|
2083
|
+
except Exception as e:
|
|
2084
|
+
log_error(f"Error deleting cultural artifact: {e}")
|
|
2085
|
+
raise e
|
|
2086
|
+
|
|
2087
|
+
def get_cultural_knowledge(
|
|
2088
|
+
self, id: str, deserialize: Optional[bool] = True
|
|
2089
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
2090
|
+
"""Get a cultural artifact from the database.
|
|
2091
|
+
|
|
2092
|
+
Args:
|
|
2093
|
+
id (str): The ID of the cultural artifact to get.
|
|
2094
|
+
deserialize (Optional[bool]): Whether to serialize the cultural artifact. Defaults to True.
|
|
2095
|
+
|
|
2096
|
+
Returns:
|
|
2097
|
+
Optional[CulturalKnowledge]: The cultural artifact, or None if it doesn't exist.
|
|
2098
|
+
|
|
2099
|
+
Raises:
|
|
2100
|
+
Exception: If an error occurs during retrieval.
|
|
2101
|
+
"""
|
|
2102
|
+
try:
|
|
2103
|
+
table = self._get_table(table_type="culture")
|
|
2104
|
+
if table is None:
|
|
2105
|
+
return None
|
|
2106
|
+
|
|
2107
|
+
with self.Session() as sess, sess.begin():
|
|
2108
|
+
stmt = select(table).where(table.c.id == id)
|
|
2109
|
+
result = sess.execute(stmt).fetchone()
|
|
2110
|
+
if result is None:
|
|
2111
|
+
return None
|
|
2112
|
+
|
|
2113
|
+
db_row = dict(result._mapping)
|
|
2114
|
+
if not db_row or not deserialize:
|
|
2115
|
+
return db_row
|
|
2116
|
+
|
|
2117
|
+
return deserialize_cultural_knowledge_from_db(db_row)
|
|
2118
|
+
|
|
2119
|
+
except Exception as e:
|
|
2120
|
+
log_error(f"Exception reading from cultural artifacts table: {e}")
|
|
2121
|
+
raise e
|
|
2122
|
+
|
|
2123
|
+
def get_all_cultural_knowledge(
|
|
2124
|
+
self,
|
|
2125
|
+
name: Optional[str] = None,
|
|
2126
|
+
agent_id: Optional[str] = None,
|
|
2127
|
+
team_id: Optional[str] = None,
|
|
2128
|
+
limit: Optional[int] = None,
|
|
2129
|
+
page: Optional[int] = None,
|
|
2130
|
+
sort_by: Optional[str] = None,
|
|
2131
|
+
sort_order: Optional[str] = None,
|
|
2132
|
+
deserialize: Optional[bool] = True,
|
|
2133
|
+
) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
2134
|
+
"""Get all cultural artifacts from the database as CulturalNotion objects.
|
|
2135
|
+
|
|
2136
|
+
Args:
|
|
2137
|
+
name (Optional[str]): The name of the cultural artifact to filter by.
|
|
2138
|
+
agent_id (Optional[str]): The ID of the agent to filter by.
|
|
2139
|
+
team_id (Optional[str]): The ID of the team to filter by.
|
|
2140
|
+
limit (Optional[int]): The maximum number of cultural artifacts to return.
|
|
2141
|
+
page (Optional[int]): The page number.
|
|
2142
|
+
sort_by (Optional[str]): The column to sort by.
|
|
2143
|
+
sort_order (Optional[str]): The order to sort by.
|
|
2144
|
+
deserialize (Optional[bool]): Whether to serialize the cultural artifacts. Defaults to True.
|
|
2145
|
+
|
|
2146
|
+
Returns:
|
|
2147
|
+
Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
2148
|
+
- When deserialize=True: List of CulturalNotion objects
|
|
2149
|
+
- When deserialize=False: List of CulturalNotion dictionaries and total count
|
|
2150
|
+
|
|
2151
|
+
Raises:
|
|
2152
|
+
Exception: If an error occurs during retrieval.
|
|
2153
|
+
"""
|
|
2154
|
+
try:
|
|
2155
|
+
table = self._get_table(table_type="culture")
|
|
2156
|
+
if table is None:
|
|
2157
|
+
return [] if deserialize else ([], 0)
|
|
2158
|
+
|
|
2159
|
+
with self.Session() as sess, sess.begin():
|
|
2160
|
+
stmt = select(table)
|
|
2161
|
+
|
|
2162
|
+
# Filtering
|
|
2163
|
+
if name is not None:
|
|
2164
|
+
stmt = stmt.where(table.c.name == name)
|
|
2165
|
+
if agent_id is not None:
|
|
2166
|
+
stmt = stmt.where(table.c.agent_id == agent_id)
|
|
2167
|
+
if team_id is not None:
|
|
2168
|
+
stmt = stmt.where(table.c.team_id == team_id)
|
|
2169
|
+
|
|
2170
|
+
# Get total count after applying filtering
|
|
2171
|
+
count_stmt = select(func.count()).select_from(stmt.alias())
|
|
2172
|
+
total_count = sess.execute(count_stmt).scalar()
|
|
2173
|
+
|
|
2174
|
+
# Sorting
|
|
2175
|
+
stmt = apply_sorting(stmt, table, sort_by, sort_order)
|
|
2176
|
+
# Paginating
|
|
2177
|
+
if limit is not None:
|
|
2178
|
+
stmt = stmt.limit(limit)
|
|
2179
|
+
if page is not None:
|
|
2180
|
+
stmt = stmt.offset((page - 1) * limit)
|
|
2181
|
+
|
|
2182
|
+
result = sess.execute(stmt).fetchall()
|
|
2183
|
+
if not result:
|
|
2184
|
+
return [] if deserialize else ([], 0)
|
|
2185
|
+
|
|
2186
|
+
db_rows = [dict(record._mapping) for record in result]
|
|
2187
|
+
|
|
2188
|
+
if not deserialize:
|
|
2189
|
+
return db_rows, total_count
|
|
2190
|
+
|
|
2191
|
+
return [deserialize_cultural_knowledge_from_db(row) for row in db_rows]
|
|
2192
|
+
|
|
2193
|
+
except Exception as e:
|
|
2194
|
+
log_error(f"Error reading from cultural artifacts table: {e}")
|
|
2195
|
+
raise e
|
|
2196
|
+
|
|
2197
|
+
def upsert_cultural_knowledge(
|
|
2198
|
+
self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
|
|
2199
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
2200
|
+
"""Upsert a cultural artifact into the database.
|
|
2201
|
+
|
|
2202
|
+
Args:
|
|
2203
|
+
cultural_knowledge (CulturalKnowledge): The cultural artifact to upsert.
|
|
2204
|
+
deserialize (Optional[bool]): Whether to serialize the cultural artifact. Defaults to True.
|
|
2205
|
+
|
|
2206
|
+
Returns:
|
|
2207
|
+
Optional[Union[CulturalNotion, Dict[str, Any]]]:
|
|
2208
|
+
- When deserialize=True: CulturalNotion object
|
|
2209
|
+
- When deserialize=False: CulturalNotion dictionary
|
|
2210
|
+
|
|
2211
|
+
Raises:
|
|
2212
|
+
Exception: If an error occurs during upsert.
|
|
2213
|
+
"""
|
|
2214
|
+
try:
|
|
2215
|
+
table = self._get_table(table_type="culture", create_table_if_not_found=True)
|
|
2216
|
+
if table is None:
|
|
2217
|
+
return None
|
|
2218
|
+
|
|
2219
|
+
if cultural_knowledge.id is None:
|
|
2220
|
+
cultural_knowledge.id = str(uuid4())
|
|
2221
|
+
|
|
2222
|
+
# Serialize content, categories, and notes into a JSON string for DB storage (SQLite requires strings)
|
|
2223
|
+
content_json_str = serialize_cultural_knowledge_for_db(cultural_knowledge)
|
|
2224
|
+
|
|
2225
|
+
with self.Session() as sess, sess.begin():
|
|
2226
|
+
stmt = sqlite.insert(table).values(
|
|
2227
|
+
id=cultural_knowledge.id,
|
|
2228
|
+
name=cultural_knowledge.name,
|
|
2229
|
+
summary=cultural_knowledge.summary,
|
|
2230
|
+
content=content_json_str,
|
|
2231
|
+
metadata=cultural_knowledge.metadata,
|
|
2232
|
+
input=cultural_knowledge.input,
|
|
2233
|
+
created_at=cultural_knowledge.created_at,
|
|
2234
|
+
updated_at=int(time.time()),
|
|
2235
|
+
agent_id=cultural_knowledge.agent_id,
|
|
2236
|
+
team_id=cultural_knowledge.team_id,
|
|
2237
|
+
)
|
|
2238
|
+
stmt = stmt.on_conflict_do_update( # type: ignore
|
|
2239
|
+
index_elements=["id"],
|
|
2240
|
+
set_=dict(
|
|
2241
|
+
name=cultural_knowledge.name,
|
|
2242
|
+
summary=cultural_knowledge.summary,
|
|
2243
|
+
content=content_json_str,
|
|
2244
|
+
metadata=cultural_knowledge.metadata,
|
|
2245
|
+
input=cultural_knowledge.input,
|
|
2246
|
+
updated_at=int(time.time()),
|
|
2247
|
+
agent_id=cultural_knowledge.agent_id,
|
|
2248
|
+
team_id=cultural_knowledge.team_id,
|
|
2249
|
+
),
|
|
2250
|
+
).returning(table)
|
|
2251
|
+
|
|
2252
|
+
result = sess.execute(stmt)
|
|
2253
|
+
row = result.fetchone()
|
|
2254
|
+
|
|
2255
|
+
if row is None:
|
|
2256
|
+
return None
|
|
2257
|
+
|
|
2258
|
+
db_row: Dict[str, Any] = dict(row._mapping)
|
|
2259
|
+
if not db_row or not deserialize:
|
|
2260
|
+
return db_row
|
|
2261
|
+
|
|
2262
|
+
return deserialize_cultural_knowledge_from_db(db_row)
|
|
2263
|
+
|
|
2264
|
+
except Exception as e:
|
|
2265
|
+
log_error(f"Error upserting cultural knowledge: {e}")
|
|
2266
|
+
raise e
|
agno/db/sqlite/utils.py
CHANGED
|
@@ -4,6 +4,7 @@ from datetime import date, datetime, timedelta, timezone
|
|
|
4
4
|
from typing import Any, Dict, List, Optional
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
7
8
|
from agno.db.sqlite.schemas import get_table_schema_definition
|
|
8
9
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
9
10
|
|
|
@@ -271,3 +272,64 @@ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
|
271
272
|
if days_diff <= 0:
|
|
272
273
|
return []
|
|
273
274
|
return [starting_date + timedelta(days=x) for x in range(days_diff)]
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
# -- Cultural Knowledge util methods --
|
|
278
|
+
def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> str:
|
|
279
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
280
|
+
|
|
281
|
+
Converts the model's separate content, categories, and notes fields
|
|
282
|
+
into a single JSON string for the database content column.
|
|
283
|
+
SQLite requires JSON to be stored as strings.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
str: A JSON string containing content, categories, and notes.
|
|
290
|
+
"""
|
|
291
|
+
content_dict: Dict[str, Any] = {}
|
|
292
|
+
if cultural_knowledge.content is not None:
|
|
293
|
+
content_dict["content"] = cultural_knowledge.content
|
|
294
|
+
if cultural_knowledge.categories is not None:
|
|
295
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
296
|
+
if cultural_knowledge.notes is not None:
|
|
297
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
298
|
+
|
|
299
|
+
return json.dumps(content_dict) if content_dict else None # type: ignore
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
303
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
304
|
+
|
|
305
|
+
The database stores content as a JSON dict containing content, categories, and notes.
|
|
306
|
+
This method extracts those fields and converts them back to the model format.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
313
|
+
"""
|
|
314
|
+
# Extract content, categories, and notes from the JSON content field
|
|
315
|
+
content_json = db_row.get("content", {}) or {}
|
|
316
|
+
|
|
317
|
+
if isinstance(content_json, str):
|
|
318
|
+
content_json = json.loads(content_json) if content_json else {}
|
|
319
|
+
|
|
320
|
+
return CulturalKnowledge.from_dict(
|
|
321
|
+
{
|
|
322
|
+
"id": db_row.get("id"),
|
|
323
|
+
"name": db_row.get("name"),
|
|
324
|
+
"summary": db_row.get("summary"),
|
|
325
|
+
"content": content_json.get("content"),
|
|
326
|
+
"categories": content_json.get("categories"),
|
|
327
|
+
"notes": content_json.get("notes"),
|
|
328
|
+
"metadata": db_row.get("metadata"),
|
|
329
|
+
"input": db_row.get("input"),
|
|
330
|
+
"created_at": db_row.get("created_at"),
|
|
331
|
+
"updated_at": db_row.get("updated_at"),
|
|
332
|
+
"agent_id": db_row.get("agent_id"),
|
|
333
|
+
"team_id": db_row.get("team_id"),
|
|
334
|
+
}
|
|
335
|
+
)
|
agno/db/surrealdb/models.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from dataclasses import asdict
|
|
2
2
|
from datetime import date, datetime, timezone
|
|
3
3
|
from textwrap import dedent
|
|
4
|
-
from typing import List, Literal, Optional, Sequence
|
|
4
|
+
from typing import Any, Dict, List, Literal, Optional, Sequence
|
|
5
5
|
|
|
6
6
|
from surrealdb import RecordID
|
|
7
7
|
|
|
8
8
|
from agno.db.base import SessionType
|
|
9
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
9
10
|
from agno.db.schemas.evals import EvalRunRecord
|
|
10
11
|
from agno.db.schemas.knowledge import KnowledgeRow
|
|
11
12
|
from agno.db.schemas.memory import UserMemory
|
|
@@ -16,6 +17,7 @@ from agno.session.workflow import WorkflowSession
|
|
|
16
17
|
|
|
17
18
|
TableType = Literal[
|
|
18
19
|
"agents",
|
|
20
|
+
"culture",
|
|
19
21
|
"evals",
|
|
20
22
|
"knowledge",
|
|
21
23
|
"memories",
|
|
@@ -204,6 +206,48 @@ def serialize_knowledge_row(knowledge_row: KnowledgeRow, knowledge_table_name: s
|
|
|
204
206
|
return dict_
|
|
205
207
|
|
|
206
208
|
|
|
209
|
+
def deserialize_cultural_knowledge(cultural_knowledge_raw: dict) -> CulturalKnowledge:
|
|
210
|
+
copy = cultural_knowledge_raw.copy()
|
|
211
|
+
|
|
212
|
+
copy = deserialize_record_id(copy, "id")
|
|
213
|
+
copy = desurrealize_dates(copy)
|
|
214
|
+
|
|
215
|
+
# Extract content, categories, and notes from the content field
|
|
216
|
+
content_json = copy.get("content", {}) or {}
|
|
217
|
+
if isinstance(content_json, dict):
|
|
218
|
+
copy["content"] = content_json.get("content")
|
|
219
|
+
copy["categories"] = content_json.get("categories")
|
|
220
|
+
copy["notes"] = content_json.get("notes")
|
|
221
|
+
|
|
222
|
+
return CulturalKnowledge.from_dict(copy)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def serialize_cultural_knowledge(cultural_knowledge: CulturalKnowledge, culture_table_name: str) -> dict:
|
|
226
|
+
dict_ = asdict(cultural_knowledge)
|
|
227
|
+
if cultural_knowledge.id is not None:
|
|
228
|
+
dict_["id"] = RecordID(culture_table_name, cultural_knowledge.id)
|
|
229
|
+
|
|
230
|
+
# Serialize content, categories, and notes into a single content dict for DB storage
|
|
231
|
+
content_dict: Dict[str, Any] = {}
|
|
232
|
+
if cultural_knowledge.content is not None:
|
|
233
|
+
content_dict["content"] = cultural_knowledge.content
|
|
234
|
+
if cultural_knowledge.categories is not None:
|
|
235
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
236
|
+
if cultural_knowledge.notes is not None:
|
|
237
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
238
|
+
|
|
239
|
+
# Replace the separate fields with the combined content field
|
|
240
|
+
dict_["content"] = content_dict if content_dict else None
|
|
241
|
+
# Remove the now-redundant fields since they're in content
|
|
242
|
+
dict_.pop("categories", None)
|
|
243
|
+
dict_.pop("notes", None)
|
|
244
|
+
|
|
245
|
+
# surrealize dates
|
|
246
|
+
dict_ = surrealize_dates(dict_)
|
|
247
|
+
|
|
248
|
+
return dict_
|
|
249
|
+
|
|
250
|
+
|
|
207
251
|
def desurrealize_eval_run_record(eval_run_record_raw: dict) -> dict:
|
|
208
252
|
copy = eval_run_record_raw.copy()
|
|
209
253
|
|
|
@@ -249,6 +293,12 @@ def get_schema(table_type: TableType, table_name: str) -> str:
|
|
|
249
293
|
DEFINE FIELD OVERWRITE created_at ON {table_name} TYPE datetime VALUE time::now();
|
|
250
294
|
DEFINE FIELD OVERWRITE updated_at ON {table_name} TYPE datetime VALUE time::now();
|
|
251
295
|
""")
|
|
296
|
+
elif table_type == "culture":
|
|
297
|
+
return dedent(f"""
|
|
298
|
+
{define_table}
|
|
299
|
+
DEFINE FIELD OVERWRITE created_at ON {table_name} TYPE datetime VALUE time::now();
|
|
300
|
+
DEFINE FIELD OVERWRITE updated_at ON {table_name} TYPE datetime VALUE time::now();
|
|
301
|
+
""")
|
|
252
302
|
elif table_type == "sessions":
|
|
253
303
|
return dedent(f"""
|
|
254
304
|
{define_table}
|