agno 2.3.24__py3-none-any.whl → 2.3.25__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 +297 -11
- agno/db/base.py +214 -0
- agno/db/dynamo/dynamo.py +47 -0
- agno/db/firestore/firestore.py +47 -0
- agno/db/gcs_json/gcs_json_db.py +47 -0
- agno/db/in_memory/in_memory_db.py +47 -0
- agno/db/json/json_db.py +47 -0
- agno/db/mongo/async_mongo.py +229 -0
- agno/db/mongo/mongo.py +47 -0
- agno/db/mongo/schemas.py +16 -0
- agno/db/mysql/async_mysql.py +47 -0
- agno/db/mysql/mysql.py +47 -0
- agno/db/postgres/async_postgres.py +231 -0
- agno/db/postgres/postgres.py +239 -0
- agno/db/postgres/schemas.py +19 -0
- agno/db/redis/redis.py +47 -0
- agno/db/singlestore/singlestore.py +47 -0
- agno/db/sqlite/async_sqlite.py +242 -0
- agno/db/sqlite/schemas.py +18 -0
- agno/db/sqlite/sqlite.py +239 -0
- agno/db/surrealdb/surrealdb.py +47 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +62 -2
- agno/knowledge/chunking/strategy.py +14 -0
- agno/knowledge/knowledge.py +7 -1
- agno/knowledge/reader/arxiv_reader.py +1 -0
- agno/knowledge/reader/csv_reader.py +1 -0
- agno/knowledge/reader/docx_reader.py +1 -0
- agno/knowledge/reader/firecrawl_reader.py +1 -0
- agno/knowledge/reader/json_reader.py +1 -0
- agno/knowledge/reader/markdown_reader.py +1 -0
- agno/knowledge/reader/pdf_reader.py +1 -0
- agno/knowledge/reader/pptx_reader.py +1 -0
- agno/knowledge/reader/s3_reader.py +1 -0
- agno/knowledge/reader/tavily_reader.py +1 -0
- agno/knowledge/reader/text_reader.py +1 -0
- agno/knowledge/reader/web_search_reader.py +1 -0
- agno/knowledge/reader/website_reader.py +1 -0
- agno/knowledge/reader/wikipedia_reader.py +1 -0
- agno/knowledge/reader/youtube_reader.py +1 -0
- agno/knowledge/utils.py +1 -0
- agno/learn/__init__.py +65 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +690 -0
- agno/learn/schemas.py +1043 -0
- agno/learn/stores/__init__.py +35 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/models/base.py +59 -0
- agno/os/routers/knowledge/knowledge.py +7 -0
- agno/tools/browserbase.py +78 -6
- agno/tools/google_bigquery.py +11 -2
- agno/utils/agent.py +30 -1
- {agno-2.3.24.dist-info → agno-2.3.25.dist-info}/METADATA +24 -2
- {agno-2.3.24.dist-info → agno-2.3.25.dist-info}/RECORD +64 -50
- {agno-2.3.24.dist-info → agno-2.3.25.dist-info}/WHEEL +0 -0
- {agno-2.3.24.dist-info → agno-2.3.25.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.24.dist-info → agno-2.3.25.dist-info}/top_level.txt +0 -0
agno/db/sqlite/async_sqlite.py
CHANGED
|
@@ -54,6 +54,7 @@ class AsyncSqliteDb(AsyncBaseDb):
|
|
|
54
54
|
traces_table: Optional[str] = None,
|
|
55
55
|
spans_table: Optional[str] = None,
|
|
56
56
|
versions_table: Optional[str] = None,
|
|
57
|
+
learnings_table: Optional[str] = None,
|
|
57
58
|
id: Optional[str] = None,
|
|
58
59
|
):
|
|
59
60
|
"""
|
|
@@ -78,6 +79,7 @@ class AsyncSqliteDb(AsyncBaseDb):
|
|
|
78
79
|
traces_table (Optional[str]): Name of the table to store run traces.
|
|
79
80
|
spans_table (Optional[str]): Name of the table to store span events.
|
|
80
81
|
versions_table (Optional[str]): Name of the table to store schema versions.
|
|
82
|
+
learnings_table (Optional[str]): Name of the table to store learning records.
|
|
81
83
|
id (Optional[str]): ID of the database.
|
|
82
84
|
|
|
83
85
|
Raises:
|
|
@@ -98,6 +100,7 @@ class AsyncSqliteDb(AsyncBaseDb):
|
|
|
98
100
|
traces_table=traces_table,
|
|
99
101
|
spans_table=spans_table,
|
|
100
102
|
versions_table=versions_table,
|
|
103
|
+
learnings_table=learnings_table,
|
|
101
104
|
)
|
|
102
105
|
|
|
103
106
|
_engine: Optional[AsyncEngine] = db_engine
|
|
@@ -155,6 +158,7 @@ class AsyncSqliteDb(AsyncBaseDb):
|
|
|
155
158
|
(self.eval_table_name, "evals"),
|
|
156
159
|
(self.knowledge_table_name, "knowledge"),
|
|
157
160
|
(self.versions_table_name, "versions"),
|
|
161
|
+
(self.learnings_table_name, "learnings"),
|
|
158
162
|
]
|
|
159
163
|
|
|
160
164
|
for table_name, table_type in tables_to_create:
|
|
@@ -341,6 +345,15 @@ class AsyncSqliteDb(AsyncBaseDb):
|
|
|
341
345
|
)
|
|
342
346
|
return self.spans_table
|
|
343
347
|
|
|
348
|
+
elif table_type == "learnings":
|
|
349
|
+
if not hasattr(self, "learnings_table"):
|
|
350
|
+
self.learnings_table = await self._get_or_create_table(
|
|
351
|
+
table_name=self.learnings_table_name,
|
|
352
|
+
table_type="learnings",
|
|
353
|
+
create_table_if_not_found=create_table_if_not_found,
|
|
354
|
+
)
|
|
355
|
+
return self.learnings_table
|
|
356
|
+
|
|
344
357
|
else:
|
|
345
358
|
raise ValueError(f"Unknown table type: '{table_type}'")
|
|
346
359
|
|
|
@@ -2923,3 +2936,232 @@ class AsyncSqliteDb(AsyncBaseDb):
|
|
|
2923
2936
|
except Exception as e:
|
|
2924
2937
|
log_error(f"Error getting spans: {e}")
|
|
2925
2938
|
return []
|
|
2939
|
+
|
|
2940
|
+
# -- Learning methods --
|
|
2941
|
+
async def get_learning(
|
|
2942
|
+
self,
|
|
2943
|
+
learning_type: str,
|
|
2944
|
+
user_id: Optional[str] = None,
|
|
2945
|
+
agent_id: Optional[str] = None,
|
|
2946
|
+
team_id: Optional[str] = None,
|
|
2947
|
+
workflow_id: Optional[str] = None,
|
|
2948
|
+
session_id: Optional[str] = None,
|
|
2949
|
+
namespace: Optional[str] = None,
|
|
2950
|
+
entity_id: Optional[str] = None,
|
|
2951
|
+
entity_type: Optional[str] = None,
|
|
2952
|
+
) -> Optional[Dict[str, Any]]:
|
|
2953
|
+
"""Retrieve a learning record.
|
|
2954
|
+
|
|
2955
|
+
Args:
|
|
2956
|
+
learning_type: Type of learning ('user_profile', 'session_context', etc.)
|
|
2957
|
+
user_id: Filter by user ID.
|
|
2958
|
+
agent_id: Filter by agent ID.
|
|
2959
|
+
team_id: Filter by team ID.
|
|
2960
|
+
workflow_id: Filter by workflow ID.
|
|
2961
|
+
session_id: Filter by session ID.
|
|
2962
|
+
namespace: Filter by namespace ('user', 'global', or custom).
|
|
2963
|
+
entity_id: Filter by entity ID (for entity-specific learnings).
|
|
2964
|
+
entity_type: Filter by entity type ('person', 'company', etc.).
|
|
2965
|
+
|
|
2966
|
+
Returns:
|
|
2967
|
+
Dict with 'content' key containing the learning data, or None.
|
|
2968
|
+
"""
|
|
2969
|
+
try:
|
|
2970
|
+
table = await self._get_table(table_type="learnings")
|
|
2971
|
+
if table is None:
|
|
2972
|
+
return None
|
|
2973
|
+
|
|
2974
|
+
async with self.async_session_factory() as sess:
|
|
2975
|
+
stmt = select(table).where(table.c.learning_type == learning_type)
|
|
2976
|
+
|
|
2977
|
+
if user_id is not None:
|
|
2978
|
+
stmt = stmt.where(table.c.user_id == user_id)
|
|
2979
|
+
if agent_id is not None:
|
|
2980
|
+
stmt = stmt.where(table.c.agent_id == agent_id)
|
|
2981
|
+
if team_id is not None:
|
|
2982
|
+
stmt = stmt.where(table.c.team_id == team_id)
|
|
2983
|
+
if workflow_id is not None:
|
|
2984
|
+
stmt = stmt.where(table.c.workflow_id == workflow_id)
|
|
2985
|
+
if session_id is not None:
|
|
2986
|
+
stmt = stmt.where(table.c.session_id == session_id)
|
|
2987
|
+
if namespace is not None:
|
|
2988
|
+
stmt = stmt.where(table.c.namespace == namespace)
|
|
2989
|
+
if entity_id is not None:
|
|
2990
|
+
stmt = stmt.where(table.c.entity_id == entity_id)
|
|
2991
|
+
if entity_type is not None:
|
|
2992
|
+
stmt = stmt.where(table.c.entity_type == entity_type)
|
|
2993
|
+
|
|
2994
|
+
result = await sess.execute(stmt)
|
|
2995
|
+
row = result.fetchone()
|
|
2996
|
+
if row is None:
|
|
2997
|
+
return None
|
|
2998
|
+
|
|
2999
|
+
row_dict = dict(row._mapping)
|
|
3000
|
+
return {"content": row_dict.get("content")}
|
|
3001
|
+
|
|
3002
|
+
except Exception as e:
|
|
3003
|
+
log_debug(f"Error retrieving learning: {e}")
|
|
3004
|
+
return None
|
|
3005
|
+
|
|
3006
|
+
async def upsert_learning(
|
|
3007
|
+
self,
|
|
3008
|
+
id: str,
|
|
3009
|
+
learning_type: str,
|
|
3010
|
+
content: Dict[str, Any],
|
|
3011
|
+
user_id: Optional[str] = None,
|
|
3012
|
+
agent_id: Optional[str] = None,
|
|
3013
|
+
team_id: Optional[str] = None,
|
|
3014
|
+
workflow_id: Optional[str] = None,
|
|
3015
|
+
session_id: Optional[str] = None,
|
|
3016
|
+
namespace: Optional[str] = None,
|
|
3017
|
+
entity_id: Optional[str] = None,
|
|
3018
|
+
entity_type: Optional[str] = None,
|
|
3019
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
3020
|
+
) -> None:
|
|
3021
|
+
"""Insert or update a learning record.
|
|
3022
|
+
|
|
3023
|
+
Args:
|
|
3024
|
+
id: Unique identifier for the learning.
|
|
3025
|
+
learning_type: Type of learning ('user_profile', 'session_context', etc.)
|
|
3026
|
+
content: The learning content as a dict.
|
|
3027
|
+
user_id: Associated user ID.
|
|
3028
|
+
agent_id: Associated agent ID.
|
|
3029
|
+
team_id: Associated team ID.
|
|
3030
|
+
workflow_id: Associated workflow ID.
|
|
3031
|
+
session_id: Associated session ID.
|
|
3032
|
+
namespace: Namespace for scoping ('user', 'global', or custom).
|
|
3033
|
+
entity_id: Associated entity ID (for entity-specific learnings).
|
|
3034
|
+
entity_type: Entity type ('person', 'company', etc.).
|
|
3035
|
+
metadata: Optional metadata.
|
|
3036
|
+
"""
|
|
3037
|
+
try:
|
|
3038
|
+
table = await self._get_table(table_type="learnings", create_table_if_not_found=True)
|
|
3039
|
+
if table is None:
|
|
3040
|
+
return
|
|
3041
|
+
|
|
3042
|
+
current_time = int(time.time())
|
|
3043
|
+
|
|
3044
|
+
async with self.async_session_factory() as sess, sess.begin():
|
|
3045
|
+
stmt = sqlite.insert(table).values(
|
|
3046
|
+
learning_id=id,
|
|
3047
|
+
learning_type=learning_type,
|
|
3048
|
+
namespace=namespace,
|
|
3049
|
+
user_id=user_id,
|
|
3050
|
+
agent_id=agent_id,
|
|
3051
|
+
team_id=team_id,
|
|
3052
|
+
workflow_id=workflow_id,
|
|
3053
|
+
session_id=session_id,
|
|
3054
|
+
entity_id=entity_id,
|
|
3055
|
+
entity_type=entity_type,
|
|
3056
|
+
content=content,
|
|
3057
|
+
metadata=metadata,
|
|
3058
|
+
created_at=current_time,
|
|
3059
|
+
updated_at=current_time,
|
|
3060
|
+
)
|
|
3061
|
+
stmt = stmt.on_conflict_do_update(
|
|
3062
|
+
index_elements=["learning_id"],
|
|
3063
|
+
set_=dict(
|
|
3064
|
+
content=content,
|
|
3065
|
+
metadata=metadata,
|
|
3066
|
+
updated_at=current_time,
|
|
3067
|
+
),
|
|
3068
|
+
)
|
|
3069
|
+
await sess.execute(stmt)
|
|
3070
|
+
|
|
3071
|
+
log_debug(f"Upserted learning: {id}")
|
|
3072
|
+
|
|
3073
|
+
except Exception as e:
|
|
3074
|
+
log_debug(f"Error upserting learning: {e}")
|
|
3075
|
+
|
|
3076
|
+
async def delete_learning(self, id: str) -> bool:
|
|
3077
|
+
"""Delete a learning record.
|
|
3078
|
+
|
|
3079
|
+
Args:
|
|
3080
|
+
id: The learning ID to delete.
|
|
3081
|
+
|
|
3082
|
+
Returns:
|
|
3083
|
+
True if deleted, False otherwise.
|
|
3084
|
+
"""
|
|
3085
|
+
try:
|
|
3086
|
+
table = await self._get_table(table_type="learnings")
|
|
3087
|
+
if table is None:
|
|
3088
|
+
return False
|
|
3089
|
+
|
|
3090
|
+
async with self.async_session_factory() as sess, sess.begin():
|
|
3091
|
+
stmt = table.delete().where(table.c.learning_id == id)
|
|
3092
|
+
result = await sess.execute(stmt)
|
|
3093
|
+
return result.rowcount > 0
|
|
3094
|
+
|
|
3095
|
+
except Exception as e:
|
|
3096
|
+
log_debug(f"Error deleting learning: {e}")
|
|
3097
|
+
return False
|
|
3098
|
+
|
|
3099
|
+
async def get_learnings(
|
|
3100
|
+
self,
|
|
3101
|
+
learning_type: Optional[str] = None,
|
|
3102
|
+
user_id: Optional[str] = None,
|
|
3103
|
+
agent_id: Optional[str] = None,
|
|
3104
|
+
team_id: Optional[str] = None,
|
|
3105
|
+
workflow_id: Optional[str] = None,
|
|
3106
|
+
session_id: Optional[str] = None,
|
|
3107
|
+
namespace: Optional[str] = None,
|
|
3108
|
+
entity_id: Optional[str] = None,
|
|
3109
|
+
entity_type: Optional[str] = None,
|
|
3110
|
+
limit: Optional[int] = None,
|
|
3111
|
+
) -> List[Dict[str, Any]]:
|
|
3112
|
+
"""Get multiple learning records.
|
|
3113
|
+
|
|
3114
|
+
Args:
|
|
3115
|
+
learning_type: Filter by learning type.
|
|
3116
|
+
user_id: Filter by user ID.
|
|
3117
|
+
agent_id: Filter by agent ID.
|
|
3118
|
+
team_id: Filter by team ID.
|
|
3119
|
+
workflow_id: Filter by workflow ID.
|
|
3120
|
+
session_id: Filter by session ID.
|
|
3121
|
+
namespace: Filter by namespace ('user', 'global', or custom).
|
|
3122
|
+
entity_id: Filter by entity ID (for entity-specific learnings).
|
|
3123
|
+
entity_type: Filter by entity type ('person', 'company', etc.).
|
|
3124
|
+
limit: Maximum number of records to return.
|
|
3125
|
+
|
|
3126
|
+
Returns:
|
|
3127
|
+
List of learning records.
|
|
3128
|
+
"""
|
|
3129
|
+
try:
|
|
3130
|
+
table = await self._get_table(table_type="learnings")
|
|
3131
|
+
if table is None:
|
|
3132
|
+
return []
|
|
3133
|
+
|
|
3134
|
+
async with self.async_session_factory() as sess:
|
|
3135
|
+
stmt = select(table)
|
|
3136
|
+
|
|
3137
|
+
if learning_type is not None:
|
|
3138
|
+
stmt = stmt.where(table.c.learning_type == learning_type)
|
|
3139
|
+
if user_id is not None:
|
|
3140
|
+
stmt = stmt.where(table.c.user_id == user_id)
|
|
3141
|
+
if agent_id is not None:
|
|
3142
|
+
stmt = stmt.where(table.c.agent_id == agent_id)
|
|
3143
|
+
if team_id is not None:
|
|
3144
|
+
stmt = stmt.where(table.c.team_id == team_id)
|
|
3145
|
+
if workflow_id is not None:
|
|
3146
|
+
stmt = stmt.where(table.c.workflow_id == workflow_id)
|
|
3147
|
+
if session_id is not None:
|
|
3148
|
+
stmt = stmt.where(table.c.session_id == session_id)
|
|
3149
|
+
if namespace is not None:
|
|
3150
|
+
stmt = stmt.where(table.c.namespace == namespace)
|
|
3151
|
+
if entity_id is not None:
|
|
3152
|
+
stmt = stmt.where(table.c.entity_id == entity_id)
|
|
3153
|
+
if entity_type is not None:
|
|
3154
|
+
stmt = stmt.where(table.c.entity_type == entity_type)
|
|
3155
|
+
|
|
3156
|
+
stmt = stmt.order_by(table.c.updated_at.desc())
|
|
3157
|
+
|
|
3158
|
+
if limit is not None:
|
|
3159
|
+
stmt = stmt.limit(limit)
|
|
3160
|
+
|
|
3161
|
+
result = await sess.execute(stmt)
|
|
3162
|
+
results = result.fetchall()
|
|
3163
|
+
return [dict(row._mapping) for row in results]
|
|
3164
|
+
|
|
3165
|
+
except Exception as e:
|
|
3166
|
+
log_debug(f"Error getting learnings: {e}")
|
|
3167
|
+
return []
|
agno/db/sqlite/schemas.py
CHANGED
|
@@ -162,6 +162,23 @@ VERSIONS_TABLE_SCHEMA = {
|
|
|
162
162
|
"updated_at": {"type": String, "nullable": True},
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
LEARNINGS_TABLE_SCHEMA = {
|
|
166
|
+
"learning_id": {"type": String, "primary_key": True, "nullable": False},
|
|
167
|
+
"learning_type": {"type": String, "nullable": False, "index": True},
|
|
168
|
+
"namespace": {"type": String, "nullable": True, "index": True},
|
|
169
|
+
"user_id": {"type": String, "nullable": True, "index": True},
|
|
170
|
+
"agent_id": {"type": String, "nullable": True, "index": True},
|
|
171
|
+
"team_id": {"type": String, "nullable": True, "index": True},
|
|
172
|
+
"workflow_id": {"type": String, "nullable": True, "index": True},
|
|
173
|
+
"session_id": {"type": String, "nullable": True, "index": True},
|
|
174
|
+
"entity_id": {"type": String, "nullable": True, "index": True},
|
|
175
|
+
"entity_type": {"type": String, "nullable": True, "index": True},
|
|
176
|
+
"content": {"type": JSON, "nullable": False},
|
|
177
|
+
"metadata": {"type": JSON, "nullable": True},
|
|
178
|
+
"created_at": {"type": BigInteger, "nullable": False, "index": True},
|
|
179
|
+
"updated_at": {"type": BigInteger, "nullable": True},
|
|
180
|
+
}
|
|
181
|
+
|
|
165
182
|
|
|
166
183
|
def get_table_schema_definition(table_type: str, traces_table_name: str = "agno_traces") -> dict[str, Any]:
|
|
167
184
|
"""
|
|
@@ -187,6 +204,7 @@ def get_table_schema_definition(table_type: str, traces_table_name: str = "agno_
|
|
|
187
204
|
"traces": TRACE_TABLE_SCHEMA,
|
|
188
205
|
"culture": CULTURAL_KNOWLEDGE_TABLE_SCHEMA,
|
|
189
206
|
"versions": VERSIONS_TABLE_SCHEMA,
|
|
207
|
+
"learnings": LEARNINGS_TABLE_SCHEMA,
|
|
190
208
|
}
|
|
191
209
|
schema = schemas.get(table_type, {})
|
|
192
210
|
|
agno/db/sqlite/sqlite.py
CHANGED
|
@@ -55,6 +55,7 @@ class SqliteDb(BaseDb):
|
|
|
55
55
|
traces_table: Optional[str] = None,
|
|
56
56
|
spans_table: Optional[str] = None,
|
|
57
57
|
versions_table: Optional[str] = None,
|
|
58
|
+
learnings_table: Optional[str] = None,
|
|
58
59
|
id: Optional[str] = None,
|
|
59
60
|
):
|
|
60
61
|
"""
|
|
@@ -79,6 +80,7 @@ class SqliteDb(BaseDb):
|
|
|
79
80
|
traces_table (Optional[str]): Name of the table to store run traces.
|
|
80
81
|
spans_table (Optional[str]): Name of the table to store span events.
|
|
81
82
|
versions_table (Optional[str]): Name of the table to store schema versions.
|
|
83
|
+
learnings_table (Optional[str]): Name of the table to store learning records.
|
|
82
84
|
id (Optional[str]): ID of the database.
|
|
83
85
|
|
|
84
86
|
Raises:
|
|
@@ -99,6 +101,7 @@ class SqliteDb(BaseDb):
|
|
|
99
101
|
traces_table=traces_table,
|
|
100
102
|
spans_table=spans_table,
|
|
101
103
|
versions_table=versions_table,
|
|
104
|
+
learnings_table=learnings_table,
|
|
102
105
|
)
|
|
103
106
|
|
|
104
107
|
_engine: Optional[Engine] = db_engine
|
|
@@ -156,6 +159,7 @@ class SqliteDb(BaseDb):
|
|
|
156
159
|
(self.eval_table_name, "evals"),
|
|
157
160
|
(self.knowledge_table_name, "knowledge"),
|
|
158
161
|
(self.versions_table_name, "versions"),
|
|
162
|
+
(self.learnings_table_name, "learnings"),
|
|
159
163
|
]
|
|
160
164
|
|
|
161
165
|
for table_name, table_type in tables_to_create:
|
|
@@ -334,6 +338,14 @@ class SqliteDb(BaseDb):
|
|
|
334
338
|
)
|
|
335
339
|
return self.versions_table
|
|
336
340
|
|
|
341
|
+
elif table_type == "learnings":
|
|
342
|
+
self.learnings_table = self._get_or_create_table(
|
|
343
|
+
table_name=self.learnings_table_name,
|
|
344
|
+
table_type="learnings",
|
|
345
|
+
create_table_if_not_found=create_table_if_not_found,
|
|
346
|
+
)
|
|
347
|
+
return self.learnings_table
|
|
348
|
+
|
|
337
349
|
else:
|
|
338
350
|
raise ValueError(f"Unknown table type: '{table_type}'")
|
|
339
351
|
|
|
@@ -2912,3 +2924,230 @@ class SqliteDb(BaseDb):
|
|
|
2912
2924
|
except Exception as e:
|
|
2913
2925
|
log_error(f"Error upserting cultural knowledge: {e}")
|
|
2914
2926
|
raise e
|
|
2927
|
+
|
|
2928
|
+
# -- Learning methods --
|
|
2929
|
+
def get_learning(
|
|
2930
|
+
self,
|
|
2931
|
+
learning_type: str,
|
|
2932
|
+
user_id: Optional[str] = None,
|
|
2933
|
+
agent_id: Optional[str] = None,
|
|
2934
|
+
team_id: Optional[str] = None,
|
|
2935
|
+
workflow_id: Optional[str] = None,
|
|
2936
|
+
session_id: Optional[str] = None,
|
|
2937
|
+
namespace: Optional[str] = None,
|
|
2938
|
+
entity_id: Optional[str] = None,
|
|
2939
|
+
entity_type: Optional[str] = None,
|
|
2940
|
+
) -> Optional[Dict[str, Any]]:
|
|
2941
|
+
"""Retrieve a learning record.
|
|
2942
|
+
|
|
2943
|
+
Args:
|
|
2944
|
+
learning_type: Type of learning ('user_profile', 'session_context', etc.)
|
|
2945
|
+
user_id: Filter by user ID.
|
|
2946
|
+
agent_id: Filter by agent ID.
|
|
2947
|
+
team_id: Filter by team ID.
|
|
2948
|
+
workflow_id: Filter by workflow ID.
|
|
2949
|
+
session_id: Filter by session ID.
|
|
2950
|
+
namespace: Filter by namespace ('user', 'global', or custom).
|
|
2951
|
+
entity_id: Filter by entity ID (for entity-specific learnings).
|
|
2952
|
+
entity_type: Filter by entity type ('person', 'company', etc.).
|
|
2953
|
+
|
|
2954
|
+
Returns:
|
|
2955
|
+
Dict with 'content' key containing the learning data, or None.
|
|
2956
|
+
"""
|
|
2957
|
+
try:
|
|
2958
|
+
table = self._get_table(table_type="learnings")
|
|
2959
|
+
if table is None:
|
|
2960
|
+
return None
|
|
2961
|
+
|
|
2962
|
+
with self.Session() as sess:
|
|
2963
|
+
stmt = select(table).where(table.c.learning_type == learning_type)
|
|
2964
|
+
|
|
2965
|
+
if user_id is not None:
|
|
2966
|
+
stmt = stmt.where(table.c.user_id == user_id)
|
|
2967
|
+
if agent_id is not None:
|
|
2968
|
+
stmt = stmt.where(table.c.agent_id == agent_id)
|
|
2969
|
+
if team_id is not None:
|
|
2970
|
+
stmt = stmt.where(table.c.team_id == team_id)
|
|
2971
|
+
if workflow_id is not None:
|
|
2972
|
+
stmt = stmt.where(table.c.workflow_id == workflow_id)
|
|
2973
|
+
if session_id is not None:
|
|
2974
|
+
stmt = stmt.where(table.c.session_id == session_id)
|
|
2975
|
+
if namespace is not None:
|
|
2976
|
+
stmt = stmt.where(table.c.namespace == namespace)
|
|
2977
|
+
if entity_id is not None:
|
|
2978
|
+
stmt = stmt.where(table.c.entity_id == entity_id)
|
|
2979
|
+
if entity_type is not None:
|
|
2980
|
+
stmt = stmt.where(table.c.entity_type == entity_type)
|
|
2981
|
+
|
|
2982
|
+
result = sess.execute(stmt).fetchone()
|
|
2983
|
+
if result is None:
|
|
2984
|
+
return None
|
|
2985
|
+
|
|
2986
|
+
row = dict(result._mapping)
|
|
2987
|
+
return {"content": row.get("content")}
|
|
2988
|
+
|
|
2989
|
+
except Exception as e:
|
|
2990
|
+
log_debug(f"Error retrieving learning: {e}")
|
|
2991
|
+
return None
|
|
2992
|
+
|
|
2993
|
+
def upsert_learning(
|
|
2994
|
+
self,
|
|
2995
|
+
id: str,
|
|
2996
|
+
learning_type: str,
|
|
2997
|
+
content: Dict[str, Any],
|
|
2998
|
+
user_id: Optional[str] = None,
|
|
2999
|
+
agent_id: Optional[str] = None,
|
|
3000
|
+
team_id: Optional[str] = None,
|
|
3001
|
+
workflow_id: Optional[str] = None,
|
|
3002
|
+
session_id: Optional[str] = None,
|
|
3003
|
+
namespace: Optional[str] = None,
|
|
3004
|
+
entity_id: Optional[str] = None,
|
|
3005
|
+
entity_type: Optional[str] = None,
|
|
3006
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
3007
|
+
) -> None:
|
|
3008
|
+
"""Insert or update a learning record.
|
|
3009
|
+
|
|
3010
|
+
Args:
|
|
3011
|
+
id: Unique identifier for the learning.
|
|
3012
|
+
learning_type: Type of learning ('user_profile', 'session_context', etc.)
|
|
3013
|
+
content: The learning content as a dict.
|
|
3014
|
+
user_id: Associated user ID.
|
|
3015
|
+
agent_id: Associated agent ID.
|
|
3016
|
+
team_id: Associated team ID.
|
|
3017
|
+
workflow_id: Associated workflow ID.
|
|
3018
|
+
session_id: Associated session ID.
|
|
3019
|
+
namespace: Namespace for scoping ('user', 'global', or custom).
|
|
3020
|
+
entity_id: Associated entity ID (for entity-specific learnings).
|
|
3021
|
+
entity_type: Entity type ('person', 'company', etc.).
|
|
3022
|
+
metadata: Optional metadata.
|
|
3023
|
+
"""
|
|
3024
|
+
try:
|
|
3025
|
+
table = self._get_table(table_type="learnings", create_table_if_not_found=True)
|
|
3026
|
+
if table is None:
|
|
3027
|
+
return
|
|
3028
|
+
|
|
3029
|
+
current_time = int(time.time())
|
|
3030
|
+
|
|
3031
|
+
with self.Session() as sess, sess.begin():
|
|
3032
|
+
stmt = sqlite.insert(table).values(
|
|
3033
|
+
learning_id=id,
|
|
3034
|
+
learning_type=learning_type,
|
|
3035
|
+
namespace=namespace,
|
|
3036
|
+
user_id=user_id,
|
|
3037
|
+
agent_id=agent_id,
|
|
3038
|
+
team_id=team_id,
|
|
3039
|
+
workflow_id=workflow_id,
|
|
3040
|
+
session_id=session_id,
|
|
3041
|
+
entity_id=entity_id,
|
|
3042
|
+
entity_type=entity_type,
|
|
3043
|
+
content=content,
|
|
3044
|
+
metadata=metadata,
|
|
3045
|
+
created_at=current_time,
|
|
3046
|
+
updated_at=current_time,
|
|
3047
|
+
)
|
|
3048
|
+
stmt = stmt.on_conflict_do_update(
|
|
3049
|
+
index_elements=["learning_id"],
|
|
3050
|
+
set_=dict(
|
|
3051
|
+
content=content,
|
|
3052
|
+
metadata=metadata,
|
|
3053
|
+
updated_at=current_time,
|
|
3054
|
+
),
|
|
3055
|
+
)
|
|
3056
|
+
sess.execute(stmt)
|
|
3057
|
+
|
|
3058
|
+
log_debug(f"Upserted learning: {id}")
|
|
3059
|
+
|
|
3060
|
+
except Exception as e:
|
|
3061
|
+
log_debug(f"Error upserting learning: {e}")
|
|
3062
|
+
|
|
3063
|
+
def delete_learning(self, id: str) -> bool:
|
|
3064
|
+
"""Delete a learning record.
|
|
3065
|
+
|
|
3066
|
+
Args:
|
|
3067
|
+
id: The learning ID to delete.
|
|
3068
|
+
|
|
3069
|
+
Returns:
|
|
3070
|
+
True if deleted, False otherwise.
|
|
3071
|
+
"""
|
|
3072
|
+
try:
|
|
3073
|
+
table = self._get_table(table_type="learnings")
|
|
3074
|
+
if table is None:
|
|
3075
|
+
return False
|
|
3076
|
+
|
|
3077
|
+
with self.Session() as sess, sess.begin():
|
|
3078
|
+
stmt = table.delete().where(table.c.learning_id == id)
|
|
3079
|
+
result = sess.execute(stmt)
|
|
3080
|
+
return result.rowcount > 0
|
|
3081
|
+
|
|
3082
|
+
except Exception as e:
|
|
3083
|
+
log_debug(f"Error deleting learning: {e}")
|
|
3084
|
+
return False
|
|
3085
|
+
|
|
3086
|
+
def get_learnings(
|
|
3087
|
+
self,
|
|
3088
|
+
learning_type: Optional[str] = None,
|
|
3089
|
+
user_id: Optional[str] = None,
|
|
3090
|
+
agent_id: Optional[str] = None,
|
|
3091
|
+
team_id: Optional[str] = None,
|
|
3092
|
+
workflow_id: Optional[str] = None,
|
|
3093
|
+
session_id: Optional[str] = None,
|
|
3094
|
+
namespace: Optional[str] = None,
|
|
3095
|
+
entity_id: Optional[str] = None,
|
|
3096
|
+
entity_type: Optional[str] = None,
|
|
3097
|
+
limit: Optional[int] = None,
|
|
3098
|
+
) -> List[Dict[str, Any]]:
|
|
3099
|
+
"""Get multiple learning records.
|
|
3100
|
+
|
|
3101
|
+
Args:
|
|
3102
|
+
learning_type: Filter by learning type.
|
|
3103
|
+
user_id: Filter by user ID.
|
|
3104
|
+
agent_id: Filter by agent ID.
|
|
3105
|
+
team_id: Filter by team ID.
|
|
3106
|
+
workflow_id: Filter by workflow ID.
|
|
3107
|
+
session_id: Filter by session ID.
|
|
3108
|
+
namespace: Filter by namespace ('user', 'global', or custom).
|
|
3109
|
+
entity_id: Filter by entity ID (for entity-specific learnings).
|
|
3110
|
+
entity_type: Filter by entity type ('person', 'company', etc.).
|
|
3111
|
+
limit: Maximum number of records to return.
|
|
3112
|
+
|
|
3113
|
+
Returns:
|
|
3114
|
+
List of learning records.
|
|
3115
|
+
"""
|
|
3116
|
+
try:
|
|
3117
|
+
table = self._get_table(table_type="learnings")
|
|
3118
|
+
if table is None:
|
|
3119
|
+
return []
|
|
3120
|
+
|
|
3121
|
+
with self.Session() as sess:
|
|
3122
|
+
stmt = select(table)
|
|
3123
|
+
|
|
3124
|
+
if learning_type is not None:
|
|
3125
|
+
stmt = stmt.where(table.c.learning_type == learning_type)
|
|
3126
|
+
if user_id is not None:
|
|
3127
|
+
stmt = stmt.where(table.c.user_id == user_id)
|
|
3128
|
+
if agent_id is not None:
|
|
3129
|
+
stmt = stmt.where(table.c.agent_id == agent_id)
|
|
3130
|
+
if team_id is not None:
|
|
3131
|
+
stmt = stmt.where(table.c.team_id == team_id)
|
|
3132
|
+
if workflow_id is not None:
|
|
3133
|
+
stmt = stmt.where(table.c.workflow_id == workflow_id)
|
|
3134
|
+
if session_id is not None:
|
|
3135
|
+
stmt = stmt.where(table.c.session_id == session_id)
|
|
3136
|
+
if namespace is not None:
|
|
3137
|
+
stmt = stmt.where(table.c.namespace == namespace)
|
|
3138
|
+
if entity_id is not None:
|
|
3139
|
+
stmt = stmt.where(table.c.entity_id == entity_id)
|
|
3140
|
+
if entity_type is not None:
|
|
3141
|
+
stmt = stmt.where(table.c.entity_type == entity_type)
|
|
3142
|
+
|
|
3143
|
+
stmt = stmt.order_by(table.c.updated_at.desc())
|
|
3144
|
+
|
|
3145
|
+
if limit is not None:
|
|
3146
|
+
stmt = stmt.limit(limit)
|
|
3147
|
+
|
|
3148
|
+
results = sess.execute(stmt).fetchall()
|
|
3149
|
+
return [dict(row._mapping) for row in results]
|
|
3150
|
+
|
|
3151
|
+
except Exception as e:
|
|
3152
|
+
log_debug(f"Error getting learnings: {e}")
|
|
3153
|
+
return []
|
agno/db/surrealdb/surrealdb.py
CHANGED
|
@@ -1906,3 +1906,50 @@ class SurrealDb(BaseDb):
|
|
|
1906
1906
|
span_data[field] = span_data[field].isoformat()
|
|
1907
1907
|
|
|
1908
1908
|
return Span.from_dict(span_data)
|
|
1909
|
+
|
|
1910
|
+
# -- Learning methods (stubs) --
|
|
1911
|
+
def get_learning(
|
|
1912
|
+
self,
|
|
1913
|
+
learning_type: str,
|
|
1914
|
+
user_id: Optional[str] = None,
|
|
1915
|
+
agent_id: Optional[str] = None,
|
|
1916
|
+
team_id: Optional[str] = None,
|
|
1917
|
+
session_id: Optional[str] = None,
|
|
1918
|
+
namespace: Optional[str] = None,
|
|
1919
|
+
entity_id: Optional[str] = None,
|
|
1920
|
+
entity_type: Optional[str] = None,
|
|
1921
|
+
) -> Optional[Dict[str, Any]]:
|
|
1922
|
+
raise NotImplementedError("Learning methods not yet implemented for SurrealDb")
|
|
1923
|
+
|
|
1924
|
+
def upsert_learning(
|
|
1925
|
+
self,
|
|
1926
|
+
id: str,
|
|
1927
|
+
learning_type: str,
|
|
1928
|
+
content: Dict[str, Any],
|
|
1929
|
+
user_id: Optional[str] = None,
|
|
1930
|
+
agent_id: Optional[str] = None,
|
|
1931
|
+
team_id: Optional[str] = None,
|
|
1932
|
+
session_id: Optional[str] = None,
|
|
1933
|
+
namespace: Optional[str] = None,
|
|
1934
|
+
entity_id: Optional[str] = None,
|
|
1935
|
+
entity_type: Optional[str] = None,
|
|
1936
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
1937
|
+
) -> None:
|
|
1938
|
+
raise NotImplementedError("Learning methods not yet implemented for SurrealDb")
|
|
1939
|
+
|
|
1940
|
+
def delete_learning(self, id: str) -> bool:
|
|
1941
|
+
raise NotImplementedError("Learning methods not yet implemented for SurrealDb")
|
|
1942
|
+
|
|
1943
|
+
def get_learnings(
|
|
1944
|
+
self,
|
|
1945
|
+
learning_type: Optional[str] = None,
|
|
1946
|
+
user_id: Optional[str] = None,
|
|
1947
|
+
agent_id: Optional[str] = None,
|
|
1948
|
+
team_id: Optional[str] = None,
|
|
1949
|
+
session_id: Optional[str] = None,
|
|
1950
|
+
namespace: Optional[str] = None,
|
|
1951
|
+
entity_id: Optional[str] = None,
|
|
1952
|
+
entity_type: Optional[str] = None,
|
|
1953
|
+
limit: Optional[int] = None,
|
|
1954
|
+
) -> List[Dict[str, Any]]:
|
|
1955
|
+
raise NotImplementedError("Learning methods not yet implemented for SurrealDb")
|