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/agent/agent.py
CHANGED
|
@@ -46,6 +46,7 @@ from agno.filters import FilterExpr
|
|
|
46
46
|
from agno.guardrails import BaseGuardrail
|
|
47
47
|
from agno.knowledge.knowledge import Knowledge
|
|
48
48
|
from agno.knowledge.types import KnowledgeFilter
|
|
49
|
+
from agno.learn.machine import LearningMachine
|
|
49
50
|
from agno.media import Audio, File, Image, Video
|
|
50
51
|
from agno.memory import MemoryManager
|
|
51
52
|
from agno.models.base import Model
|
|
@@ -359,6 +360,12 @@ class Agent:
|
|
|
359
360
|
# If True, resolve session_state, dependencies, and metadata in the user and system messages
|
|
360
361
|
resolve_in_context: bool = True
|
|
361
362
|
|
|
363
|
+
# --- Learning Machine ---
|
|
364
|
+
# LearningMachine for unified learning capabilities
|
|
365
|
+
learning: Optional[Union[bool, LearningMachine]] = None
|
|
366
|
+
# Add learnings context to system prompt
|
|
367
|
+
add_learnings_to_context: bool = True
|
|
368
|
+
|
|
362
369
|
# --- Extra Messages ---
|
|
363
370
|
# A list of extra messages added after the system message and before the user message.
|
|
364
371
|
# Use these for few-shot learning or to provide additional context to the Model.
|
|
@@ -527,6 +534,8 @@ class Agent:
|
|
|
527
534
|
add_location_to_context: bool = False,
|
|
528
535
|
timezone_identifier: Optional[str] = None,
|
|
529
536
|
resolve_in_context: bool = True,
|
|
537
|
+
learning: Optional[Union[bool, LearningMachine]] = None,
|
|
538
|
+
add_learnings_to_context: bool = True,
|
|
530
539
|
additional_input: Optional[List[Union[str, Dict, BaseModel, Message]]] = None,
|
|
531
540
|
user_message_role: str = "user",
|
|
532
541
|
build_user_context: bool = True,
|
|
@@ -656,6 +665,8 @@ class Agent:
|
|
|
656
665
|
self.add_location_to_context = add_location_to_context
|
|
657
666
|
self.timezone_identifier = timezone_identifier
|
|
658
667
|
self.resolve_in_context = resolve_in_context
|
|
668
|
+
self.learning = learning
|
|
669
|
+
self.add_learnings_to_context = add_learnings_to_context
|
|
659
670
|
self.additional_input = additional_input
|
|
660
671
|
self.user_message_role = user_message_role
|
|
661
672
|
self.build_user_context = build_user_context
|
|
@@ -705,6 +716,10 @@ class Agent:
|
|
|
705
716
|
self.debug_level = debug_level
|
|
706
717
|
self.telemetry = telemetry
|
|
707
718
|
|
|
719
|
+
# Internal use: _learning holds the resolved LearningMachine instance
|
|
720
|
+
# use get_learning_machine() to access it.
|
|
721
|
+
self._learning: Optional[LearningMachine] = None
|
|
722
|
+
|
|
708
723
|
# If we are caching the agent session
|
|
709
724
|
self._cached_session: Optional[AgentSession] = None
|
|
710
725
|
|
|
@@ -811,6 +826,49 @@ class Agent:
|
|
|
811
826
|
self.enable_user_memories or self.enable_agentic_memory or self.memory_manager is not None
|
|
812
827
|
)
|
|
813
828
|
|
|
829
|
+
def _set_learning_machine(self) -> None:
|
|
830
|
+
"""Initialize LearningMachine with agent's db and model.
|
|
831
|
+
|
|
832
|
+
Sets the internal _learning field without modifying the public learning field.
|
|
833
|
+
|
|
834
|
+
Handles:
|
|
835
|
+
- learning=True: Create default LearningMachine
|
|
836
|
+
- learning=False/None: Disabled
|
|
837
|
+
- learning=LearningMachine(...): Use provided, inject db/model/knowledge
|
|
838
|
+
"""
|
|
839
|
+
# Handle learning=False or learning=None
|
|
840
|
+
if self.learning is None or self.learning is False:
|
|
841
|
+
self._learning = None
|
|
842
|
+
return
|
|
843
|
+
|
|
844
|
+
# Check db requirement
|
|
845
|
+
if self.db is None:
|
|
846
|
+
log_warning("Database not provided. LearningMachine not initialized.")
|
|
847
|
+
self._learning = None
|
|
848
|
+
return
|
|
849
|
+
|
|
850
|
+
# Handle learning=True: create default LearningMachine
|
|
851
|
+
if self.learning is True:
|
|
852
|
+
self._learning = LearningMachine(db=self.db, model=self.model, user_profile=True)
|
|
853
|
+
return
|
|
854
|
+
|
|
855
|
+
# Handle learning=LearningMachine(...): inject dependencies
|
|
856
|
+
if isinstance(self.learning, LearningMachine):
|
|
857
|
+
if self.learning.db is None:
|
|
858
|
+
self.learning.db = self.db
|
|
859
|
+
if self.learning.model is None:
|
|
860
|
+
self.learning.model = self.model
|
|
861
|
+
self._learning = self.learning
|
|
862
|
+
|
|
863
|
+
def get_learning_machine(self) -> Optional[LearningMachine]:
|
|
864
|
+
"""Get the resolved LearningMachine instance.
|
|
865
|
+
|
|
866
|
+
Returns:
|
|
867
|
+
The LearningMachine instance if learning is enabled and initialized,
|
|
868
|
+
None otherwise.
|
|
869
|
+
"""
|
|
870
|
+
return self._learning
|
|
871
|
+
|
|
814
872
|
def _set_session_summary_manager(self) -> None:
|
|
815
873
|
if self.enable_session_summaries and self.session_summary_manager is None:
|
|
816
874
|
self.session_summary_manager = SessionSummaryManager(model=self.model)
|
|
@@ -871,6 +929,8 @@ class Agent:
|
|
|
871
929
|
self._set_session_summary_manager()
|
|
872
930
|
if self.compress_tool_results or self.compression_manager is not None:
|
|
873
931
|
self._set_compression_manager()
|
|
932
|
+
if self.learning is not None and self.learning is not False:
|
|
933
|
+
self._set_learning_machine()
|
|
874
934
|
|
|
875
935
|
log_debug(f"Agent ID: {self.id}", center=True)
|
|
876
936
|
|
|
@@ -1008,6 +1068,7 @@ class Agent:
|
|
|
1008
1068
|
13. Cleanup and store the run response and session
|
|
1009
1069
|
"""
|
|
1010
1070
|
memory_future = None
|
|
1071
|
+
learning_future = None
|
|
1011
1072
|
cultural_knowledge_future = None
|
|
1012
1073
|
|
|
1013
1074
|
try:
|
|
@@ -1083,6 +1144,14 @@ class Agent:
|
|
|
1083
1144
|
existing_future=memory_future,
|
|
1084
1145
|
)
|
|
1085
1146
|
|
|
1147
|
+
# Start learning extraction as a background task (runs concurrently with the main execution)
|
|
1148
|
+
learning_future = self._start_learning_future(
|
|
1149
|
+
run_messages=run_messages,
|
|
1150
|
+
session=session,
|
|
1151
|
+
user_id=user_id,
|
|
1152
|
+
existing_future=learning_future,
|
|
1153
|
+
)
|
|
1154
|
+
|
|
1086
1155
|
# Start cultural knowledge creation in background thread
|
|
1087
1156
|
cultural_knowledge_future = self._start_cultural_knowledge_future(
|
|
1088
1157
|
run_messages=run_messages,
|
|
@@ -1130,6 +1199,7 @@ class Agent:
|
|
|
1130
1199
|
wait_for_open_threads(
|
|
1131
1200
|
memory_future=memory_future, # type: ignore
|
|
1132
1201
|
cultural_knowledge_future=cultural_knowledge_future, # type: ignore
|
|
1202
|
+
learning_future=learning_future, # type: ignore
|
|
1133
1203
|
)
|
|
1134
1204
|
|
|
1135
1205
|
return self._handle_agent_run_paused(
|
|
@@ -1164,6 +1234,7 @@ class Agent:
|
|
|
1164
1234
|
wait_for_open_threads(
|
|
1165
1235
|
memory_future=memory_future, # type: ignore
|
|
1166
1236
|
cultural_knowledge_future=cultural_knowledge_future, # type: ignore
|
|
1237
|
+
learning_future=learning_future, # type: ignore
|
|
1167
1238
|
)
|
|
1168
1239
|
|
|
1169
1240
|
# 12. Create session summary
|
|
@@ -1251,6 +1322,8 @@ class Agent:
|
|
|
1251
1322
|
memory_future.cancel()
|
|
1252
1323
|
if cultural_knowledge_future is not None and not cultural_knowledge_future.done():
|
|
1253
1324
|
cultural_knowledge_future.cancel()
|
|
1325
|
+
if learning_future is not None and not learning_future.done():
|
|
1326
|
+
learning_future.cancel()
|
|
1254
1327
|
|
|
1255
1328
|
# Always disconnect connectable tools
|
|
1256
1329
|
self._disconnect_connectable_tools()
|
|
@@ -1290,6 +1363,7 @@ class Agent:
|
|
|
1290
1363
|
10. Cleanup and store the run response and session
|
|
1291
1364
|
"""
|
|
1292
1365
|
memory_future = None
|
|
1366
|
+
learning_future = None
|
|
1293
1367
|
cultural_knowledge_future = None
|
|
1294
1368
|
|
|
1295
1369
|
try:
|
|
@@ -1366,6 +1440,14 @@ class Agent:
|
|
|
1366
1440
|
existing_future=memory_future,
|
|
1367
1441
|
)
|
|
1368
1442
|
|
|
1443
|
+
# Start learning extraction as a background task (runs concurrently with the main execution)
|
|
1444
|
+
learning_future = self._start_learning_future(
|
|
1445
|
+
run_messages=run_messages,
|
|
1446
|
+
session=session,
|
|
1447
|
+
user_id=user_id,
|
|
1448
|
+
existing_future=learning_future,
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1369
1451
|
# Start cultural knowledge creation in background thread
|
|
1370
1452
|
cultural_knowledge_future = self._start_cultural_knowledge_future(
|
|
1371
1453
|
run_messages=run_messages,
|
|
@@ -1454,6 +1536,7 @@ class Agent:
|
|
|
1454
1536
|
yield from wait_for_thread_tasks_stream(
|
|
1455
1537
|
memory_future=memory_future, # type: ignore
|
|
1456
1538
|
cultural_knowledge_future=cultural_knowledge_future, # type: ignore
|
|
1539
|
+
learning_future=learning_future, # type: ignore
|
|
1457
1540
|
stream_events=stream_events,
|
|
1458
1541
|
run_response=run_response,
|
|
1459
1542
|
events_to_skip=self.events_to_skip,
|
|
@@ -1493,6 +1576,7 @@ class Agent:
|
|
|
1493
1576
|
yield from wait_for_thread_tasks_stream(
|
|
1494
1577
|
memory_future=memory_future, # type: ignore
|
|
1495
1578
|
cultural_knowledge_future=cultural_knowledge_future, # type: ignore
|
|
1579
|
+
learning_future=learning_future, # type: ignore
|
|
1496
1580
|
stream_events=stream_events,
|
|
1497
1581
|
run_response=run_response,
|
|
1498
1582
|
)
|
|
@@ -1643,6 +1727,8 @@ class Agent:
|
|
|
1643
1727
|
memory_future.cancel()
|
|
1644
1728
|
if cultural_knowledge_future is not None and not cultural_knowledge_future.done():
|
|
1645
1729
|
cultural_knowledge_future.cancel()
|
|
1730
|
+
if learning_future is not None and not learning_future.done():
|
|
1731
|
+
learning_future.cancel()
|
|
1646
1732
|
|
|
1647
1733
|
# Always disconnect connectable tools
|
|
1648
1734
|
self._disconnect_connectable_tools()
|
|
@@ -1964,8 +2050,9 @@ class Agent:
|
|
|
1964
2050
|
await aregister_run(run_context.run_id)
|
|
1965
2051
|
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
1966
2052
|
|
|
1967
|
-
cultural_knowledge_task = None
|
|
1968
2053
|
memory_task = None
|
|
2054
|
+
learning_task = None
|
|
2055
|
+
cultural_knowledge_task = None
|
|
1969
2056
|
|
|
1970
2057
|
# Set up retry logic
|
|
1971
2058
|
num_attempts = self.retries + 1
|
|
@@ -2063,6 +2150,14 @@ class Agent:
|
|
|
2063
2150
|
existing_task=memory_task,
|
|
2064
2151
|
)
|
|
2065
2152
|
|
|
2153
|
+
# Start learning extraction as a background task
|
|
2154
|
+
learning_task = await self._astart_learning_task(
|
|
2155
|
+
run_messages=run_messages,
|
|
2156
|
+
session=agent_session,
|
|
2157
|
+
user_id=user_id,
|
|
2158
|
+
existing_task=learning_task,
|
|
2159
|
+
)
|
|
2160
|
+
|
|
2066
2161
|
# Start cultural knowledge creation as a background task (runs concurrently with the main execution)
|
|
2067
2162
|
cultural_knowledge_task = await self._astart_cultural_knowledge_task(
|
|
2068
2163
|
run_messages=run_messages,
|
|
@@ -2114,7 +2209,9 @@ class Agent:
|
|
|
2114
2209
|
# We should break out of the run function
|
|
2115
2210
|
if any(tool_call.is_paused for tool_call in run_response.tools or []):
|
|
2116
2211
|
await await_for_open_threads(
|
|
2117
|
-
memory_task=memory_task,
|
|
2212
|
+
memory_task=memory_task,
|
|
2213
|
+
cultural_knowledge_task=cultural_knowledge_task,
|
|
2214
|
+
learning_task=learning_task,
|
|
2118
2215
|
)
|
|
2119
2216
|
return await self._ahandle_agent_run_paused(
|
|
2120
2217
|
run_response=run_response, session=agent_session, user_id=user_id
|
|
@@ -2146,7 +2243,9 @@ class Agent:
|
|
|
2146
2243
|
|
|
2147
2244
|
# 14. Wait for background memory creation
|
|
2148
2245
|
await await_for_open_threads(
|
|
2149
|
-
memory_task=memory_task,
|
|
2246
|
+
memory_task=memory_task,
|
|
2247
|
+
cultural_knowledge_task=cultural_knowledge_task,
|
|
2248
|
+
learning_task=learning_task,
|
|
2150
2249
|
)
|
|
2151
2250
|
|
|
2152
2251
|
# 15. Create session summary
|
|
@@ -2262,6 +2361,12 @@ class Agent:
|
|
|
2262
2361
|
await cultural_knowledge_task
|
|
2263
2362
|
except asyncio.CancelledError:
|
|
2264
2363
|
pass
|
|
2364
|
+
if learning_task is not None and not learning_task.done():
|
|
2365
|
+
learning_task.cancel()
|
|
2366
|
+
try:
|
|
2367
|
+
await learning_task
|
|
2368
|
+
except asyncio.CancelledError:
|
|
2369
|
+
pass
|
|
2265
2370
|
|
|
2266
2371
|
# Always clean up the run tracking
|
|
2267
2372
|
await acleanup_run(run_response.run_id) # type: ignore
|
|
@@ -2306,6 +2411,7 @@ class Agent:
|
|
|
2306
2411
|
|
|
2307
2412
|
memory_task = None
|
|
2308
2413
|
cultural_knowledge_task = None
|
|
2414
|
+
learning_task = None
|
|
2309
2415
|
|
|
2310
2416
|
# 1. Read or create session. Reads from the database if provided.
|
|
2311
2417
|
agent_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
@@ -2411,6 +2517,14 @@ class Agent:
|
|
|
2411
2517
|
existing_task=memory_task,
|
|
2412
2518
|
)
|
|
2413
2519
|
|
|
2520
|
+
# Start learning extraction as a background task
|
|
2521
|
+
learning_task = await self._astart_learning_task(
|
|
2522
|
+
run_messages=run_messages,
|
|
2523
|
+
session=agent_session,
|
|
2524
|
+
user_id=user_id,
|
|
2525
|
+
existing_task=learning_task,
|
|
2526
|
+
)
|
|
2527
|
+
|
|
2414
2528
|
# Start cultural knowledge creation as a background task (runs concurrently with the main execution)
|
|
2415
2529
|
cultural_knowledge_task = await self._astart_cultural_knowledge_task(
|
|
2416
2530
|
run_messages=run_messages,
|
|
@@ -2503,6 +2617,7 @@ class Agent:
|
|
|
2503
2617
|
async for item in await_for_thread_tasks_stream(
|
|
2504
2618
|
memory_task=memory_task,
|
|
2505
2619
|
cultural_knowledge_task=cultural_knowledge_task,
|
|
2620
|
+
learning_task=learning_task,
|
|
2506
2621
|
stream_events=stream_events,
|
|
2507
2622
|
run_response=run_response,
|
|
2508
2623
|
):
|
|
@@ -2533,6 +2648,7 @@ class Agent:
|
|
|
2533
2648
|
async for item in await_for_thread_tasks_stream(
|
|
2534
2649
|
memory_task=memory_task,
|
|
2535
2650
|
cultural_knowledge_task=cultural_knowledge_task,
|
|
2651
|
+
learning_task=learning_task,
|
|
2536
2652
|
stream_events=stream_events,
|
|
2537
2653
|
run_response=run_response,
|
|
2538
2654
|
events_to_skip=self.events_to_skip,
|
|
@@ -2728,6 +2844,13 @@ class Agent:
|
|
|
2728
2844
|
except asyncio.CancelledError:
|
|
2729
2845
|
pass
|
|
2730
2846
|
|
|
2847
|
+
if learning_task is not None and not learning_task.done():
|
|
2848
|
+
learning_task.cancel()
|
|
2849
|
+
try:
|
|
2850
|
+
await learning_task
|
|
2851
|
+
except asyncio.CancelledError:
|
|
2852
|
+
pass
|
|
2853
|
+
|
|
2731
2854
|
# Always clean up the run tracking
|
|
2732
2855
|
await acleanup_run(run_response.run_id) # type: ignore
|
|
2733
2856
|
|
|
@@ -6252,6 +6375,93 @@ class Agent:
|
|
|
6252
6375
|
|
|
6253
6376
|
return None
|
|
6254
6377
|
|
|
6378
|
+
def _process_learnings(
|
|
6379
|
+
self,
|
|
6380
|
+
run_messages: RunMessages,
|
|
6381
|
+
session: AgentSession,
|
|
6382
|
+
user_id: Optional[str],
|
|
6383
|
+
) -> None:
|
|
6384
|
+
"""Process learnings from conversation (runs in background thread)."""
|
|
6385
|
+
if self._learning is None:
|
|
6386
|
+
return
|
|
6387
|
+
|
|
6388
|
+
try:
|
|
6389
|
+
# Convert run messages to list format expected by LearningMachine
|
|
6390
|
+
messages = run_messages.messages if run_messages else []
|
|
6391
|
+
|
|
6392
|
+
self._learning.process(
|
|
6393
|
+
messages=messages,
|
|
6394
|
+
user_id=user_id,
|
|
6395
|
+
session_id=session.session_id if session else None,
|
|
6396
|
+
agent_id=self.id,
|
|
6397
|
+
team_id=self.team_id,
|
|
6398
|
+
)
|
|
6399
|
+
log_debug("Learning extraction completed.")
|
|
6400
|
+
except Exception as e:
|
|
6401
|
+
log_warning(f"Error processing learnings: {e}")
|
|
6402
|
+
|
|
6403
|
+
async def _astart_learning_task(
|
|
6404
|
+
self,
|
|
6405
|
+
run_messages: RunMessages,
|
|
6406
|
+
session: AgentSession,
|
|
6407
|
+
user_id: Optional[str],
|
|
6408
|
+
existing_task: Optional[Task] = None,
|
|
6409
|
+
) -> Optional[Task]:
|
|
6410
|
+
"""Start learning extraction as async task.
|
|
6411
|
+
|
|
6412
|
+
Args:
|
|
6413
|
+
run_messages: The run messages containing conversation.
|
|
6414
|
+
session: The agent session.
|
|
6415
|
+
user_id: The user ID for learning extraction.
|
|
6416
|
+
existing_task: An existing task to cancel before starting a new one.
|
|
6417
|
+
|
|
6418
|
+
Returns:
|
|
6419
|
+
A new learning task if conditions are met, None otherwise.
|
|
6420
|
+
"""
|
|
6421
|
+
# Cancel any existing task from a previous retry attempt
|
|
6422
|
+
if existing_task is not None and not existing_task.done():
|
|
6423
|
+
existing_task.cancel()
|
|
6424
|
+
try:
|
|
6425
|
+
await existing_task
|
|
6426
|
+
except CancelledError:
|
|
6427
|
+
pass
|
|
6428
|
+
|
|
6429
|
+
# Create new task if learning is enabled
|
|
6430
|
+
if self._learning is not None:
|
|
6431
|
+
log_debug("Starting learning extraction as async task.")
|
|
6432
|
+
return create_task(
|
|
6433
|
+
self._aprocess_learnings(
|
|
6434
|
+
run_messages=run_messages,
|
|
6435
|
+
session=session,
|
|
6436
|
+
user_id=user_id,
|
|
6437
|
+
)
|
|
6438
|
+
)
|
|
6439
|
+
|
|
6440
|
+
return None
|
|
6441
|
+
|
|
6442
|
+
async def _aprocess_learnings(
|
|
6443
|
+
self,
|
|
6444
|
+
run_messages: RunMessages,
|
|
6445
|
+
session: AgentSession,
|
|
6446
|
+
user_id: Optional[str],
|
|
6447
|
+
) -> None:
|
|
6448
|
+
"""Async process learnings from conversation."""
|
|
6449
|
+
if self._learning is None:
|
|
6450
|
+
return
|
|
6451
|
+
|
|
6452
|
+
try:
|
|
6453
|
+
messages = run_messages.messages if run_messages else []
|
|
6454
|
+
await self._learning.aprocess(
|
|
6455
|
+
messages=messages,
|
|
6456
|
+
user_id=user_id,
|
|
6457
|
+
session_id=session.session_id if session else None,
|
|
6458
|
+
agent_id=self.id,
|
|
6459
|
+
team_id=self.team_id,
|
|
6460
|
+
)
|
|
6461
|
+
log_debug("Learning extraction completed.")
|
|
6462
|
+
except Exception as e:
|
|
6463
|
+
log_warning(f"Error processing learnings: {e}")
|
|
6464
|
+
|
|
6255
6465
|
def _start_memory_future(
|
|
6256
6466
|
self,
|
|
6257
6467
|
run_messages: RunMessages,
|
|
@@ -6285,6 +6495,40 @@ class Agent:
|
|
|
6285
6495
|
|
|
6286
6496
|
return None
|
|
6287
6497
|
|
|
6498
|
+
def _start_learning_future(
|
|
6499
|
+
self,
|
|
6500
|
+
run_messages: RunMessages,
|
|
6501
|
+
session: AgentSession,
|
|
6502
|
+
user_id: Optional[str],
|
|
6503
|
+
existing_future: Optional[Future] = None,
|
|
6504
|
+
) -> Optional[Future]:
|
|
6505
|
+
"""Start learning extraction in background thread.
|
|
6506
|
+
|
|
6507
|
+
Args:
|
|
6508
|
+
run_messages: The run messages containing conversation.
|
|
6509
|
+
session: The agent session.
|
|
6510
|
+
user_id: The user ID for learning extraction.
|
|
6511
|
+
existing_future: An existing future to cancel before starting a new one.
|
|
6512
|
+
|
|
6513
|
+
Returns:
|
|
6514
|
+
A new learning future if conditions are met, None otherwise.
|
|
6515
|
+
"""
|
|
6516
|
+
# Cancel any existing future from a previous retry attempt
|
|
6517
|
+
if existing_future is not None and not existing_future.done():
|
|
6518
|
+
existing_future.cancel()
|
|
6519
|
+
|
|
6520
|
+
# Create new future if learning is enabled
|
|
6521
|
+
if self._learning is not None:
|
|
6522
|
+
log_debug("Starting learning extraction in background thread.")
|
|
6523
|
+
return self.background_executor.submit(
|
|
6524
|
+
self._process_learnings,
|
|
6525
|
+
run_messages=run_messages,
|
|
6526
|
+
session=session,
|
|
6527
|
+
user_id=user_id,
|
|
6528
|
+
)
|
|
6529
|
+
|
|
6530
|
+
return None
|
|
6531
|
+
|
|
6288
6532
|
def _start_cultural_knowledge_future(
|
|
6289
6533
|
self,
|
|
6290
6534
|
run_messages: RunMessages,
|
|
@@ -6376,6 +6620,15 @@ class Agent:
|
|
|
6376
6620
|
if self.enable_agentic_memory:
|
|
6377
6621
|
agent_tools.append(self._get_update_user_memory_function(user_id=user_id, async_mode=False))
|
|
6378
6622
|
|
|
6623
|
+
# Add learning machine tools
|
|
6624
|
+
if self._learning is not None:
|
|
6625
|
+
learning_tools = self._learning.get_tools(
|
|
6626
|
+
user_id=user_id,
|
|
6627
|
+
session_id=session.session_id if session else None,
|
|
6628
|
+
agent_id=self.id,
|
|
6629
|
+
)
|
|
6630
|
+
agent_tools.extend(learning_tools)
|
|
6631
|
+
|
|
6379
6632
|
if self.enable_agentic_culture:
|
|
6380
6633
|
agent_tools.append(self._get_update_cultural_knowledge_function(async_mode=False))
|
|
6381
6634
|
|
|
@@ -6485,6 +6738,18 @@ class Agent:
|
|
|
6485
6738
|
if self.enable_agentic_memory:
|
|
6486
6739
|
agent_tools.append(self._get_update_user_memory_function(user_id=user_id, async_mode=True))
|
|
6487
6740
|
|
|
6741
|
+
# Add learning machine tools (async)
|
|
6742
|
+
if self._learning is not None:
|
|
6743
|
+
learning_tools = await self._learning.aget_tools(
|
|
6744
|
+
user_id=user_id,
|
|
6745
|
+
session_id=session.session_id if session else None,
|
|
6746
|
+
agent_id=self.id,
|
|
6747
|
+
)
|
|
6748
|
+
agent_tools.extend(learning_tools)
|
|
6749
|
+
|
|
6750
|
+
if self.enable_agentic_culture:
|
|
6751
|
+
agent_tools.append(self._get_update_cultural_knowledge_function(async_mode=True))
|
|
6752
|
+
|
|
6488
6753
|
if self.enable_agentic_state:
|
|
6489
6754
|
agent_tools.append(Function(name="update_session_state", entrypoint=self._update_session_state_tool))
|
|
6490
6755
|
|
|
@@ -8097,12 +8362,22 @@ class Agent:
|
|
|
8097
8362
|
"You should ALWAYS prefer information from this conversation over the past summary.\n\n"
|
|
8098
8363
|
)
|
|
8099
8364
|
|
|
8100
|
-
# 3.3.12
|
|
8365
|
+
# 3.3.12 then add learnings to the system prompt
|
|
8366
|
+
if self._learning is not None and self.add_learnings_to_context:
|
|
8367
|
+
learning_context = self._learning.build_context(
|
|
8368
|
+
user_id=user_id,
|
|
8369
|
+
session_id=session.session_id if session else None,
|
|
8370
|
+
agent_id=self.id,
|
|
8371
|
+
)
|
|
8372
|
+
if learning_context:
|
|
8373
|
+
system_message_content += learning_context + "\n"
|
|
8374
|
+
|
|
8375
|
+
# 3.3.13 Add the system message from the Model
|
|
8101
8376
|
system_message_from_model = self.model.get_system_message_for_model(tools)
|
|
8102
8377
|
if system_message_from_model is not None:
|
|
8103
8378
|
system_message_content += system_message_from_model
|
|
8104
8379
|
|
|
8105
|
-
# 3.3.
|
|
8380
|
+
# 3.3.14 Add the JSON output prompt if output_schema is provided and the model does not support native structured outputs or JSON schema outputs
|
|
8106
8381
|
# or if use_json_mode is True
|
|
8107
8382
|
if (
|
|
8108
8383
|
output_schema is not None
|
|
@@ -8114,11 +8389,11 @@ class Agent:
|
|
|
8114
8389
|
):
|
|
8115
8390
|
system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
8116
8391
|
|
|
8117
|
-
# 3.3.
|
|
8392
|
+
# 3.3.15 Add the response model format prompt if output_schema is provided (Pydantic only)
|
|
8118
8393
|
if output_schema is not None and self.parser_model is not None and not isinstance(output_schema, dict):
|
|
8119
8394
|
system_message_content += f"{get_response_model_format_prompt(output_schema)}"
|
|
8120
8395
|
|
|
8121
|
-
# 3.3.
|
|
8396
|
+
# 3.3.16 Add the session state to the system message
|
|
8122
8397
|
if add_session_state_to_context and session_state is not None:
|
|
8123
8398
|
system_message_content += f"\n<session_state>\n{session_state}\n</session_state>\n\n"
|
|
8124
8399
|
|
|
@@ -8449,12 +8724,22 @@ class Agent:
|
|
|
8449
8724
|
"You should ALWAYS prefer information from this conversation over the past summary.\n\n"
|
|
8450
8725
|
)
|
|
8451
8726
|
|
|
8452
|
-
# 3.3.12
|
|
8727
|
+
# 3.3.12 then add learnings to the system prompt
|
|
8728
|
+
if self._learning is not None and self.add_learnings_to_context:
|
|
8729
|
+
learning_context = await self._learning.abuild_context(
|
|
8730
|
+
user_id=user_id,
|
|
8731
|
+
session_id=session.session_id if session else None,
|
|
8732
|
+
agent_id=self.id,
|
|
8733
|
+
)
|
|
8734
|
+
if learning_context:
|
|
8735
|
+
system_message_content += learning_context + "\n"
|
|
8736
|
+
|
|
8737
|
+
# 3.3.13 Add the system message from the Model
|
|
8453
8738
|
system_message_from_model = self.model.get_system_message_for_model(tools)
|
|
8454
8739
|
if system_message_from_model is not None:
|
|
8455
8740
|
system_message_content += system_message_from_model
|
|
8456
8741
|
|
|
8457
|
-
# 3.3.
|
|
8742
|
+
# 3.3.14 Add the JSON output prompt if output_schema is provided and the model does not support native structured outputs or JSON schema outputs
|
|
8458
8743
|
# or if use_json_mode is True
|
|
8459
8744
|
if (
|
|
8460
8745
|
output_schema is not None
|
|
@@ -8466,11 +8751,11 @@ class Agent:
|
|
|
8466
8751
|
):
|
|
8467
8752
|
system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
8468
8753
|
|
|
8469
|
-
# 3.3.
|
|
8754
|
+
# 3.3.15 Add the response model format prompt if output_schema is provided (Pydantic only)
|
|
8470
8755
|
if output_schema is not None and self.parser_model is not None and not isinstance(output_schema, dict):
|
|
8471
8756
|
system_message_content += f"{get_response_model_format_prompt(output_schema)}"
|
|
8472
8757
|
|
|
8473
|
-
# 3.3.
|
|
8758
|
+
# 3.3.16 Add the session state to the system message
|
|
8474
8759
|
if add_session_state_to_context and session_state is not None:
|
|
8475
8760
|
system_message_content += self._get_formatted_session_state_for_system_message(session_state)
|
|
8476
8761
|
|
|
@@ -11291,6 +11576,7 @@ class Agent:
|
|
|
11291
11576
|
"has_memory": self.enable_user_memories is True
|
|
11292
11577
|
or self.enable_agentic_memory is True
|
|
11293
11578
|
or self.memory_manager is not None,
|
|
11579
|
+
"has_learnings": self._learning is not None,
|
|
11294
11580
|
"has_culture": self.enable_agentic_culture is True
|
|
11295
11581
|
or self.update_cultural_knowledge is True
|
|
11296
11582
|
or self.culture_manager is not None,
|