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
agno/db/firestore/utils.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing import Any, Dict, List, Optional
|
|
|
7
7
|
from uuid import uuid4
|
|
8
8
|
|
|
9
9
|
from agno.db.firestore.schemas import get_collection_indexes
|
|
10
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
10
11
|
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
11
12
|
|
|
12
13
|
try:
|
|
@@ -122,6 +123,42 @@ def apply_pagination(query, limit: Optional[int] = None, page: Optional[int] = N
|
|
|
122
123
|
return query
|
|
123
124
|
|
|
124
125
|
|
|
126
|
+
def apply_sorting_to_records(
|
|
127
|
+
records: List[Dict[str, Any]], sort_by: Optional[str] = None, sort_order: Optional[str] = None
|
|
128
|
+
) -> List[Dict[str, Any]]:
|
|
129
|
+
"""Apply sorting to in-memory records (for cases where Firestore query sorting isn't possible)."""
|
|
130
|
+
if sort_by is None:
|
|
131
|
+
return records
|
|
132
|
+
|
|
133
|
+
def get_sort_key(record):
|
|
134
|
+
value = record.get(sort_by, 0)
|
|
135
|
+
if value is None:
|
|
136
|
+
return 0 if records and isinstance(records[0].get(sort_by, 0), (int, float)) else ""
|
|
137
|
+
return value
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
is_reverse = sort_order == "desc"
|
|
141
|
+
return sorted(records, key=get_sort_key, reverse=is_reverse)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
log_warning(f"Error sorting Firestore records: {e}")
|
|
144
|
+
return records
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def apply_pagination_to_records(
|
|
148
|
+
records: List[Dict[str, Any]], limit: Optional[int] = None, page: Optional[int] = None
|
|
149
|
+
) -> List[Dict[str, Any]]:
|
|
150
|
+
"""Apply pagination to in-memory records (for cases where Firestore query pagination isn't possible)."""
|
|
151
|
+
if limit is None:
|
|
152
|
+
return records
|
|
153
|
+
|
|
154
|
+
if page is not None and page > 0:
|
|
155
|
+
start_idx = (page - 1) * limit
|
|
156
|
+
end_idx = start_idx + limit
|
|
157
|
+
return records[start_idx:end_idx]
|
|
158
|
+
else:
|
|
159
|
+
return records[:limit]
|
|
160
|
+
|
|
161
|
+
|
|
125
162
|
# -- Metrics util methods --
|
|
126
163
|
|
|
127
164
|
|
|
@@ -278,3 +315,62 @@ def bulk_upsert_metrics(collection_ref, metrics_records: List[Dict[str, Any]]) -
|
|
|
278
315
|
log_error(f"Error committing metrics batch: {e}")
|
|
279
316
|
|
|
280
317
|
return results
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# -- Cultural Knowledge util methods --
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
|
|
324
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
325
|
+
|
|
326
|
+
Converts the model's separate content, categories, and notes fields
|
|
327
|
+
into a single dict for the database content field.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
Dict[str, Any]: A dictionary with content, categories, and notes.
|
|
334
|
+
"""
|
|
335
|
+
content_dict: Dict[str, Any] = {}
|
|
336
|
+
if cultural_knowledge.content is not None:
|
|
337
|
+
content_dict["content"] = cultural_knowledge.content
|
|
338
|
+
if cultural_knowledge.categories is not None:
|
|
339
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
340
|
+
if cultural_knowledge.notes is not None:
|
|
341
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
342
|
+
|
|
343
|
+
return content_dict if content_dict else {}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
347
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
348
|
+
|
|
349
|
+
The database stores content as a dict containing content, categories, and notes.
|
|
350
|
+
This method extracts those fields and converts them back to the model format.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
357
|
+
"""
|
|
358
|
+
# Extract content, categories, and notes from the content field
|
|
359
|
+
content_json = db_row.get("content", {}) or {}
|
|
360
|
+
|
|
361
|
+
return CulturalKnowledge.from_dict(
|
|
362
|
+
{
|
|
363
|
+
"id": db_row.get("id"),
|
|
364
|
+
"name": db_row.get("name"),
|
|
365
|
+
"summary": db_row.get("summary"),
|
|
366
|
+
"content": content_json.get("content"),
|
|
367
|
+
"categories": content_json.get("categories"),
|
|
368
|
+
"notes": content_json.get("notes"),
|
|
369
|
+
"metadata": db_row.get("metadata"),
|
|
370
|
+
"input": db_row.get("input"),
|
|
371
|
+
"created_at": db_row.get("created_at"),
|
|
372
|
+
"updated_at": db_row.get("updated_at"),
|
|
373
|
+
"agent_id": db_row.get("agent_id"),
|
|
374
|
+
"team_id": db_row.get("team_id"),
|
|
375
|
+
}
|
|
376
|
+
)
|
agno/db/gcs_json/gcs_json_db.py
CHANGED
|
@@ -8,9 +8,12 @@ from agno.db.base import BaseDb, SessionType
|
|
|
8
8
|
from agno.db.gcs_json.utils import (
|
|
9
9
|
apply_sorting,
|
|
10
10
|
calculate_date_metrics,
|
|
11
|
+
deserialize_cultural_knowledge_from_db,
|
|
11
12
|
fetch_all_sessions_data,
|
|
12
13
|
get_dates_to_calculate_metrics_for,
|
|
14
|
+
serialize_cultural_knowledge_for_db,
|
|
13
15
|
)
|
|
16
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
14
17
|
from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
|
|
15
18
|
from agno.db.schemas.knowledge import KnowledgeRow
|
|
16
19
|
from agno.db.schemas.memory import UserMemory
|
|
@@ -34,6 +37,7 @@ class GcsJsonDb(BaseDb):
|
|
|
34
37
|
metrics_table: Optional[str] = None,
|
|
35
38
|
eval_table: Optional[str] = None,
|
|
36
39
|
knowledge_table: Optional[str] = None,
|
|
40
|
+
culture_table: Optional[str] = None,
|
|
37
41
|
project: Optional[str] = None,
|
|
38
42
|
credentials: Optional[Any] = None,
|
|
39
43
|
id: Optional[str] = None,
|
|
@@ -49,6 +53,7 @@ class GcsJsonDb(BaseDb):
|
|
|
49
53
|
metrics_table (Optional[str]): Name of the JSON file to store metrics.
|
|
50
54
|
eval_table (Optional[str]): Name of the JSON file to store evaluation runs.
|
|
51
55
|
knowledge_table (Optional[str]): Name of the JSON file to store knowledge content.
|
|
56
|
+
culture_table (Optional[str]): Name of the JSON file to store cultural knowledge.
|
|
52
57
|
project (Optional[str]): GCP project ID. If None, uses default project.
|
|
53
58
|
location (Optional[str]): GCS bucket location. If None, uses default location.
|
|
54
59
|
credentials (Optional[Any]): GCP credentials. If None, uses default credentials.
|
|
@@ -66,6 +71,7 @@ class GcsJsonDb(BaseDb):
|
|
|
66
71
|
metrics_table=metrics_table,
|
|
67
72
|
eval_table=eval_table,
|
|
68
73
|
knowledge_table=knowledge_table,
|
|
74
|
+
culture_table=culture_table,
|
|
69
75
|
)
|
|
70
76
|
|
|
71
77
|
self.bucket_name = bucket_name
|
|
@@ -1143,3 +1149,187 @@ class GcsJsonDb(BaseDb):
|
|
|
1143
1149
|
except Exception as e:
|
|
1144
1150
|
log_warning(f"Error renaming eval run {eval_run_id}: {e}")
|
|
1145
1151
|
raise e
|
|
1152
|
+
|
|
1153
|
+
# -- Cultural Knowledge methods --
|
|
1154
|
+
def clear_cultural_knowledge(self) -> None:
|
|
1155
|
+
"""Delete all cultural knowledge from the database.
|
|
1156
|
+
|
|
1157
|
+
Raises:
|
|
1158
|
+
Exception: If an error occurs during deletion.
|
|
1159
|
+
"""
|
|
1160
|
+
try:
|
|
1161
|
+
self._write_json_file(self.culture_table_name, [])
|
|
1162
|
+
except Exception as e:
|
|
1163
|
+
log_warning(f"Exception deleting all cultural knowledge: {e}")
|
|
1164
|
+
raise e
|
|
1165
|
+
|
|
1166
|
+
def delete_cultural_knowledge(self, id: str) -> None:
|
|
1167
|
+
"""Delete cultural knowledge by ID.
|
|
1168
|
+
|
|
1169
|
+
Args:
|
|
1170
|
+
id (str): The ID of the cultural knowledge to delete.
|
|
1171
|
+
|
|
1172
|
+
Raises:
|
|
1173
|
+
Exception: If an error occurs during deletion.
|
|
1174
|
+
"""
|
|
1175
|
+
try:
|
|
1176
|
+
cultural_knowledge = self._read_json_file(self.culture_table_name)
|
|
1177
|
+
cultural_knowledge = [item for item in cultural_knowledge if item.get("id") != id]
|
|
1178
|
+
self._write_json_file(self.culture_table_name, cultural_knowledge)
|
|
1179
|
+
log_debug(f"Deleted cultural knowledge with ID: {id}")
|
|
1180
|
+
except Exception as e:
|
|
1181
|
+
log_warning(f"Error deleting cultural knowledge: {e}")
|
|
1182
|
+
raise e
|
|
1183
|
+
|
|
1184
|
+
def get_cultural_knowledge(
|
|
1185
|
+
self, id: str, deserialize: Optional[bool] = True
|
|
1186
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1187
|
+
"""Get cultural knowledge by ID.
|
|
1188
|
+
|
|
1189
|
+
Args:
|
|
1190
|
+
id (str): The ID of the cultural knowledge to retrieve.
|
|
1191
|
+
deserialize (Optional[bool]): Whether to deserialize to CulturalKnowledge object. Defaults to True.
|
|
1192
|
+
|
|
1193
|
+
Returns:
|
|
1194
|
+
Optional[Union[CulturalKnowledge, Dict[str, Any]]]: The cultural knowledge if found, None otherwise.
|
|
1195
|
+
|
|
1196
|
+
Raises:
|
|
1197
|
+
Exception: If an error occurs during retrieval.
|
|
1198
|
+
"""
|
|
1199
|
+
try:
|
|
1200
|
+
cultural_knowledge = self._read_json_file(self.culture_table_name)
|
|
1201
|
+
|
|
1202
|
+
for item in cultural_knowledge:
|
|
1203
|
+
if item.get("id") == id:
|
|
1204
|
+
if not deserialize:
|
|
1205
|
+
return item
|
|
1206
|
+
return deserialize_cultural_knowledge_from_db(item)
|
|
1207
|
+
|
|
1208
|
+
return None
|
|
1209
|
+
except Exception as e:
|
|
1210
|
+
log_warning(f"Error getting cultural knowledge: {e}")
|
|
1211
|
+
raise e
|
|
1212
|
+
|
|
1213
|
+
def get_all_cultural_knowledge(
|
|
1214
|
+
self,
|
|
1215
|
+
agent_id: Optional[str] = None,
|
|
1216
|
+
team_id: Optional[str] = None,
|
|
1217
|
+
name: Optional[str] = None,
|
|
1218
|
+
limit: Optional[int] = None,
|
|
1219
|
+
page: Optional[int] = None,
|
|
1220
|
+
sort_by: Optional[str] = None,
|
|
1221
|
+
sort_order: Optional[str] = None,
|
|
1222
|
+
deserialize: Optional[bool] = True,
|
|
1223
|
+
) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
1224
|
+
"""Get all cultural knowledge with filtering and pagination.
|
|
1225
|
+
|
|
1226
|
+
Args:
|
|
1227
|
+
agent_id (Optional[str]): Filter by agent ID.
|
|
1228
|
+
team_id (Optional[str]): Filter by team ID.
|
|
1229
|
+
name (Optional[str]): Filter by name (case-insensitive partial match).
|
|
1230
|
+
limit (Optional[int]): Maximum number of results to return.
|
|
1231
|
+
page (Optional[int]): Page number for pagination.
|
|
1232
|
+
sort_by (Optional[str]): Field to sort by.
|
|
1233
|
+
sort_order (Optional[str]): Sort order ('asc' or 'desc').
|
|
1234
|
+
deserialize (Optional[bool]): Whether to deserialize to CulturalKnowledge objects. Defaults to True.
|
|
1235
|
+
|
|
1236
|
+
Returns:
|
|
1237
|
+
Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
1238
|
+
- When deserialize=True: List of CulturalKnowledge objects
|
|
1239
|
+
- When deserialize=False: Tuple with list of dictionaries and total count
|
|
1240
|
+
|
|
1241
|
+
Raises:
|
|
1242
|
+
Exception: If an error occurs during retrieval.
|
|
1243
|
+
"""
|
|
1244
|
+
try:
|
|
1245
|
+
cultural_knowledge = self._read_json_file(self.culture_table_name)
|
|
1246
|
+
|
|
1247
|
+
# Apply filters
|
|
1248
|
+
filtered_items = []
|
|
1249
|
+
for item in cultural_knowledge:
|
|
1250
|
+
if agent_id is not None and item.get("agent_id") != agent_id:
|
|
1251
|
+
continue
|
|
1252
|
+
if team_id is not None and item.get("team_id") != team_id:
|
|
1253
|
+
continue
|
|
1254
|
+
if name is not None and name.lower() not in item.get("name", "").lower():
|
|
1255
|
+
continue
|
|
1256
|
+
|
|
1257
|
+
filtered_items.append(item)
|
|
1258
|
+
|
|
1259
|
+
total_count = len(filtered_items)
|
|
1260
|
+
|
|
1261
|
+
# Apply sorting
|
|
1262
|
+
filtered_items = apply_sorting(filtered_items, sort_by, sort_order)
|
|
1263
|
+
|
|
1264
|
+
# Apply pagination
|
|
1265
|
+
if limit is not None:
|
|
1266
|
+
start_idx = 0
|
|
1267
|
+
if page is not None:
|
|
1268
|
+
start_idx = (page - 1) * limit
|
|
1269
|
+
filtered_items = filtered_items[start_idx : start_idx + limit]
|
|
1270
|
+
|
|
1271
|
+
if not deserialize:
|
|
1272
|
+
return filtered_items, total_count
|
|
1273
|
+
|
|
1274
|
+
return [deserialize_cultural_knowledge_from_db(item) for item in filtered_items]
|
|
1275
|
+
|
|
1276
|
+
except Exception as e:
|
|
1277
|
+
log_warning(f"Error getting all cultural knowledge: {e}")
|
|
1278
|
+
raise e
|
|
1279
|
+
|
|
1280
|
+
def upsert_cultural_knowledge(
|
|
1281
|
+
self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
|
|
1282
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1283
|
+
"""Upsert cultural knowledge in the GCS JSON file.
|
|
1284
|
+
|
|
1285
|
+
Args:
|
|
1286
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge to upsert.
|
|
1287
|
+
deserialize (Optional[bool]): Whether to deserialize the result. Defaults to True.
|
|
1288
|
+
|
|
1289
|
+
Returns:
|
|
1290
|
+
Optional[Union[CulturalKnowledge, Dict[str, Any]]]: The upserted cultural knowledge.
|
|
1291
|
+
|
|
1292
|
+
Raises:
|
|
1293
|
+
Exception: If an error occurs during upsert.
|
|
1294
|
+
"""
|
|
1295
|
+
try:
|
|
1296
|
+
cultural_knowledge_list = self._read_json_file(self.culture_table_name, create_table_if_not_found=True)
|
|
1297
|
+
|
|
1298
|
+
# Serialize content, categories, and notes into a dict for DB storage
|
|
1299
|
+
content_dict = serialize_cultural_knowledge_for_db(cultural_knowledge)
|
|
1300
|
+
|
|
1301
|
+
# Create the item dict with serialized content
|
|
1302
|
+
cultural_knowledge_dict = {
|
|
1303
|
+
"id": cultural_knowledge.id,
|
|
1304
|
+
"name": cultural_knowledge.name,
|
|
1305
|
+
"summary": cultural_knowledge.summary,
|
|
1306
|
+
"content": content_dict if content_dict else None,
|
|
1307
|
+
"metadata": cultural_knowledge.metadata,
|
|
1308
|
+
"input": cultural_knowledge.input,
|
|
1309
|
+
"created_at": cultural_knowledge.created_at,
|
|
1310
|
+
"updated_at": int(time.time()),
|
|
1311
|
+
"agent_id": cultural_knowledge.agent_id,
|
|
1312
|
+
"team_id": cultural_knowledge.team_id,
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
# Find existing item to update
|
|
1316
|
+
item_updated = False
|
|
1317
|
+
for i, existing_item in enumerate(cultural_knowledge_list):
|
|
1318
|
+
if existing_item.get("id") == cultural_knowledge.id:
|
|
1319
|
+
cultural_knowledge_list[i] = cultural_knowledge_dict
|
|
1320
|
+
item_updated = True
|
|
1321
|
+
break
|
|
1322
|
+
|
|
1323
|
+
if not item_updated:
|
|
1324
|
+
cultural_knowledge_list.append(cultural_knowledge_dict)
|
|
1325
|
+
|
|
1326
|
+
self._write_json_file(self.culture_table_name, cultural_knowledge_list)
|
|
1327
|
+
|
|
1328
|
+
if not deserialize:
|
|
1329
|
+
return cultural_knowledge_dict
|
|
1330
|
+
|
|
1331
|
+
return deserialize_cultural_knowledge_from_db(cultural_knowledge_dict)
|
|
1332
|
+
|
|
1333
|
+
except Exception as e:
|
|
1334
|
+
log_warning(f"Error upserting cultural knowledge: {e}")
|
|
1335
|
+
raise e
|
agno/db/gcs_json/utils.py
CHANGED
|
@@ -6,6 +6,7 @@ from typing import Any, Dict, List, Optional
|
|
|
6
6
|
from uuid import uuid4
|
|
7
7
|
|
|
8
8
|
from agno.db.base import SessionType
|
|
9
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
9
10
|
from agno.run.agent import RunOutput
|
|
10
11
|
from agno.run.team import TeamRunOutput
|
|
11
12
|
from agno.session.summary import SessionSummary
|
|
@@ -192,3 +193,60 @@ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
|
192
193
|
if days_diff <= 0:
|
|
193
194
|
return []
|
|
194
195
|
return [starting_date + timedelta(days=x) for x in range(days_diff)]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# -- Cultural Knowledge util methods --
|
|
199
|
+
def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
|
|
200
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
201
|
+
|
|
202
|
+
Converts the model's separate content, categories, and notes fields
|
|
203
|
+
into a single dict for the database content column.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Dict[str, Any]: A dictionary with the content field as a dict containing content, categories, and notes.
|
|
210
|
+
"""
|
|
211
|
+
content_dict: Dict[str, Any] = {}
|
|
212
|
+
if cultural_knowledge.content is not None:
|
|
213
|
+
content_dict["content"] = cultural_knowledge.content
|
|
214
|
+
if cultural_knowledge.categories is not None:
|
|
215
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
216
|
+
if cultural_knowledge.notes is not None:
|
|
217
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
218
|
+
|
|
219
|
+
return content_dict if content_dict else {}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
223
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
224
|
+
|
|
225
|
+
The database stores content as a dict containing content, categories, and notes.
|
|
226
|
+
This method extracts those fields and converts them back to the model format.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
233
|
+
"""
|
|
234
|
+
# Extract content, categories, and notes from the content field
|
|
235
|
+
content_json = db_row.get("content", {}) or {}
|
|
236
|
+
|
|
237
|
+
return CulturalKnowledge.from_dict(
|
|
238
|
+
{
|
|
239
|
+
"id": db_row.get("id"),
|
|
240
|
+
"name": db_row.get("name"),
|
|
241
|
+
"summary": db_row.get("summary"),
|
|
242
|
+
"content": content_json.get("content"),
|
|
243
|
+
"categories": content_json.get("categories"),
|
|
244
|
+
"notes": content_json.get("notes"),
|
|
245
|
+
"metadata": db_row.get("metadata"),
|
|
246
|
+
"input": db_row.get("input"),
|
|
247
|
+
"created_at": db_row.get("created_at"),
|
|
248
|
+
"updated_at": db_row.get("updated_at"),
|
|
249
|
+
"agent_id": db_row.get("agent_id"),
|
|
250
|
+
"team_id": db_row.get("team_id"),
|
|
251
|
+
}
|
|
252
|
+
)
|
|
@@ -8,9 +8,12 @@ from agno.db.base import BaseDb, SessionType
|
|
|
8
8
|
from agno.db.in_memory.utils import (
|
|
9
9
|
apply_sorting,
|
|
10
10
|
calculate_date_metrics,
|
|
11
|
+
deserialize_cultural_knowledge_from_db,
|
|
11
12
|
fetch_all_sessions_data,
|
|
12
13
|
get_dates_to_calculate_metrics_for,
|
|
14
|
+
serialize_cultural_knowledge_for_db,
|
|
13
15
|
)
|
|
16
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
14
17
|
from agno.db.schemas.evals import EvalFilterType, EvalRunRecord, EvalType
|
|
15
18
|
from agno.db.schemas.knowledge import KnowledgeRow
|
|
16
19
|
from agno.db.schemas.memory import UserMemory
|
|
@@ -29,6 +32,7 @@ class InMemoryDb(BaseDb):
|
|
|
29
32
|
self._metrics: List[Dict[str, Any]] = []
|
|
30
33
|
self._eval_runs: List[Dict[str, Any]] = []
|
|
31
34
|
self._knowledge: List[Dict[str, Any]] = []
|
|
35
|
+
self._cultural_knowledge: List[Dict[str, Any]] = []
|
|
32
36
|
|
|
33
37
|
# -- Session methods --
|
|
34
38
|
|
|
@@ -1039,3 +1043,117 @@ class InMemoryDb(BaseDb):
|
|
|
1039
1043
|
except Exception as e:
|
|
1040
1044
|
log_error(f"Error renaming eval run {eval_run_id}: {e}")
|
|
1041
1045
|
raise e
|
|
1046
|
+
|
|
1047
|
+
# -- Culture methods --
|
|
1048
|
+
|
|
1049
|
+
def clear_cultural_knowledge(self) -> None:
|
|
1050
|
+
"""Delete all cultural knowledge from in-memory storage."""
|
|
1051
|
+
try:
|
|
1052
|
+
self._cultural_knowledge = []
|
|
1053
|
+
except Exception as e:
|
|
1054
|
+
log_error(f"Error clearing cultural knowledge: {e}")
|
|
1055
|
+
raise e
|
|
1056
|
+
|
|
1057
|
+
def delete_cultural_knowledge(self, id: str) -> None:
|
|
1058
|
+
"""Delete a cultural knowledge entry from in-memory storage."""
|
|
1059
|
+
try:
|
|
1060
|
+
self._cultural_knowledge = [ck for ck in self._cultural_knowledge if ck.get("id") != id]
|
|
1061
|
+
except Exception as e:
|
|
1062
|
+
log_error(f"Error deleting cultural knowledge: {e}")
|
|
1063
|
+
raise e
|
|
1064
|
+
|
|
1065
|
+
def get_cultural_knowledge(
|
|
1066
|
+
self, id: str, deserialize: Optional[bool] = True
|
|
1067
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1068
|
+
"""Get a cultural knowledge entry from in-memory storage."""
|
|
1069
|
+
try:
|
|
1070
|
+
for ck_data in self._cultural_knowledge:
|
|
1071
|
+
if ck_data.get("id") == id:
|
|
1072
|
+
ck_data_copy = deepcopy(ck_data)
|
|
1073
|
+
if not deserialize:
|
|
1074
|
+
return ck_data_copy
|
|
1075
|
+
return deserialize_cultural_knowledge_from_db(ck_data_copy)
|
|
1076
|
+
return None
|
|
1077
|
+
except Exception as e:
|
|
1078
|
+
log_error(f"Error getting cultural knowledge: {e}")
|
|
1079
|
+
raise e
|
|
1080
|
+
|
|
1081
|
+
def get_all_cultural_knowledge(
|
|
1082
|
+
self,
|
|
1083
|
+
name: Optional[str] = None,
|
|
1084
|
+
agent_id: Optional[str] = None,
|
|
1085
|
+
team_id: Optional[str] = None,
|
|
1086
|
+
limit: Optional[int] = None,
|
|
1087
|
+
page: Optional[int] = None,
|
|
1088
|
+
sort_by: Optional[str] = None,
|
|
1089
|
+
sort_order: Optional[str] = None,
|
|
1090
|
+
deserialize: Optional[bool] = True,
|
|
1091
|
+
) -> Union[List[CulturalKnowledge], Tuple[List[Dict[str, Any]], int]]:
|
|
1092
|
+
"""Get all cultural knowledge from in-memory storage."""
|
|
1093
|
+
try:
|
|
1094
|
+
filtered_ck = []
|
|
1095
|
+
for ck_data in self._cultural_knowledge:
|
|
1096
|
+
if name and ck_data.get("name") != name:
|
|
1097
|
+
continue
|
|
1098
|
+
if agent_id and ck_data.get("agent_id") != agent_id:
|
|
1099
|
+
continue
|
|
1100
|
+
if team_id and ck_data.get("team_id") != team_id:
|
|
1101
|
+
continue
|
|
1102
|
+
filtered_ck.append(ck_data)
|
|
1103
|
+
|
|
1104
|
+
# Apply sorting
|
|
1105
|
+
if sort_by:
|
|
1106
|
+
filtered_ck = apply_sorting(filtered_ck, sort_by, sort_order)
|
|
1107
|
+
|
|
1108
|
+
total_count = len(filtered_ck)
|
|
1109
|
+
|
|
1110
|
+
# Apply pagination
|
|
1111
|
+
if limit and page:
|
|
1112
|
+
start = (page - 1) * limit
|
|
1113
|
+
filtered_ck = filtered_ck[start : start + limit]
|
|
1114
|
+
elif limit:
|
|
1115
|
+
filtered_ck = filtered_ck[:limit]
|
|
1116
|
+
|
|
1117
|
+
if not deserialize:
|
|
1118
|
+
return [deepcopy(ck) for ck in filtered_ck], total_count
|
|
1119
|
+
|
|
1120
|
+
return [deserialize_cultural_knowledge_from_db(deepcopy(ck)) for ck in filtered_ck]
|
|
1121
|
+
except Exception as e:
|
|
1122
|
+
log_error(f"Error getting all cultural knowledge: {e}")
|
|
1123
|
+
raise e
|
|
1124
|
+
|
|
1125
|
+
def upsert_cultural_knowledge(
|
|
1126
|
+
self, cultural_knowledge: CulturalKnowledge, deserialize: Optional[bool] = True
|
|
1127
|
+
) -> Optional[Union[CulturalKnowledge, Dict[str, Any]]]:
|
|
1128
|
+
"""Upsert a cultural knowledge entry into in-memory storage."""
|
|
1129
|
+
try:
|
|
1130
|
+
if not cultural_knowledge.id:
|
|
1131
|
+
cultural_knowledge.id = str(uuid4())
|
|
1132
|
+
|
|
1133
|
+
# Serialize content, categories, and notes into a dict for DB storage
|
|
1134
|
+
content_dict = serialize_cultural_knowledge_for_db(cultural_knowledge)
|
|
1135
|
+
|
|
1136
|
+
# Create the item dict with serialized content
|
|
1137
|
+
ck_dict = {
|
|
1138
|
+
"id": cultural_knowledge.id,
|
|
1139
|
+
"name": cultural_knowledge.name,
|
|
1140
|
+
"summary": cultural_knowledge.summary,
|
|
1141
|
+
"content": content_dict if content_dict else None,
|
|
1142
|
+
"metadata": cultural_knowledge.metadata,
|
|
1143
|
+
"input": cultural_knowledge.input,
|
|
1144
|
+
"created_at": cultural_knowledge.created_at,
|
|
1145
|
+
"updated_at": int(time.time()),
|
|
1146
|
+
"agent_id": cultural_knowledge.agent_id,
|
|
1147
|
+
"team_id": cultural_knowledge.team_id,
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
# Remove existing entry with same id
|
|
1151
|
+
self._cultural_knowledge = [ck for ck in self._cultural_knowledge if ck.get("id") != cultural_knowledge.id]
|
|
1152
|
+
|
|
1153
|
+
# Add new entry
|
|
1154
|
+
self._cultural_knowledge.append(ck_dict)
|
|
1155
|
+
|
|
1156
|
+
return self.get_cultural_knowledge(cultural_knowledge.id, deserialize=deserialize)
|
|
1157
|
+
except Exception as e:
|
|
1158
|
+
log_error(f"Error upserting cultural knowledge: {e}")
|
|
1159
|
+
raise e
|
agno/db/in_memory/utils.py
CHANGED
|
@@ -5,6 +5,7 @@ from datetime import date, datetime, timedelta, timezone
|
|
|
5
5
|
from typing import Any, Dict, List, Optional
|
|
6
6
|
from uuid import uuid4
|
|
7
7
|
|
|
8
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
8
9
|
from agno.utils.log import log_debug
|
|
9
10
|
|
|
10
11
|
|
|
@@ -170,3 +171,60 @@ def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
|
170
171
|
if days_diff <= 0:
|
|
171
172
|
return []
|
|
172
173
|
return [starting_date + timedelta(days=x) for x in range(days_diff)]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# -- Cultural Knowledge util methods --
|
|
177
|
+
def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
|
|
178
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
179
|
+
|
|
180
|
+
Converts the model's separate content, categories, and notes fields
|
|
181
|
+
into a single dict for the database content column.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Dict[str, Any]: A dictionary with the content field as a dict containing content, categories, and notes.
|
|
188
|
+
"""
|
|
189
|
+
content_dict: Dict[str, Any] = {}
|
|
190
|
+
if cultural_knowledge.content is not None:
|
|
191
|
+
content_dict["content"] = cultural_knowledge.content
|
|
192
|
+
if cultural_knowledge.categories is not None:
|
|
193
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
194
|
+
if cultural_knowledge.notes is not None:
|
|
195
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
196
|
+
|
|
197
|
+
return content_dict if content_dict else {}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
201
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
202
|
+
|
|
203
|
+
The database stores content as a dict containing content, categories, and notes.
|
|
204
|
+
This method extracts those fields and converts them back to the model format.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
211
|
+
"""
|
|
212
|
+
# Extract content, categories, and notes from the content field
|
|
213
|
+
content_json = db_row.get("content", {}) or {}
|
|
214
|
+
|
|
215
|
+
return CulturalKnowledge.from_dict(
|
|
216
|
+
{
|
|
217
|
+
"id": db_row.get("id"),
|
|
218
|
+
"name": db_row.get("name"),
|
|
219
|
+
"summary": db_row.get("summary"),
|
|
220
|
+
"content": content_json.get("content"),
|
|
221
|
+
"categories": content_json.get("categories"),
|
|
222
|
+
"notes": content_json.get("notes"),
|
|
223
|
+
"metadata": db_row.get("metadata"),
|
|
224
|
+
"input": db_row.get("input"),
|
|
225
|
+
"created_at": db_row.get("created_at"),
|
|
226
|
+
"updated_at": db_row.get("updated_at"),
|
|
227
|
+
"agent_id": db_row.get("agent_id"),
|
|
228
|
+
"team_id": db_row.get("team_id"),
|
|
229
|
+
}
|
|
230
|
+
)
|