agno 2.3.13__py3-none-any.whl → 2.3.14__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 +1131 -1402
- agno/eval/__init__.py +21 -8
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/models/anthropic/claude.py +4 -1
- agno/models/base.py +8 -4
- agno/models/openai/responses.py +2 -2
- agno/os/app.py +39 -0
- agno/os/interfaces/a2a/router.py +619 -9
- agno/os/interfaces/a2a/utils.py +31 -32
- agno/os/middleware/jwt.py +5 -5
- agno/os/routers/agents/schema.py +14 -1
- agno/os/routers/teams/schema.py +14 -1
- agno/os/utils.py +61 -53
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +91 -1
- agno/reasoning/gemini.py +81 -1
- agno/reasoning/groq.py +103 -1
- agno/reasoning/manager.py +1244 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +113 -1
- agno/reasoning/vertexai.py +85 -1
- agno/run/agent.py +11 -0
- agno/run/base.py +1 -1
- agno/run/team.py +11 -0
- agno/session/team.py +0 -3
- agno/team/team.py +1201 -1445
- agno/utils/events.py +69 -2
- agno/utils/hooks.py +4 -10
- agno/utils/print_response/agent.py +26 -0
- agno/utils/print_response/team.py +11 -0
- agno/utils/prompts.py +8 -6
- agno/utils/string.py +46 -0
- agno/utils/team.py +1 -1
- agno/vectordb/milvus/milvus.py +32 -3
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/METADATA +3 -2
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/RECORD +41 -40
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/WHEEL +0 -0
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/top_level.txt +0 -0
agno/agent/agent.py
CHANGED
|
@@ -9,7 +9,6 @@ from inspect import iscoroutinefunction
|
|
|
9
9
|
from os import getenv
|
|
10
10
|
from textwrap import dedent
|
|
11
11
|
from typing import (
|
|
12
|
-
TYPE_CHECKING,
|
|
13
12
|
Any,
|
|
14
13
|
AsyncIterator,
|
|
15
14
|
Callable,
|
|
@@ -31,13 +30,11 @@ from uuid import uuid4
|
|
|
31
30
|
|
|
32
31
|
from pydantic import BaseModel
|
|
33
32
|
|
|
34
|
-
if TYPE_CHECKING:
|
|
35
|
-
from agno.eval.base import BaseEval
|
|
36
|
-
|
|
37
33
|
from agno.compression.manager import CompressionManager
|
|
38
34
|
from agno.culture.manager import CultureManager
|
|
39
35
|
from agno.db.base import AsyncBaseDb, BaseDb, SessionType, UserMemory
|
|
40
36
|
from agno.db.schemas.culture import CulturalKnowledge
|
|
37
|
+
from agno.eval.base import BaseEval
|
|
41
38
|
from agno.exceptions import (
|
|
42
39
|
InputCheckError,
|
|
43
40
|
OutputCheckError,
|
|
@@ -112,6 +109,7 @@ from agno.utils.agent import (
|
|
|
112
109
|
)
|
|
113
110
|
from agno.utils.common import is_typed_dict, validate_typed_dict
|
|
114
111
|
from agno.utils.events import (
|
|
112
|
+
add_error_event,
|
|
115
113
|
create_parser_model_response_completed_event,
|
|
116
114
|
create_parser_model_response_started_event,
|
|
117
115
|
create_post_hook_completed_event,
|
|
@@ -119,6 +117,7 @@ from agno.utils.events import (
|
|
|
119
117
|
create_pre_hook_completed_event,
|
|
120
118
|
create_pre_hook_started_event,
|
|
121
119
|
create_reasoning_completed_event,
|
|
120
|
+
create_reasoning_content_delta_event,
|
|
122
121
|
create_reasoning_started_event,
|
|
123
122
|
create_reasoning_step_event,
|
|
124
123
|
create_run_cancelled_event,
|
|
@@ -173,7 +172,7 @@ from agno.utils.response import (
|
|
|
173
172
|
get_paused_content,
|
|
174
173
|
)
|
|
175
174
|
from agno.utils.safe_formatter import SafeFormatter
|
|
176
|
-
from agno.utils.string import generate_id_from_name, parse_response_model_str
|
|
175
|
+
from agno.utils.string import generate_id_from_name, parse_response_dict_str, parse_response_model_str
|
|
177
176
|
from agno.utils.timer import Timer
|
|
178
177
|
|
|
179
178
|
|
|
@@ -280,9 +279,9 @@ class Agent:
|
|
|
280
279
|
|
|
281
280
|
# --- Agent Hooks ---
|
|
282
281
|
# Functions called right after agent-session is loaded, before processing starts
|
|
283
|
-
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
282
|
+
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None
|
|
284
283
|
# Functions called after output is generated but before the response is returned
|
|
285
|
-
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
284
|
+
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None
|
|
286
285
|
# If True, run hooks as FastAPI background tasks (non-blocking). Set by AgentOS.
|
|
287
286
|
_run_hooks_in_background: Optional[bool] = None
|
|
288
287
|
|
|
@@ -369,8 +368,9 @@ class Agent:
|
|
|
369
368
|
# --- Agent Response Model Settings ---
|
|
370
369
|
# Provide an input schema to validate the input
|
|
371
370
|
input_schema: Optional[Type[BaseModel]] = None
|
|
372
|
-
# Provide a response model to get the response
|
|
373
|
-
|
|
371
|
+
# Provide a response model to get the response in the implied format.
|
|
372
|
+
# You can use a Pydantic model or a JSON fitting the provider's expected schema.
|
|
373
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None
|
|
374
374
|
# Provide a secondary model to parse the response from the primary model
|
|
375
375
|
parser_model: Optional[Model] = None
|
|
376
376
|
# Provide a prompt for the parser model
|
|
@@ -487,8 +487,8 @@ class Agent:
|
|
|
487
487
|
tool_call_limit: Optional[int] = None,
|
|
488
488
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
489
489
|
tool_hooks: Optional[List[Callable]] = None,
|
|
490
|
-
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
491
|
-
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
490
|
+
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None,
|
|
491
|
+
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None,
|
|
492
492
|
reasoning: bool = False,
|
|
493
493
|
reasoning_model: Optional[Union[Model, str]] = None,
|
|
494
494
|
reasoning_agent: Optional[Agent] = None,
|
|
@@ -522,7 +522,7 @@ class Agent:
|
|
|
522
522
|
parser_model: Optional[Union[Model, str]] = None,
|
|
523
523
|
parser_model_prompt: Optional[str] = None,
|
|
524
524
|
input_schema: Optional[Type[BaseModel]] = None,
|
|
525
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
525
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
526
526
|
parse_response: bool = True,
|
|
527
527
|
output_model: Optional[Union[Model, str]] = None,
|
|
528
528
|
output_model_prompt: Optional[str] = None,
|
|
@@ -1041,90 +1041,91 @@ class Agent:
|
|
|
1041
1041
|
12. Create session summary
|
|
1042
1042
|
13. Cleanup and store the run response and session
|
|
1043
1043
|
"""
|
|
1044
|
+
memory_future = None
|
|
1045
|
+
cultural_knowledge_future = None
|
|
1044
1046
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1047
|
+
try:
|
|
1048
|
+
# 1. Execute pre-hooks
|
|
1049
|
+
run_input = cast(RunInput, run_response.input)
|
|
1050
|
+
self.model = cast(Model, self.model)
|
|
1051
|
+
if self.pre_hooks is not None:
|
|
1052
|
+
# Can modify the run input
|
|
1053
|
+
pre_hook_iterator = self._execute_pre_hooks(
|
|
1054
|
+
hooks=self.pre_hooks, # type: ignore
|
|
1055
|
+
run_response=run_response,
|
|
1056
|
+
run_input=run_input,
|
|
1057
|
+
run_context=run_context,
|
|
1058
|
+
session=session,
|
|
1059
|
+
user_id=user_id,
|
|
1060
|
+
debug_mode=debug_mode,
|
|
1061
|
+
background_tasks=background_tasks,
|
|
1062
|
+
**kwargs,
|
|
1063
|
+
)
|
|
1064
|
+
# Consume the generator without yielding
|
|
1065
|
+
deque(pre_hook_iterator, maxlen=0)
|
|
1066
|
+
|
|
1067
|
+
# 2. Determine tools for model
|
|
1068
|
+
processed_tools = self.get_tools(
|
|
1052
1069
|
run_response=run_response,
|
|
1053
|
-
run_input=run_input,
|
|
1054
1070
|
run_context=run_context,
|
|
1055
1071
|
session=session,
|
|
1056
1072
|
user_id=user_id,
|
|
1057
|
-
debug_mode=debug_mode,
|
|
1058
|
-
background_tasks=background_tasks,
|
|
1059
|
-
**kwargs,
|
|
1060
1073
|
)
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
session=session,
|
|
1069
|
-
user_id=user_id,
|
|
1070
|
-
)
|
|
1071
|
-
_tools = self._determine_tools_for_model(
|
|
1072
|
-
model=self.model,
|
|
1073
|
-
processed_tools=processed_tools,
|
|
1074
|
-
run_response=run_response,
|
|
1075
|
-
session=session,
|
|
1076
|
-
run_context=run_context,
|
|
1077
|
-
)
|
|
1074
|
+
_tools = self._determine_tools_for_model(
|
|
1075
|
+
model=self.model,
|
|
1076
|
+
processed_tools=processed_tools,
|
|
1077
|
+
run_response=run_response,
|
|
1078
|
+
session=session,
|
|
1079
|
+
run_context=run_context,
|
|
1080
|
+
)
|
|
1078
1081
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1082
|
+
# 3. Prepare run messages
|
|
1083
|
+
run_messages: RunMessages = self._get_run_messages(
|
|
1084
|
+
run_response=run_response,
|
|
1085
|
+
run_context=run_context,
|
|
1086
|
+
input=run_input.input_content,
|
|
1087
|
+
session=session,
|
|
1088
|
+
user_id=user_id,
|
|
1089
|
+
audio=run_input.audios,
|
|
1090
|
+
images=run_input.images,
|
|
1091
|
+
videos=run_input.videos,
|
|
1092
|
+
files=run_input.files,
|
|
1093
|
+
add_history_to_context=add_history_to_context,
|
|
1094
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1095
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1096
|
+
tools=_tools,
|
|
1097
|
+
**kwargs,
|
|
1098
|
+
)
|
|
1099
|
+
if len(run_messages.messages) == 0:
|
|
1100
|
+
log_error("No messages to be sent to the model.")
|
|
1098
1101
|
|
|
1099
|
-
|
|
1102
|
+
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
1100
1103
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1104
|
+
# Start memory creation on a separate thread (runs concurrently with the main execution loop)
|
|
1105
|
+
memory_future = None
|
|
1106
|
+
# 4. Start memory creation in background thread if memory manager is enabled and agentic memory is disabled
|
|
1107
|
+
if (
|
|
1108
|
+
run_messages.user_message is not None
|
|
1109
|
+
and self.memory_manager is not None
|
|
1110
|
+
and self.enable_user_memories
|
|
1111
|
+
and not self.enable_agentic_memory
|
|
1112
|
+
):
|
|
1113
|
+
log_debug("Starting memory creation in background thread.")
|
|
1114
|
+
memory_future = self.background_executor.submit(
|
|
1115
|
+
self._make_memories, run_messages=run_messages, user_id=user_id
|
|
1116
|
+
)
|
|
1114
1117
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
)
|
|
1118
|
+
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
1119
|
+
if (
|
|
1120
|
+
run_messages.user_message is not None
|
|
1121
|
+
and self.culture_manager is not None
|
|
1122
|
+
and self.update_cultural_knowledge
|
|
1123
|
+
):
|
|
1124
|
+
log_debug("Starting cultural knowledge creation in background thread.")
|
|
1125
|
+
cultural_knowledge_future = self.background_executor.submit(
|
|
1126
|
+
self._make_cultural_knowledge, run_messages=run_messages
|
|
1127
|
+
)
|
|
1126
1128
|
|
|
1127
|
-
try:
|
|
1128
1129
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1129
1130
|
|
|
1130
1131
|
# 5. Reason about the task
|
|
@@ -1215,18 +1216,6 @@ class Agent:
|
|
|
1215
1216
|
|
|
1216
1217
|
log_debug(f"Agent Run End: {run_response.run_id}", center=True, symbol="*")
|
|
1217
1218
|
|
|
1218
|
-
return run_response
|
|
1219
|
-
except RunCancelledException as e:
|
|
1220
|
-
# Handle run cancellation
|
|
1221
|
-
log_info(f"Run {run_response.run_id} was cancelled")
|
|
1222
|
-
run_response.content = str(e)
|
|
1223
|
-
run_response.status = RunStatus.cancelled
|
|
1224
|
-
|
|
1225
|
-
# Cleanup and store the run response and session
|
|
1226
|
-
self._cleanup_and_store(
|
|
1227
|
-
run_response=run_response, session=session, run_context=run_context, user_id=user_id
|
|
1228
|
-
)
|
|
1229
|
-
|
|
1230
1219
|
return run_response
|
|
1231
1220
|
finally:
|
|
1232
1221
|
# Always disconnect connectable tools
|
|
@@ -1264,91 +1253,92 @@ class Agent:
|
|
|
1264
1253
|
9. Create session summary
|
|
1265
1254
|
10. Cleanup and store the run response and session
|
|
1266
1255
|
"""
|
|
1256
|
+
memory_future = None
|
|
1257
|
+
cultural_knowledge_future = None
|
|
1267
1258
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1259
|
+
try:
|
|
1260
|
+
# 1. Execute pre-hooks
|
|
1261
|
+
run_input = cast(RunInput, run_response.input)
|
|
1262
|
+
self.model = cast(Model, self.model)
|
|
1263
|
+
if self.pre_hooks is not None:
|
|
1264
|
+
# Can modify the run input
|
|
1265
|
+
pre_hook_iterator = self._execute_pre_hooks(
|
|
1266
|
+
hooks=self.pre_hooks, # type: ignore
|
|
1267
|
+
run_response=run_response,
|
|
1268
|
+
run_input=run_input,
|
|
1269
|
+
run_context=run_context,
|
|
1270
|
+
session=session,
|
|
1271
|
+
user_id=user_id,
|
|
1272
|
+
debug_mode=debug_mode,
|
|
1273
|
+
stream_events=stream_events,
|
|
1274
|
+
background_tasks=background_tasks,
|
|
1275
|
+
**kwargs,
|
|
1276
|
+
)
|
|
1277
|
+
for event in pre_hook_iterator:
|
|
1278
|
+
yield event
|
|
1279
|
+
|
|
1280
|
+
# 2. Determine tools for model
|
|
1281
|
+
processed_tools = self.get_tools(
|
|
1275
1282
|
run_response=run_response,
|
|
1276
|
-
run_input=run_input,
|
|
1277
1283
|
run_context=run_context,
|
|
1278
1284
|
session=session,
|
|
1279
1285
|
user_id=user_id,
|
|
1280
|
-
debug_mode=debug_mode,
|
|
1281
|
-
stream_events=stream_events,
|
|
1282
|
-
background_tasks=background_tasks,
|
|
1283
|
-
**kwargs,
|
|
1284
1286
|
)
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
session=session,
|
|
1293
|
-
user_id=user_id,
|
|
1294
|
-
)
|
|
1295
|
-
_tools = self._determine_tools_for_model(
|
|
1296
|
-
model=self.model,
|
|
1297
|
-
processed_tools=processed_tools,
|
|
1298
|
-
run_response=run_response,
|
|
1299
|
-
session=session,
|
|
1300
|
-
run_context=run_context,
|
|
1301
|
-
)
|
|
1287
|
+
_tools = self._determine_tools_for_model(
|
|
1288
|
+
model=self.model,
|
|
1289
|
+
processed_tools=processed_tools,
|
|
1290
|
+
run_response=run_response,
|
|
1291
|
+
session=session,
|
|
1292
|
+
run_context=run_context,
|
|
1293
|
+
)
|
|
1302
1294
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1295
|
+
# 3. Prepare run messages
|
|
1296
|
+
run_messages: RunMessages = self._get_run_messages(
|
|
1297
|
+
run_response=run_response,
|
|
1298
|
+
input=run_input.input_content,
|
|
1299
|
+
session=session,
|
|
1300
|
+
run_context=run_context,
|
|
1301
|
+
user_id=user_id,
|
|
1302
|
+
audio=run_input.audios,
|
|
1303
|
+
images=run_input.images,
|
|
1304
|
+
videos=run_input.videos,
|
|
1305
|
+
files=run_input.files,
|
|
1306
|
+
add_history_to_context=add_history_to_context,
|
|
1307
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1308
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1309
|
+
tools=_tools,
|
|
1310
|
+
**kwargs,
|
|
1311
|
+
)
|
|
1312
|
+
if len(run_messages.messages) == 0:
|
|
1313
|
+
log_error("No messages to be sent to the model.")
|
|
1322
1314
|
|
|
1323
|
-
|
|
1315
|
+
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
1324
1316
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1317
|
+
# Start memory creation on a separate thread (runs concurrently with the main execution loop)
|
|
1318
|
+
memory_future = None
|
|
1319
|
+
# 4. Start memory creation in background thread if memory manager is enabled and agentic memory is disabled
|
|
1320
|
+
if (
|
|
1321
|
+
run_messages.user_message is not None
|
|
1322
|
+
and self.memory_manager is not None
|
|
1323
|
+
and self.enable_user_memories
|
|
1324
|
+
and not self.enable_agentic_memory
|
|
1325
|
+
):
|
|
1326
|
+
log_debug("Starting memory creation in background thread.")
|
|
1327
|
+
memory_future = self.background_executor.submit(
|
|
1328
|
+
self._make_memories, run_messages=run_messages, user_id=user_id
|
|
1329
|
+
)
|
|
1338
1330
|
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
)
|
|
1331
|
+
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
1332
|
+
if (
|
|
1333
|
+
run_messages.user_message is not None
|
|
1334
|
+
and self.culture_manager is not None
|
|
1335
|
+
and self.update_cultural_knowledge
|
|
1336
|
+
):
|
|
1337
|
+
log_debug("Starting cultural knowledge creation in background thread.")
|
|
1338
|
+
cultural_knowledge_future = self.background_executor.submit(
|
|
1339
|
+
self._make_cultural_knowledge, run_messages=run_messages
|
|
1340
|
+
)
|
|
1350
1341
|
|
|
1351
|
-
try:
|
|
1352
1342
|
# Start the Run by yielding a RunStarted event
|
|
1353
1343
|
if stream_events:
|
|
1354
1344
|
yield handle_event( # type: ignore
|
|
@@ -1531,28 +1521,6 @@ class Agent:
|
|
|
1531
1521
|
self._log_agent_telemetry(session_id=session.session_id, run_id=run_response.run_id)
|
|
1532
1522
|
|
|
1533
1523
|
log_debug(f"Agent Run End: {run_response.run_id}", center=True, symbol="*")
|
|
1534
|
-
|
|
1535
|
-
except RunCancelledException as e:
|
|
1536
|
-
# Handle run cancellation during streaming
|
|
1537
|
-
log_info(f"Run {run_response.run_id} was cancelled during streaming")
|
|
1538
|
-
run_response.status = RunStatus.cancelled
|
|
1539
|
-
# Don't overwrite content - preserve any partial content that was streamed
|
|
1540
|
-
# Only set content if it's empty
|
|
1541
|
-
if not run_response.content:
|
|
1542
|
-
run_response.content = str(e)
|
|
1543
|
-
|
|
1544
|
-
# Yield the cancellation event
|
|
1545
|
-
yield handle_event( # type: ignore
|
|
1546
|
-
create_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
1547
|
-
run_response,
|
|
1548
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
1549
|
-
store_events=self.store_events,
|
|
1550
|
-
)
|
|
1551
|
-
|
|
1552
|
-
# Cleanup and store the run response and session
|
|
1553
|
-
self._cleanup_and_store(
|
|
1554
|
-
run_response=run_response, session=session, run_context=run_context, user_id=user_id
|
|
1555
|
-
)
|
|
1556
1524
|
finally:
|
|
1557
1525
|
# Always disconnect connectable tools
|
|
1558
1526
|
self._disconnect_connectable_tools()
|
|
@@ -1582,7 +1550,7 @@ class Agent:
|
|
|
1582
1550
|
add_session_state_to_context: Optional[bool] = None,
|
|
1583
1551
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1584
1552
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1585
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
1553
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
1586
1554
|
debug_mode: Optional[bool] = None,
|
|
1587
1555
|
**kwargs: Any,
|
|
1588
1556
|
) -> RunOutput: ...
|
|
@@ -1610,7 +1578,7 @@ class Agent:
|
|
|
1610
1578
|
add_session_state_to_context: Optional[bool] = None,
|
|
1611
1579
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1612
1580
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1613
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
1581
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
1614
1582
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
1615
1583
|
yield_run_output: bool = False,
|
|
1616
1584
|
debug_mode: Optional[bool] = None,
|
|
@@ -1639,7 +1607,7 @@ class Agent:
|
|
|
1639
1607
|
add_session_state_to_context: Optional[bool] = None,
|
|
1640
1608
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1641
1609
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1642
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
1610
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
1643
1611
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
1644
1612
|
yield_run_output: Optional[bool] = None,
|
|
1645
1613
|
debug_mode: Optional[bool] = None,
|
|
@@ -1651,9 +1619,10 @@ class Agent:
|
|
|
1651
1619
|
"`run` method is not supported with an async database. Please use `arun` method instead."
|
|
1652
1620
|
)
|
|
1653
1621
|
|
|
1654
|
-
#
|
|
1622
|
+
# Initialize session early for error handling
|
|
1623
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
1624
|
+
# Set the id for the run
|
|
1655
1625
|
run_id = run_id or str(uuid4())
|
|
1656
|
-
register_run(run_id)
|
|
1657
1626
|
|
|
1658
1627
|
if (add_history_to_context or self.add_history_to_context) and not self.db and not self.team_id:
|
|
1659
1628
|
log_warning(
|
|
@@ -1667,82 +1636,84 @@ class Agent:
|
|
|
1667
1636
|
stacklevel=2,
|
|
1668
1637
|
)
|
|
1669
1638
|
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1639
|
+
# Set up retry logic
|
|
1640
|
+
num_attempts = self.retries + 1
|
|
1641
|
+
for attempt in range(num_attempts):
|
|
1642
|
+
if num_attempts > 1:
|
|
1643
|
+
log_debug(f"Retrying Agent run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
1673
1644
|
|
|
1674
|
-
|
|
1645
|
+
try:
|
|
1646
|
+
# Register run for cancellation tracking
|
|
1647
|
+
register_run(run_id)
|
|
1675
1648
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1649
|
+
background_tasks = kwargs.pop("background_tasks", None)
|
|
1650
|
+
if background_tasks is not None:
|
|
1651
|
+
from fastapi import BackgroundTasks
|
|
1678
1652
|
|
|
1679
|
-
|
|
1680
|
-
if not self._hooks_normalised:
|
|
1681
|
-
if self.pre_hooks:
|
|
1682
|
-
self.pre_hooks = normalize_pre_hooks(self.pre_hooks) # type: ignore
|
|
1683
|
-
if self.post_hooks:
|
|
1684
|
-
self.post_hooks = normalize_post_hooks(self.post_hooks) # type: ignore
|
|
1685
|
-
self._hooks_normalised = True
|
|
1653
|
+
background_tasks: BackgroundTasks = background_tasks # type: ignore
|
|
1686
1654
|
|
|
1687
|
-
|
|
1655
|
+
# Validate input against input_schema if provided
|
|
1656
|
+
validated_input = self._validate_input(input)
|
|
1688
1657
|
|
|
1689
|
-
|
|
1690
|
-
|
|
1658
|
+
# Normalise hook & guardails
|
|
1659
|
+
if not self._hooks_normalised:
|
|
1660
|
+
if self.pre_hooks:
|
|
1661
|
+
self.pre_hooks = normalize_pre_hooks(self.pre_hooks) # type: ignore
|
|
1662
|
+
if self.post_hooks:
|
|
1663
|
+
self.post_hooks = normalize_post_hooks(self.post_hooks) # type: ignore
|
|
1664
|
+
self._hooks_normalised = True
|
|
1691
1665
|
|
|
1692
|
-
|
|
1693
|
-
images=images, videos=videos, audios=audio, files=files
|
|
1694
|
-
)
|
|
1666
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
1695
1667
|
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
input_content=validated_input,
|
|
1699
|
-
images=image_artifacts,
|
|
1700
|
-
videos=video_artifacts,
|
|
1701
|
-
audios=audio_artifacts,
|
|
1702
|
-
files=file_artifacts,
|
|
1703
|
-
)
|
|
1668
|
+
# Initialize the Agent
|
|
1669
|
+
self.initialize_agent(debug_mode=debug_mode)
|
|
1704
1670
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1671
|
+
image_artifacts, video_artifacts, audio_artifacts, file_artifacts = validate_media_object_id(
|
|
1672
|
+
images=images, videos=videos, audios=audio, files=files
|
|
1673
|
+
)
|
|
1708
1674
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
session_state = self._load_session_state(session=agent_session, session_state=session_state)
|
|
1675
|
+
# Create RunInput to capture the original user input
|
|
1676
|
+
run_input = RunInput(
|
|
1677
|
+
input_content=validated_input,
|
|
1678
|
+
images=image_artifacts,
|
|
1679
|
+
videos=video_artifacts,
|
|
1680
|
+
audios=audio_artifacts,
|
|
1681
|
+
files=file_artifacts,
|
|
1682
|
+
)
|
|
1718
1683
|
|
|
1719
|
-
|
|
1720
|
-
|
|
1684
|
+
# Read existing session from database
|
|
1685
|
+
agent_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
1686
|
+
self._update_metadata(session=agent_session)
|
|
1721
1687
|
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1688
|
+
# Initialize session state
|
|
1689
|
+
session_state = self._initialize_session_state(
|
|
1690
|
+
session_state=session_state if session_state is not None else {},
|
|
1691
|
+
user_id=user_id,
|
|
1692
|
+
session_id=session_id,
|
|
1693
|
+
run_id=run_id,
|
|
1694
|
+
)
|
|
1695
|
+
# Update session state from DB
|
|
1696
|
+
session_state = self._load_session_state(session=agent_session, session_state=session_state)
|
|
1725
1697
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
run_id=run_id,
|
|
1729
|
-
session_id=session_id,
|
|
1730
|
-
user_id=user_id,
|
|
1731
|
-
session_state=session_state,
|
|
1732
|
-
dependencies=dependencies,
|
|
1733
|
-
output_schema=output_schema,
|
|
1734
|
-
)
|
|
1735
|
-
# output_schema parameter takes priority, even if run_context was provided
|
|
1736
|
-
run_context.output_schema = output_schema
|
|
1698
|
+
# Determine runtime dependencies
|
|
1699
|
+
dependencies = dependencies if dependencies is not None else self.dependencies
|
|
1737
1700
|
|
|
1738
|
-
|
|
1739
|
-
|
|
1701
|
+
# Resolve output_schema parameter takes precedence, then fall back to self.output_schema
|
|
1702
|
+
if output_schema is None:
|
|
1703
|
+
output_schema = self.output_schema
|
|
1740
1704
|
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1705
|
+
# Initialize run context
|
|
1706
|
+
run_context = run_context or RunContext(
|
|
1707
|
+
run_id=run_id,
|
|
1708
|
+
session_id=session_id,
|
|
1709
|
+
user_id=user_id,
|
|
1710
|
+
session_state=session_state,
|
|
1711
|
+
dependencies=dependencies,
|
|
1712
|
+
output_schema=output_schema,
|
|
1713
|
+
)
|
|
1714
|
+
# output_schema parameter takes priority, even if run_context was provided
|
|
1715
|
+
run_context.output_schema = output_schema
|
|
1744
1716
|
|
|
1745
|
-
try:
|
|
1746
1717
|
# Resolve dependencies
|
|
1747
1718
|
if run_context.dependencies is not None:
|
|
1748
1719
|
self._resolve_run_dependencies(run_context=run_context)
|
|
@@ -1849,23 +1820,64 @@ class Agent:
|
|
|
1849
1820
|
)
|
|
1850
1821
|
return response
|
|
1851
1822
|
except (InputCheckError, OutputCheckError) as e:
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1823
|
+
# Handle exceptions during streaming
|
|
1824
|
+
run_response.status = RunStatus.error
|
|
1825
|
+
# Add error event to list of events
|
|
1826
|
+
run_error = create_run_error_event(
|
|
1827
|
+
run_response,
|
|
1828
|
+
error=str(e),
|
|
1829
|
+
error_id=e.error_id,
|
|
1830
|
+
error_type=e.type,
|
|
1831
|
+
additional_data=e.additional_data,
|
|
1832
|
+
)
|
|
1833
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
1834
|
+
|
|
1835
|
+
# If the content is None, set it to the error message
|
|
1836
|
+
if run_response.content is None:
|
|
1837
|
+
run_response.content = str(e)
|
|
1838
|
+
|
|
1839
|
+
log_error(f"Validation failed: {str(e)} | Check trigger: {e.check_trigger}")
|
|
1840
|
+
|
|
1841
|
+
self._cleanup_and_store(
|
|
1842
|
+
run_response=run_response, session=agent_session, run_context=run_context, user_id=user_id
|
|
1843
|
+
)
|
|
1844
|
+
|
|
1845
|
+
if stream:
|
|
1846
|
+
return generator_wrapper(run_error) # type: ignore
|
|
1847
|
+
else:
|
|
1848
|
+
return run_response
|
|
1849
|
+
except RunCancelledException as e:
|
|
1850
|
+
# Handle run cancellation during streaming
|
|
1851
|
+
log_info(f"Run {run_response.run_id} was cancelled during streaming")
|
|
1852
|
+
run_response.content = str(e)
|
|
1856
1853
|
run_response.status = RunStatus.cancelled
|
|
1854
|
+
cancelled_run_error = handle_event(
|
|
1855
|
+
create_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
1856
|
+
run_response,
|
|
1857
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
1858
|
+
store_events=self.store_events,
|
|
1859
|
+
)
|
|
1860
|
+
|
|
1861
|
+
# Cleanup and store the run response and session
|
|
1862
|
+
self._cleanup_and_store(
|
|
1863
|
+
run_response=run_response, session=agent_session, run_context=run_context, user_id=user_id
|
|
1864
|
+
)
|
|
1857
1865
|
|
|
1866
|
+
if stream:
|
|
1867
|
+
return generator_wrapper(cancelled_run_error) # type: ignore
|
|
1868
|
+
else:
|
|
1869
|
+
return run_response
|
|
1870
|
+
except KeyboardInterrupt:
|
|
1858
1871
|
if stream:
|
|
1859
1872
|
return generator_wrapper( # type: ignore
|
|
1860
|
-
create_run_cancelled_event(
|
|
1861
|
-
from_run_response=run_response,
|
|
1862
|
-
reason="Operation cancelled by user",
|
|
1863
|
-
)
|
|
1873
|
+
create_run_cancelled_event(run_response, "Operation cancelled by user") # type: ignore
|
|
1864
1874
|
)
|
|
1865
1875
|
else:
|
|
1866
|
-
|
|
1876
|
+
run_response.content = "Operation cancelled by user" # type: ignore
|
|
1877
|
+
run_response.status = RunStatus.cancelled # type: ignore
|
|
1878
|
+
return run_response # type: ignore
|
|
1879
|
+
|
|
1867
1880
|
except Exception as e:
|
|
1868
|
-
# Check if this is the last attempt
|
|
1869
1881
|
if attempt < num_attempts - 1:
|
|
1870
1882
|
# Calculate delay with exponential backoff if enabled
|
|
1871
1883
|
if self.exponential_backoff:
|
|
@@ -1876,12 +1888,27 @@ class Agent:
|
|
|
1876
1888
|
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
1877
1889
|
time.sleep(delay)
|
|
1878
1890
|
continue
|
|
1891
|
+
|
|
1892
|
+
run_response.status = RunStatus.error
|
|
1893
|
+
# Add error event to list of events
|
|
1894
|
+
run_error = create_run_error_event(run_response, error=str(e))
|
|
1895
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
1896
|
+
|
|
1897
|
+
# If the content is None, set it to the error message
|
|
1898
|
+
if run_response.content is None:
|
|
1899
|
+
run_response.content = str(e)
|
|
1900
|
+
|
|
1901
|
+
log_error(f"Error in Agent run: {str(e)}")
|
|
1902
|
+
|
|
1903
|
+
# Cleanup and store the run response and session
|
|
1904
|
+
self._cleanup_and_store(
|
|
1905
|
+
run_response=run_response, session=agent_session, run_context=run_context, user_id=user_id
|
|
1906
|
+
)
|
|
1907
|
+
|
|
1908
|
+
if stream:
|
|
1909
|
+
return generator_wrapper(run_error) # type: ignore
|
|
1879
1910
|
else:
|
|
1880
|
-
|
|
1881
|
-
log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
|
|
1882
|
-
if stream:
|
|
1883
|
-
return generator_wrapper(create_run_error_event(run_response, error=str(e))) # type: ignore
|
|
1884
|
-
raise e
|
|
1911
|
+
return run_response
|
|
1885
1912
|
|
|
1886
1913
|
# If we get here, all retries failed (shouldn't happen with current logic)
|
|
1887
1914
|
raise Exception(f"Failed after {num_attempts} attempts.")
|
|
@@ -1922,246 +1949,321 @@ class Agent:
|
|
|
1922
1949
|
"""
|
|
1923
1950
|
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
1924
1951
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1952
|
+
cultural_knowledge_task = None
|
|
1953
|
+
memory_task = None
|
|
1927
1954
|
|
|
1928
|
-
#
|
|
1929
|
-
self.
|
|
1930
|
-
# Initialize session state
|
|
1931
|
-
run_context.session_state = self._initialize_session_state(
|
|
1932
|
-
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
1933
|
-
user_id=user_id,
|
|
1934
|
-
session_id=session_id,
|
|
1935
|
-
run_id=run_response.run_id,
|
|
1936
|
-
)
|
|
1937
|
-
# Update session state from DB
|
|
1938
|
-
if run_context.session_state is not None:
|
|
1939
|
-
run_context.session_state = self._load_session_state(
|
|
1940
|
-
session=agent_session, session_state=run_context.session_state
|
|
1941
|
-
)
|
|
1955
|
+
# Set up retry logic
|
|
1956
|
+
num_attempts = self.retries + 1
|
|
1942
1957
|
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1958
|
+
for attempt in range(num_attempts):
|
|
1959
|
+
if num_attempts > 1:
|
|
1960
|
+
log_debug(f"Retrying Agent run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
1946
1961
|
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
pass
|
|
1962
|
+
try:
|
|
1963
|
+
# 1. Read or create session. Reads from the database if provided.
|
|
1964
|
+
agent_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
1965
|
+
|
|
1966
|
+
# 2. Update metadata and session state
|
|
1967
|
+
self._update_metadata(session=agent_session)
|
|
1968
|
+
# Initialize session state
|
|
1969
|
+
run_context.session_state = self._initialize_session_state(
|
|
1970
|
+
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
1971
|
+
user_id=user_id,
|
|
1972
|
+
session_id=session_id,
|
|
1973
|
+
run_id=run_response.run_id,
|
|
1974
|
+
)
|
|
1975
|
+
# Update session state from DB
|
|
1976
|
+
if run_context.session_state is not None:
|
|
1977
|
+
run_context.session_state = self._load_session_state(
|
|
1978
|
+
session=agent_session, session_state=run_context.session_state
|
|
1979
|
+
)
|
|
1966
1980
|
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
run_response=run_response,
|
|
1971
|
-
run_context=run_context,
|
|
1972
|
-
session=agent_session,
|
|
1973
|
-
user_id=user_id,
|
|
1974
|
-
)
|
|
1981
|
+
# 3. Resolve dependencies
|
|
1982
|
+
if run_context.dependencies is not None:
|
|
1983
|
+
await self._aresolve_run_dependencies(run_context=run_context)
|
|
1975
1984
|
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1985
|
+
# 4. Execute pre-hooks
|
|
1986
|
+
run_input = cast(RunInput, run_response.input)
|
|
1987
|
+
self.model = cast(Model, self.model)
|
|
1988
|
+
if self.pre_hooks is not None:
|
|
1989
|
+
# Can modify the run input
|
|
1990
|
+
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
1991
|
+
hooks=self.pre_hooks, # type: ignore
|
|
1992
|
+
run_response=run_response,
|
|
1993
|
+
run_context=run_context,
|
|
1994
|
+
run_input=run_input,
|
|
1995
|
+
session=agent_session,
|
|
1996
|
+
user_id=user_id,
|
|
1997
|
+
debug_mode=debug_mode,
|
|
1998
|
+
background_tasks=background_tasks,
|
|
1999
|
+
**kwargs,
|
|
2000
|
+
)
|
|
2001
|
+
# Consume the async iterator without yielding
|
|
2002
|
+
async for _ in pre_hook_iterator:
|
|
2003
|
+
pass
|
|
1983
2004
|
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
images=run_input.images,
|
|
1993
|
-
videos=run_input.videos,
|
|
1994
|
-
files=run_input.files,
|
|
1995
|
-
add_history_to_context=add_history_to_context,
|
|
1996
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
1997
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
1998
|
-
tools=_tools,
|
|
1999
|
-
**kwargs,
|
|
2000
|
-
)
|
|
2001
|
-
if len(run_messages.messages) == 0:
|
|
2002
|
-
log_error("No messages to be sent to the model.")
|
|
2005
|
+
# 5. Determine tools for model
|
|
2006
|
+
self.model = cast(Model, self.model)
|
|
2007
|
+
processed_tools = await self.aget_tools(
|
|
2008
|
+
run_response=run_response,
|
|
2009
|
+
run_context=run_context,
|
|
2010
|
+
session=agent_session,
|
|
2011
|
+
user_id=user_id,
|
|
2012
|
+
)
|
|
2003
2013
|
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
):
|
|
2012
|
-
log_debug("Starting memory creation in background task.")
|
|
2013
|
-
memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2014
|
+
_tools = self._determine_tools_for_model(
|
|
2015
|
+
model=self.model,
|
|
2016
|
+
processed_tools=processed_tools,
|
|
2017
|
+
run_response=run_response,
|
|
2018
|
+
run_context=run_context,
|
|
2019
|
+
session=agent_session,
|
|
2020
|
+
)
|
|
2014
2021
|
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2022
|
+
# 6. Prepare run messages
|
|
2023
|
+
run_messages: RunMessages = await self._aget_run_messages(
|
|
2024
|
+
run_response=run_response,
|
|
2025
|
+
run_context=run_context,
|
|
2026
|
+
input=run_input.input_content,
|
|
2027
|
+
session=agent_session,
|
|
2028
|
+
user_id=user_id,
|
|
2029
|
+
audio=run_input.audios,
|
|
2030
|
+
images=run_input.images,
|
|
2031
|
+
videos=run_input.videos,
|
|
2032
|
+
files=run_input.files,
|
|
2033
|
+
add_history_to_context=add_history_to_context,
|
|
2034
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2035
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2036
|
+
tools=_tools,
|
|
2037
|
+
**kwargs,
|
|
2038
|
+
)
|
|
2039
|
+
if len(run_messages.messages) == 0:
|
|
2040
|
+
log_error("No messages to be sent to the model.")
|
|
2024
2041
|
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2042
|
+
# 7. Start memory creation as a background task (runs concurrently with the main execution)
|
|
2043
|
+
memory_task = None
|
|
2044
|
+
if (
|
|
2045
|
+
run_messages.user_message is not None
|
|
2046
|
+
and self.memory_manager is not None
|
|
2047
|
+
and self.enable_user_memories
|
|
2048
|
+
and not self.enable_agentic_memory
|
|
2049
|
+
):
|
|
2050
|
+
log_debug("Starting memory creation in background task.")
|
|
2051
|
+
memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2028
2052
|
|
|
2029
|
-
|
|
2030
|
-
|
|
2053
|
+
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
2054
|
+
if (
|
|
2055
|
+
run_messages.user_message is not None
|
|
2056
|
+
and self.culture_manager is not None
|
|
2057
|
+
and self.update_cultural_knowledge
|
|
2058
|
+
):
|
|
2059
|
+
log_debug("Starting cultural knowledge creation in background thread.")
|
|
2060
|
+
cultural_knowledge_task = create_task(self._acreate_cultural_knowledge(run_messages=run_messages))
|
|
2031
2061
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2062
|
+
# Check for cancellation before model call
|
|
2063
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2034
2064
|
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
messages=run_messages.messages,
|
|
2038
|
-
tools=_tools,
|
|
2039
|
-
tool_choice=self.tool_choice,
|
|
2040
|
-
tool_call_limit=self.tool_call_limit,
|
|
2041
|
-
response_format=response_format,
|
|
2042
|
-
send_media_to_model=self.send_media_to_model,
|
|
2043
|
-
run_response=run_response,
|
|
2044
|
-
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
2045
|
-
)
|
|
2065
|
+
# 8. Reason about the task if reasoning is enabled
|
|
2066
|
+
await self._ahandle_reasoning(run_response=run_response, run_messages=run_messages)
|
|
2046
2067
|
|
|
2047
|
-
|
|
2048
|
-
|
|
2068
|
+
# Check for cancellation before model call
|
|
2069
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2049
2070
|
|
|
2050
|
-
|
|
2051
|
-
|
|
2071
|
+
# 9. Generate a response from the Model (includes running function calls)
|
|
2072
|
+
model_response: ModelResponse = await self.model.aresponse(
|
|
2073
|
+
messages=run_messages.messages,
|
|
2074
|
+
tools=_tools,
|
|
2075
|
+
tool_choice=self.tool_choice,
|
|
2076
|
+
tool_call_limit=self.tool_call_limit,
|
|
2077
|
+
response_format=response_format,
|
|
2078
|
+
send_media_to_model=self.send_media_to_model,
|
|
2079
|
+
run_response=run_response,
|
|
2080
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
2081
|
+
)
|
|
2052
2082
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
model_response=model_response, run_messages=run_messages, run_context=run_context
|
|
2056
|
-
)
|
|
2083
|
+
# Check for cancellation after model call
|
|
2084
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2057
2085
|
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
run_messages=run_messages,
|
|
2063
|
-
run_context=run_context,
|
|
2064
|
-
)
|
|
2086
|
+
# If an output model is provided, generate output using the output model
|
|
2087
|
+
await self._agenerate_response_with_output_model(
|
|
2088
|
+
model_response=model_response, run_messages=run_messages
|
|
2089
|
+
)
|
|
2065
2090
|
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
return await self._ahandle_agent_run_paused(
|
|
2070
|
-
run_response=run_response, session=agent_session, user_id=user_id
|
|
2091
|
+
# If a parser model is provided, structure the response separately
|
|
2092
|
+
await self._aparse_response_with_parser_model(
|
|
2093
|
+
model_response=model_response, run_messages=run_messages, run_context=run_context
|
|
2071
2094
|
)
|
|
2072
2095
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2096
|
+
# 10. Update the RunOutput with the model response
|
|
2097
|
+
self._update_run_response(
|
|
2098
|
+
model_response=model_response,
|
|
2099
|
+
run_response=run_response,
|
|
2100
|
+
run_messages=run_messages,
|
|
2101
|
+
run_context=run_context,
|
|
2102
|
+
)
|
|
2075
2103
|
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2104
|
+
# We should break out of the run function
|
|
2105
|
+
if any(tool_call.is_paused for tool_call in run_response.tools or []):
|
|
2106
|
+
await await_for_open_threads(
|
|
2107
|
+
memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task
|
|
2108
|
+
)
|
|
2109
|
+
return await self._ahandle_agent_run_paused(
|
|
2110
|
+
run_response=run_response, session=agent_session, user_id=user_id
|
|
2111
|
+
)
|
|
2079
2112
|
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
async for _ in self._aexecute_post_hooks(
|
|
2083
|
-
hooks=self.post_hooks, # type: ignore
|
|
2084
|
-
run_output=run_response,
|
|
2085
|
-
run_context=run_context,
|
|
2086
|
-
session=agent_session,
|
|
2087
|
-
user_id=user_id,
|
|
2088
|
-
debug_mode=debug_mode,
|
|
2089
|
-
background_tasks=background_tasks,
|
|
2090
|
-
**kwargs,
|
|
2091
|
-
):
|
|
2092
|
-
pass
|
|
2113
|
+
# 11. Convert the response to the structured format if needed
|
|
2114
|
+
self._convert_response_to_structured_format(run_response, run_context=run_context)
|
|
2093
2115
|
|
|
2094
|
-
|
|
2095
|
-
|
|
2116
|
+
# 12. Store media if enabled
|
|
2117
|
+
if self.store_media:
|
|
2118
|
+
store_media_util(run_response, model_response)
|
|
2096
2119
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2120
|
+
# 13. Execute post-hooks (after output is generated but before response is returned)
|
|
2121
|
+
if self.post_hooks is not None:
|
|
2122
|
+
async for _ in self._aexecute_post_hooks(
|
|
2123
|
+
hooks=self.post_hooks, # type: ignore
|
|
2124
|
+
run_output=run_response,
|
|
2125
|
+
run_context=run_context,
|
|
2126
|
+
session=agent_session,
|
|
2127
|
+
user_id=user_id,
|
|
2128
|
+
debug_mode=debug_mode,
|
|
2129
|
+
background_tasks=background_tasks,
|
|
2130
|
+
**kwargs,
|
|
2131
|
+
):
|
|
2132
|
+
pass
|
|
2099
2133
|
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
# Upsert the RunOutput to Agent Session before creating the session summary
|
|
2103
|
-
agent_session.upsert_run(run=run_response)
|
|
2104
|
-
try:
|
|
2105
|
-
await self.session_summary_manager.acreate_session_summary(session=agent_session)
|
|
2106
|
-
except Exception as e:
|
|
2107
|
-
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2134
|
+
# Check for cancellation
|
|
2135
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2108
2136
|
|
|
2109
|
-
|
|
2137
|
+
# 14. Wait for background memory creation
|
|
2138
|
+
await await_for_open_threads(memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task)
|
|
2110
2139
|
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2140
|
+
# 15. Create session summary
|
|
2141
|
+
if self.session_summary_manager is not None and self.enable_session_summaries:
|
|
2142
|
+
# Upsert the RunOutput to Agent Session before creating the session summary
|
|
2143
|
+
agent_session.upsert_run(run=run_response)
|
|
2144
|
+
try:
|
|
2145
|
+
await self.session_summary_manager.acreate_session_summary(session=agent_session)
|
|
2146
|
+
except Exception as e:
|
|
2147
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2118
2148
|
|
|
2119
|
-
|
|
2120
|
-
await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
|
|
2149
|
+
run_response.status = RunStatus.completed
|
|
2121
2150
|
|
|
2122
|
-
|
|
2151
|
+
# 16. Cleanup and store the run response and session
|
|
2152
|
+
await self._acleanup_and_store(
|
|
2153
|
+
run_response=run_response,
|
|
2154
|
+
session=agent_session,
|
|
2155
|
+
run_context=run_context,
|
|
2156
|
+
user_id=user_id,
|
|
2157
|
+
)
|
|
2123
2158
|
|
|
2124
|
-
|
|
2159
|
+
# Log Agent Telemetry
|
|
2160
|
+
await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
|
|
2125
2161
|
|
|
2126
|
-
|
|
2127
|
-
# Handle run cancellation
|
|
2128
|
-
log_info(f"Run {run_response.run_id} was cancelled")
|
|
2129
|
-
run_response.content = str(e)
|
|
2130
|
-
run_response.status = RunStatus.cancelled
|
|
2162
|
+
log_debug(f"Agent Run End: {run_response.run_id}", center=True, symbol="*")
|
|
2131
2163
|
|
|
2132
|
-
|
|
2133
|
-
await self._acleanup_and_store(
|
|
2134
|
-
run_response=run_response,
|
|
2135
|
-
session=agent_session,
|
|
2136
|
-
run_context=run_context,
|
|
2137
|
-
user_id=user_id,
|
|
2138
|
-
)
|
|
2164
|
+
return run_response
|
|
2139
2165
|
|
|
2140
|
-
|
|
2166
|
+
except RunCancelledException as e:
|
|
2167
|
+
# Handle run cancellation
|
|
2168
|
+
log_info(f"Run {run_response.run_id} was cancelled")
|
|
2169
|
+
run_response.content = str(e)
|
|
2170
|
+
run_response.status = RunStatus.cancelled
|
|
2141
2171
|
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2172
|
+
# Cleanup and store the run response and session
|
|
2173
|
+
await self._acleanup_and_store(
|
|
2174
|
+
run_response=run_response,
|
|
2175
|
+
session=agent_session,
|
|
2176
|
+
run_context=run_context,
|
|
2177
|
+
user_id=user_id,
|
|
2178
|
+
)
|
|
2147
2179
|
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
pass
|
|
2180
|
+
return run_response
|
|
2181
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2182
|
+
# Handle exceptions during streaming
|
|
2183
|
+
run_response.status = RunStatus.error
|
|
2184
|
+
# Add error event to list of events
|
|
2185
|
+
run_error = create_run_error_event(
|
|
2186
|
+
run_response,
|
|
2187
|
+
error=str(e),
|
|
2188
|
+
error_id=e.error_id,
|
|
2189
|
+
error_type=e.type,
|
|
2190
|
+
additional_data=e.additional_data,
|
|
2191
|
+
)
|
|
2192
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
2162
2193
|
|
|
2163
|
-
|
|
2164
|
-
|
|
2194
|
+
# If the content is None, set it to the error message
|
|
2195
|
+
if run_response.content is None:
|
|
2196
|
+
run_response.content = str(e)
|
|
2197
|
+
|
|
2198
|
+
log_error(f"Validation failed: {str(e)} | Check trigger: {e.check_trigger}")
|
|
2199
|
+
|
|
2200
|
+
await self._acleanup_and_store(
|
|
2201
|
+
run_response=run_response,
|
|
2202
|
+
session=agent_session,
|
|
2203
|
+
run_context=run_context,
|
|
2204
|
+
user_id=user_id,
|
|
2205
|
+
)
|
|
2206
|
+
|
|
2207
|
+
return run_response
|
|
2208
|
+
except Exception as e:
|
|
2209
|
+
# Check if this is the last attempt
|
|
2210
|
+
if attempt < num_attempts - 1:
|
|
2211
|
+
# Calculate delay with exponential backoff if enabled
|
|
2212
|
+
if self.exponential_backoff:
|
|
2213
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2214
|
+
else:
|
|
2215
|
+
delay = self.delay_between_retries
|
|
2216
|
+
|
|
2217
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2218
|
+
time.sleep(delay)
|
|
2219
|
+
continue
|
|
2220
|
+
|
|
2221
|
+
run_response.status = RunStatus.error
|
|
2222
|
+
# Add error event to list of events
|
|
2223
|
+
run_error = create_run_error_event(run_response, error=str(e))
|
|
2224
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
2225
|
+
|
|
2226
|
+
# If the content is None, set it to the error message
|
|
2227
|
+
if run_response.content is None:
|
|
2228
|
+
run_response.content = str(e)
|
|
2229
|
+
|
|
2230
|
+
log_error(f"Error in Agent run: {str(e)}")
|
|
2231
|
+
|
|
2232
|
+
# Cleanup and store the run response and session
|
|
2233
|
+
await self._acleanup_and_store(
|
|
2234
|
+
run_response=run_response,
|
|
2235
|
+
session=agent_session,
|
|
2236
|
+
run_context=run_context,
|
|
2237
|
+
user_id=user_id,
|
|
2238
|
+
)
|
|
2239
|
+
|
|
2240
|
+
return run_response
|
|
2241
|
+
|
|
2242
|
+
finally:
|
|
2243
|
+
# Always disconnect connectable tools
|
|
2244
|
+
self._disconnect_connectable_tools()
|
|
2245
|
+
# Always disconnect MCP tools
|
|
2246
|
+
await self._disconnect_mcp_tools()
|
|
2247
|
+
|
|
2248
|
+
# Cancel the memory task if it's still running
|
|
2249
|
+
if memory_task is not None and not memory_task.done():
|
|
2250
|
+
memory_task.cancel()
|
|
2251
|
+
try:
|
|
2252
|
+
await memory_task
|
|
2253
|
+
except CancelledError:
|
|
2254
|
+
pass
|
|
2255
|
+
# Cancel the cultural knowledge task if it's still running
|
|
2256
|
+
if cultural_knowledge_task is not None and not cultural_knowledge_task.done():
|
|
2257
|
+
cultural_knowledge_task.cancel()
|
|
2258
|
+
try:
|
|
2259
|
+
await cultural_knowledge_task
|
|
2260
|
+
except CancelledError:
|
|
2261
|
+
pass
|
|
2262
|
+
|
|
2263
|
+
# Always clean up the run tracking
|
|
2264
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
2265
|
+
|
|
2266
|
+
return run_response
|
|
2165
2267
|
|
|
2166
2268
|
async def _arun_stream(
|
|
2167
2269
|
self,
|
|
@@ -2198,344 +2300,422 @@ class Agent:
|
|
|
2198
2300
|
"""
|
|
2199
2301
|
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
2200
2302
|
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
yield handle_event( # type: ignore
|
|
2204
|
-
create_run_started_event(run_response),
|
|
2205
|
-
run_response,
|
|
2206
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
2207
|
-
store_events=self.store_events,
|
|
2208
|
-
)
|
|
2303
|
+
memory_task = None
|
|
2304
|
+
cultural_knowledge_task = None
|
|
2209
2305
|
|
|
2210
2306
|
# 1. Read or create session. Reads from the database if provided.
|
|
2211
|
-
agent_session =
|
|
2307
|
+
agent_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2212
2308
|
|
|
2213
|
-
#
|
|
2214
|
-
self.
|
|
2215
|
-
# Initialize session state
|
|
2216
|
-
run_context.session_state = self._initialize_session_state(
|
|
2217
|
-
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
2218
|
-
user_id=user_id,
|
|
2219
|
-
session_id=session_id,
|
|
2220
|
-
run_id=run_response.run_id,
|
|
2221
|
-
)
|
|
2222
|
-
# Update session state from DB
|
|
2223
|
-
if run_context.session_state is not None:
|
|
2224
|
-
run_context.session_state = self._load_session_state(
|
|
2225
|
-
session=agent_session, session_state=run_context.session_state
|
|
2226
|
-
)
|
|
2309
|
+
# Set up retry logic
|
|
2310
|
+
num_attempts = self.retries + 1
|
|
2227
2311
|
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2312
|
+
for attempt in range(num_attempts):
|
|
2313
|
+
if num_attempts > 1:
|
|
2314
|
+
log_debug(f"Retrying Agent run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2231
2315
|
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
run_input=run_input,
|
|
2242
|
-
session=agent_session,
|
|
2243
|
-
user_id=user_id,
|
|
2244
|
-
debug_mode=debug_mode,
|
|
2245
|
-
stream_events=stream_events,
|
|
2246
|
-
background_tasks=background_tasks,
|
|
2247
|
-
**kwargs,
|
|
2248
|
-
)
|
|
2249
|
-
async for event in pre_hook_iterator:
|
|
2250
|
-
yield event
|
|
2316
|
+
try:
|
|
2317
|
+
# Start the Run by yielding a RunStarted event
|
|
2318
|
+
if stream_events:
|
|
2319
|
+
yield handle_event( # type: ignore
|
|
2320
|
+
create_run_started_event(run_response),
|
|
2321
|
+
run_response,
|
|
2322
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2323
|
+
store_events=self.store_events,
|
|
2324
|
+
)
|
|
2251
2325
|
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2326
|
+
# 2. Update metadata and session state
|
|
2327
|
+
self._update_metadata(session=agent_session)
|
|
2328
|
+
# Initialize session state
|
|
2329
|
+
run_context.session_state = self._initialize_session_state(
|
|
2330
|
+
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
2331
|
+
user_id=user_id,
|
|
2332
|
+
session_id=session_id,
|
|
2333
|
+
run_id=run_response.run_id,
|
|
2334
|
+
)
|
|
2335
|
+
# Update session state from DB
|
|
2336
|
+
if run_context.session_state is not None:
|
|
2337
|
+
run_context.session_state = self._load_session_state(
|
|
2338
|
+
session=agent_session, session_state=run_context.session_state
|
|
2339
|
+
)
|
|
2260
2340
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
run_response=run_response,
|
|
2265
|
-
run_context=run_context,
|
|
2266
|
-
session=agent_session,
|
|
2267
|
-
)
|
|
2341
|
+
# 3. Resolve dependencies
|
|
2342
|
+
if run_context.dependencies is not None:
|
|
2343
|
+
await self._aresolve_run_dependencies(run_context=run_context)
|
|
2268
2344
|
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
log_error("No messages to be sent to the model.")
|
|
2345
|
+
# 4. Execute pre-hooks
|
|
2346
|
+
run_input = cast(RunInput, run_response.input)
|
|
2347
|
+
self.model = cast(Model, self.model)
|
|
2348
|
+
if self.pre_hooks is not None:
|
|
2349
|
+
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
2350
|
+
hooks=self.pre_hooks, # type: ignore
|
|
2351
|
+
run_response=run_response,
|
|
2352
|
+
run_context=run_context,
|
|
2353
|
+
run_input=run_input,
|
|
2354
|
+
session=agent_session,
|
|
2355
|
+
user_id=user_id,
|
|
2356
|
+
debug_mode=debug_mode,
|
|
2357
|
+
stream_events=stream_events,
|
|
2358
|
+
background_tasks=background_tasks,
|
|
2359
|
+
**kwargs,
|
|
2360
|
+
)
|
|
2361
|
+
async for event in pre_hook_iterator:
|
|
2362
|
+
yield event
|
|
2288
2363
|
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
log_debug("Starting memory creation in background task.")
|
|
2298
|
-
memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2364
|
+
# 5. Determine tools for model
|
|
2365
|
+
self.model = cast(Model, self.model)
|
|
2366
|
+
processed_tools = await self.aget_tools(
|
|
2367
|
+
run_response=run_response,
|
|
2368
|
+
run_context=run_context,
|
|
2369
|
+
session=agent_session,
|
|
2370
|
+
user_id=user_id,
|
|
2371
|
+
)
|
|
2299
2372
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2373
|
+
_tools = self._determine_tools_for_model(
|
|
2374
|
+
model=self.model,
|
|
2375
|
+
processed_tools=processed_tools,
|
|
2376
|
+
run_response=run_response,
|
|
2377
|
+
run_context=run_context,
|
|
2378
|
+
session=agent_session,
|
|
2379
|
+
)
|
|
2380
|
+
|
|
2381
|
+
# 6. Prepare run messages
|
|
2382
|
+
run_messages: RunMessages = await self._aget_run_messages(
|
|
2383
|
+
run_response=run_response,
|
|
2384
|
+
run_context=run_context,
|
|
2385
|
+
input=run_input.input_content,
|
|
2386
|
+
session=agent_session,
|
|
2387
|
+
user_id=user_id,
|
|
2388
|
+
audio=run_input.audios,
|
|
2389
|
+
images=run_input.images,
|
|
2390
|
+
videos=run_input.videos,
|
|
2391
|
+
files=run_input.files,
|
|
2392
|
+
add_history_to_context=add_history_to_context,
|
|
2393
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2394
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2395
|
+
tools=_tools,
|
|
2396
|
+
**kwargs,
|
|
2397
|
+
)
|
|
2398
|
+
if len(run_messages.messages) == 0:
|
|
2399
|
+
log_error("No messages to be sent to the model.")
|
|
2400
|
+
|
|
2401
|
+
# 7. Start memory creation as a background task (runs concurrently with the main execution)
|
|
2402
|
+
memory_task = None
|
|
2403
|
+
if (
|
|
2404
|
+
run_messages.user_message is not None
|
|
2405
|
+
and self.memory_manager is not None
|
|
2406
|
+
and self.enable_user_memories
|
|
2407
|
+
and not self.enable_agentic_memory
|
|
2408
|
+
):
|
|
2409
|
+
log_debug("Starting memory creation in background task.")
|
|
2410
|
+
memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2411
|
+
|
|
2412
|
+
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
2413
|
+
if (
|
|
2414
|
+
run_messages.user_message is not None
|
|
2415
|
+
and self.culture_manager is not None
|
|
2416
|
+
and self.update_cultural_knowledge
|
|
2417
|
+
):
|
|
2418
|
+
log_debug("Starting cultural knowledge creation in background task.")
|
|
2419
|
+
cultural_knowledge_task = create_task(self._acreate_cultural_knowledge(run_messages=run_messages))
|
|
2420
|
+
|
|
2421
|
+
# 8. Reason about the task if reasoning is enabled
|
|
2422
|
+
async for item in self._ahandle_reasoning_stream(
|
|
2423
|
+
run_response=run_response,
|
|
2424
|
+
run_messages=run_messages,
|
|
2425
|
+
stream_events=stream_events,
|
|
2426
|
+
):
|
|
2427
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2428
|
+
yield item
|
|
2309
2429
|
|
|
2310
|
-
try:
|
|
2311
|
-
# 8. Reason about the task if reasoning is enabled
|
|
2312
|
-
async for item in self._ahandle_reasoning_stream(
|
|
2313
|
-
run_response=run_response,
|
|
2314
|
-
run_messages=run_messages,
|
|
2315
|
-
stream_events=stream_events,
|
|
2316
|
-
):
|
|
2317
2430
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2318
|
-
yield item
|
|
2319
2431
|
|
|
2320
|
-
|
|
2432
|
+
# 9. Generate a response from the Model
|
|
2433
|
+
if self.output_model is None:
|
|
2434
|
+
async for event in self._ahandle_model_response_stream(
|
|
2435
|
+
session=agent_session,
|
|
2436
|
+
run_response=run_response,
|
|
2437
|
+
run_messages=run_messages,
|
|
2438
|
+
tools=_tools,
|
|
2439
|
+
response_format=response_format,
|
|
2440
|
+
stream_events=stream_events,
|
|
2441
|
+
session_state=run_context.session_state,
|
|
2442
|
+
run_context=run_context,
|
|
2443
|
+
):
|
|
2444
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2445
|
+
yield event
|
|
2446
|
+
else:
|
|
2447
|
+
from agno.run.agent import (
|
|
2448
|
+
IntermediateRunContentEvent,
|
|
2449
|
+
RunContentEvent,
|
|
2450
|
+
) # type: ignore
|
|
2321
2451
|
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2452
|
+
async for event in self._ahandle_model_response_stream(
|
|
2453
|
+
session=agent_session,
|
|
2454
|
+
run_response=run_response,
|
|
2455
|
+
run_messages=run_messages,
|
|
2456
|
+
tools=_tools,
|
|
2457
|
+
response_format=response_format,
|
|
2458
|
+
stream_events=stream_events,
|
|
2459
|
+
session_state=run_context.session_state,
|
|
2460
|
+
run_context=run_context,
|
|
2461
|
+
):
|
|
2462
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2463
|
+
if isinstance(event, RunContentEvent):
|
|
2464
|
+
if stream_events:
|
|
2465
|
+
yield IntermediateRunContentEvent(
|
|
2466
|
+
content=event.content,
|
|
2467
|
+
content_type=event.content_type,
|
|
2468
|
+
)
|
|
2469
|
+
else:
|
|
2470
|
+
yield event
|
|
2471
|
+
|
|
2472
|
+
# If an output model is provided, generate output using the output model
|
|
2473
|
+
async for event in self._agenerate_response_with_output_model_stream(
|
|
2474
|
+
session=agent_session,
|
|
2475
|
+
run_response=run_response,
|
|
2476
|
+
run_messages=run_messages,
|
|
2477
|
+
stream_events=stream_events,
|
|
2478
|
+
):
|
|
2479
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2480
|
+
yield event
|
|
2481
|
+
|
|
2482
|
+
# Check for cancellation after model processing
|
|
2483
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2484
|
+
|
|
2485
|
+
# 10. Parse response with parser model if provided
|
|
2486
|
+
async for event in self._aparse_response_with_parser_model_stream(
|
|
2325
2487
|
session=agent_session,
|
|
2326
2488
|
run_response=run_response,
|
|
2327
|
-
run_messages=run_messages,
|
|
2328
|
-
tools=_tools,
|
|
2329
|
-
response_format=response_format,
|
|
2330
2489
|
stream_events=stream_events,
|
|
2331
|
-
session_state=run_context.session_state,
|
|
2332
2490
|
run_context=run_context,
|
|
2333
2491
|
):
|
|
2334
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2335
2492
|
yield event
|
|
2336
|
-
else:
|
|
2337
|
-
from agno.run.agent import (
|
|
2338
|
-
IntermediateRunContentEvent,
|
|
2339
|
-
RunContentEvent,
|
|
2340
|
-
) # type: ignore
|
|
2341
2493
|
|
|
2342
|
-
|
|
2343
|
-
|
|
2494
|
+
if stream_events:
|
|
2495
|
+
yield handle_event( # type: ignore
|
|
2496
|
+
create_run_content_completed_event(from_run_response=run_response),
|
|
2497
|
+
run_response,
|
|
2498
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2499
|
+
store_events=self.store_events,
|
|
2500
|
+
)
|
|
2501
|
+
|
|
2502
|
+
# Break out of the run function if a tool call is paused
|
|
2503
|
+
if any(tool_call.is_paused for tool_call in run_response.tools or []):
|
|
2504
|
+
async for item in await_for_thread_tasks_stream(
|
|
2505
|
+
memory_task=memory_task,
|
|
2506
|
+
cultural_knowledge_task=cultural_knowledge_task,
|
|
2507
|
+
stream_events=stream_events,
|
|
2508
|
+
run_response=run_response,
|
|
2509
|
+
):
|
|
2510
|
+
yield item
|
|
2511
|
+
|
|
2512
|
+
async for item in self._ahandle_agent_run_paused_stream(
|
|
2513
|
+
run_response=run_response, session=agent_session, user_id=user_id
|
|
2514
|
+
):
|
|
2515
|
+
yield item
|
|
2516
|
+
return
|
|
2517
|
+
|
|
2518
|
+
# Execute post-hooks (after output is generated but before response is returned)
|
|
2519
|
+
if self.post_hooks is not None:
|
|
2520
|
+
async for event in self._aexecute_post_hooks(
|
|
2521
|
+
hooks=self.post_hooks, # type: ignore
|
|
2522
|
+
run_output=run_response,
|
|
2523
|
+
run_context=run_context,
|
|
2524
|
+
session=agent_session,
|
|
2525
|
+
user_id=user_id,
|
|
2526
|
+
debug_mode=debug_mode,
|
|
2527
|
+
stream_events=stream_events,
|
|
2528
|
+
background_tasks=background_tasks,
|
|
2529
|
+
**kwargs,
|
|
2530
|
+
):
|
|
2531
|
+
yield event
|
|
2532
|
+
|
|
2533
|
+
# 11. Wait for background memory creation
|
|
2534
|
+
async for item in await_for_thread_tasks_stream(
|
|
2535
|
+
memory_task=memory_task,
|
|
2536
|
+
cultural_knowledge_task=cultural_knowledge_task,
|
|
2537
|
+
stream_events=stream_events,
|
|
2538
|
+
run_response=run_response,
|
|
2539
|
+
events_to_skip=self.events_to_skip,
|
|
2540
|
+
store_events=self.store_events,
|
|
2541
|
+
):
|
|
2542
|
+
yield item
|
|
2543
|
+
|
|
2544
|
+
# 12. Create session summary
|
|
2545
|
+
if self.session_summary_manager is not None and self.enable_session_summaries:
|
|
2546
|
+
# Upsert the RunOutput to Agent Session before creating the session summary
|
|
2547
|
+
agent_session.upsert_run(run=run_response)
|
|
2548
|
+
|
|
2549
|
+
if stream_events:
|
|
2550
|
+
yield handle_event( # type: ignore
|
|
2551
|
+
create_session_summary_started_event(from_run_response=run_response),
|
|
2552
|
+
run_response,
|
|
2553
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2554
|
+
store_events=self.store_events,
|
|
2555
|
+
)
|
|
2556
|
+
try:
|
|
2557
|
+
await self.session_summary_manager.acreate_session_summary(session=agent_session)
|
|
2558
|
+
except Exception as e:
|
|
2559
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2560
|
+
if stream_events:
|
|
2561
|
+
yield handle_event( # type: ignore
|
|
2562
|
+
create_session_summary_completed_event(
|
|
2563
|
+
from_run_response=run_response, session_summary=agent_session.summary
|
|
2564
|
+
),
|
|
2565
|
+
run_response,
|
|
2566
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2567
|
+
store_events=self.store_events,
|
|
2568
|
+
)
|
|
2569
|
+
|
|
2570
|
+
# Update run_response.session_state before creating RunCompletedEvent
|
|
2571
|
+
# This ensures the event has the final state after all tool modifications
|
|
2572
|
+
if agent_session.session_data is not None and "session_state" in agent_session.session_data:
|
|
2573
|
+
run_response.session_state = agent_session.session_data["session_state"]
|
|
2574
|
+
|
|
2575
|
+
# Create the run completed event
|
|
2576
|
+
completed_event = handle_event(
|
|
2577
|
+
create_run_completed_event(from_run_response=run_response),
|
|
2578
|
+
run_response,
|
|
2579
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2580
|
+
store_events=self.store_events,
|
|
2581
|
+
)
|
|
2582
|
+
|
|
2583
|
+
# Set the run status to completed
|
|
2584
|
+
run_response.status = RunStatus.completed
|
|
2585
|
+
|
|
2586
|
+
# 13. Cleanup and store the run response and session
|
|
2587
|
+
await self._acleanup_and_store(
|
|
2344
2588
|
run_response=run_response,
|
|
2345
|
-
|
|
2346
|
-
tools=_tools,
|
|
2347
|
-
response_format=response_format,
|
|
2348
|
-
stream_events=stream_events,
|
|
2349
|
-
session_state=run_context.session_state,
|
|
2589
|
+
session=agent_session,
|
|
2350
2590
|
run_context=run_context,
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
if isinstance(event, RunContentEvent):
|
|
2354
|
-
if stream_events:
|
|
2355
|
-
yield IntermediateRunContentEvent(
|
|
2356
|
-
content=event.content,
|
|
2357
|
-
content_type=event.content_type,
|
|
2358
|
-
)
|
|
2359
|
-
else:
|
|
2360
|
-
yield event
|
|
2591
|
+
user_id=user_id,
|
|
2592
|
+
)
|
|
2361
2593
|
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
session=agent_session,
|
|
2365
|
-
run_response=run_response,
|
|
2366
|
-
run_messages=run_messages,
|
|
2367
|
-
stream_events=stream_events,
|
|
2368
|
-
):
|
|
2369
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2370
|
-
yield event
|
|
2594
|
+
if stream_events:
|
|
2595
|
+
yield completed_event # type: ignore
|
|
2371
2596
|
|
|
2372
|
-
|
|
2373
|
-
|
|
2597
|
+
if yield_run_output:
|
|
2598
|
+
yield run_response
|
|
2374
2599
|
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
session=agent_session, run_response=run_response, stream_events=stream_events, run_context=run_context
|
|
2378
|
-
):
|
|
2379
|
-
yield event
|
|
2600
|
+
# Log Agent Telemetry
|
|
2601
|
+
await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
|
|
2380
2602
|
|
|
2381
|
-
|
|
2603
|
+
log_debug(f"Agent Run End: {run_response.run_id}", center=True, symbol="*")
|
|
2604
|
+
|
|
2605
|
+
except RunCancelledException as e:
|
|
2606
|
+
# Handle run cancellation during async streaming
|
|
2607
|
+
log_info(f"Run {run_response.run_id} was cancelled during async streaming")
|
|
2608
|
+
run_response.status = RunStatus.cancelled
|
|
2609
|
+
# Don't overwrite content - preserve any partial content that was streamed
|
|
2610
|
+
# Only set content if it's empty
|
|
2611
|
+
if not run_response.content:
|
|
2612
|
+
run_response.content = str(e)
|
|
2613
|
+
|
|
2614
|
+
# Yield the cancellation event
|
|
2382
2615
|
yield handle_event( # type: ignore
|
|
2383
|
-
|
|
2616
|
+
create_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
2384
2617
|
run_response,
|
|
2385
2618
|
events_to_skip=self.events_to_skip, # type: ignore
|
|
2386
2619
|
store_events=self.store_events,
|
|
2387
2620
|
)
|
|
2388
2621
|
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
async for item in await_for_thread_tasks_stream(
|
|
2392
|
-
memory_task=memory_task,
|
|
2393
|
-
cultural_knowledge_task=cultural_knowledge_task,
|
|
2394
|
-
stream_events=stream_events,
|
|
2622
|
+
# Cleanup and store the run response and session
|
|
2623
|
+
await self._acleanup_and_store(
|
|
2395
2624
|
run_response=run_response,
|
|
2396
|
-
):
|
|
2397
|
-
yield item
|
|
2398
|
-
|
|
2399
|
-
async for item in self._ahandle_agent_run_paused_stream(
|
|
2400
|
-
run_response=run_response, session=agent_session, user_id=user_id
|
|
2401
|
-
):
|
|
2402
|
-
yield item
|
|
2403
|
-
return
|
|
2404
|
-
|
|
2405
|
-
# Execute post-hooks (after output is generated but before response is returned)
|
|
2406
|
-
if self.post_hooks is not None:
|
|
2407
|
-
async for event in self._aexecute_post_hooks(
|
|
2408
|
-
hooks=self.post_hooks, # type: ignore
|
|
2409
|
-
run_output=run_response,
|
|
2410
|
-
run_context=run_context,
|
|
2411
2625
|
session=agent_session,
|
|
2626
|
+
run_context=run_context,
|
|
2412
2627
|
user_id=user_id,
|
|
2413
|
-
|
|
2414
|
-
stream_events=stream_events,
|
|
2415
|
-
background_tasks=background_tasks,
|
|
2416
|
-
**kwargs,
|
|
2417
|
-
):
|
|
2418
|
-
yield event
|
|
2419
|
-
|
|
2420
|
-
# 11. Wait for background memory creation
|
|
2421
|
-
async for item in await_for_thread_tasks_stream(
|
|
2422
|
-
memory_task=memory_task,
|
|
2423
|
-
cultural_knowledge_task=cultural_knowledge_task,
|
|
2424
|
-
stream_events=stream_events,
|
|
2425
|
-
run_response=run_response,
|
|
2426
|
-
events_to_skip=self.events_to_skip,
|
|
2427
|
-
store_events=self.store_events,
|
|
2428
|
-
):
|
|
2429
|
-
yield item
|
|
2430
|
-
|
|
2431
|
-
# 12. Create session summary
|
|
2432
|
-
if self.session_summary_manager is not None and self.enable_session_summaries:
|
|
2433
|
-
# Upsert the RunOutput to Agent Session before creating the session summary
|
|
2434
|
-
agent_session.upsert_run(run=run_response)
|
|
2435
|
-
|
|
2436
|
-
if stream_events:
|
|
2437
|
-
yield handle_event( # type: ignore
|
|
2438
|
-
create_session_summary_started_event(from_run_response=run_response),
|
|
2439
|
-
run_response,
|
|
2440
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
2441
|
-
store_events=self.store_events,
|
|
2442
|
-
)
|
|
2443
|
-
try:
|
|
2444
|
-
await self.session_summary_manager.acreate_session_summary(session=agent_session)
|
|
2445
|
-
except Exception as e:
|
|
2446
|
-
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2447
|
-
if stream_events:
|
|
2448
|
-
yield handle_event( # type: ignore
|
|
2449
|
-
create_session_summary_completed_event(
|
|
2450
|
-
from_run_response=run_response, session_summary=agent_session.summary
|
|
2451
|
-
),
|
|
2452
|
-
run_response,
|
|
2453
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
2454
|
-
store_events=self.store_events,
|
|
2455
|
-
)
|
|
2628
|
+
)
|
|
2456
2629
|
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2630
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2631
|
+
# Handle exceptions during async streaming
|
|
2632
|
+
run_response.status = RunStatus.error
|
|
2633
|
+
# Add error event to list of events
|
|
2634
|
+
run_error = create_run_error_event(
|
|
2635
|
+
run_response,
|
|
2636
|
+
error=str(e),
|
|
2637
|
+
error_id=e.error_id,
|
|
2638
|
+
error_type=e.type,
|
|
2639
|
+
additional_data=e.additional_data,
|
|
2640
|
+
)
|
|
2641
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
2461
2642
|
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
run_response,
|
|
2466
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
2467
|
-
store_events=self.store_events,
|
|
2468
|
-
)
|
|
2643
|
+
# If the content is None, set it to the error message
|
|
2644
|
+
if run_response.content is None:
|
|
2645
|
+
run_response.content = str(e)
|
|
2469
2646
|
|
|
2470
|
-
|
|
2471
|
-
run_response.status = RunStatus.completed
|
|
2647
|
+
log_error(f"Validation failed: {str(e)} | Check trigger: {e.check_trigger}")
|
|
2472
2648
|
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2649
|
+
# Cleanup and store the run response and session
|
|
2650
|
+
await self._acleanup_and_store(
|
|
2651
|
+
run_response=run_response,
|
|
2652
|
+
session=agent_session,
|
|
2653
|
+
run_context=run_context,
|
|
2654
|
+
user_id=user_id,
|
|
2655
|
+
)
|
|
2480
2656
|
|
|
2481
|
-
|
|
2482
|
-
yield
|
|
2657
|
+
# Yield the error event
|
|
2658
|
+
yield run_error
|
|
2659
|
+
break
|
|
2483
2660
|
|
|
2484
|
-
|
|
2485
|
-
|
|
2661
|
+
except Exception as e:
|
|
2662
|
+
# Check if this is the last attempt
|
|
2663
|
+
if attempt < num_attempts - 1:
|
|
2664
|
+
# Calculate delay with exponential backoff if enabled
|
|
2665
|
+
if self.exponential_backoff:
|
|
2666
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2667
|
+
else:
|
|
2668
|
+
delay = self.delay_between_retries
|
|
2486
2669
|
|
|
2487
|
-
|
|
2488
|
-
|
|
2670
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2671
|
+
time.sleep(delay)
|
|
2672
|
+
continue
|
|
2489
2673
|
|
|
2490
|
-
|
|
2674
|
+
# Handle exceptions during async streaming
|
|
2675
|
+
run_response.status = RunStatus.error
|
|
2676
|
+
# Add error event to list of events
|
|
2677
|
+
run_error = create_run_error_event(run_response, error=str(e))
|
|
2678
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
2491
2679
|
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
run_response.status = RunStatus.cancelled
|
|
2496
|
-
# Don't overwrite content - preserve any partial content that was streamed
|
|
2497
|
-
# Only set content if it's empty
|
|
2498
|
-
if not run_response.content:
|
|
2499
|
-
run_response.content = str(e)
|
|
2680
|
+
# If the content is None, set it to the error message
|
|
2681
|
+
if run_response.content is None:
|
|
2682
|
+
run_response.content = str(e)
|
|
2500
2683
|
|
|
2501
|
-
|
|
2502
|
-
yield handle_event( # type: ignore
|
|
2503
|
-
create_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
2504
|
-
run_response,
|
|
2505
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
2506
|
-
store_events=self.store_events,
|
|
2507
|
-
)
|
|
2684
|
+
log_error(f"Error in Agent run: {str(e)}")
|
|
2508
2685
|
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
finally:
|
|
2517
|
-
# Always disconnect connectable tools
|
|
2518
|
-
self._disconnect_connectable_tools()
|
|
2519
|
-
# Always disconnect MCP tools
|
|
2520
|
-
await self._disconnect_mcp_tools()
|
|
2686
|
+
# Cleanup and store the run response and session
|
|
2687
|
+
await self._acleanup_and_store(
|
|
2688
|
+
run_response=run_response,
|
|
2689
|
+
session=agent_session,
|
|
2690
|
+
run_context=run_context,
|
|
2691
|
+
user_id=user_id,
|
|
2692
|
+
)
|
|
2521
2693
|
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2694
|
+
# Yield the error event
|
|
2695
|
+
yield run_error
|
|
2696
|
+
finally:
|
|
2697
|
+
# Always disconnect connectable tools
|
|
2698
|
+
self._disconnect_connectable_tools()
|
|
2699
|
+
# Always disconnect MCP tools
|
|
2700
|
+
await self._disconnect_mcp_tools()
|
|
2701
|
+
|
|
2702
|
+
# Cancel the memory task if it's still running
|
|
2703
|
+
if memory_task is not None and not memory_task.done():
|
|
2704
|
+
memory_task.cancel()
|
|
2705
|
+
try:
|
|
2706
|
+
await memory_task
|
|
2707
|
+
except CancelledError:
|
|
2708
|
+
pass
|
|
2529
2709
|
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2710
|
+
if cultural_knowledge_task is not None and not cultural_knowledge_task.done():
|
|
2711
|
+
cultural_knowledge_task.cancel()
|
|
2712
|
+
try:
|
|
2713
|
+
await cultural_knowledge_task
|
|
2714
|
+
except CancelledError:
|
|
2715
|
+
pass
|
|
2536
2716
|
|
|
2537
|
-
|
|
2538
|
-
|
|
2717
|
+
# Always clean up the run tracking
|
|
2718
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
2539
2719
|
|
|
2540
2720
|
@overload
|
|
2541
2721
|
async def arun(
|
|
@@ -2560,7 +2740,7 @@ class Agent:
|
|
|
2560
2740
|
add_session_state_to_context: Optional[bool] = None,
|
|
2561
2741
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2562
2742
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2563
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
2743
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2564
2744
|
debug_mode: Optional[bool] = None,
|
|
2565
2745
|
**kwargs: Any,
|
|
2566
2746
|
) -> RunOutput: ...
|
|
@@ -2587,7 +2767,7 @@ class Agent:
|
|
|
2587
2767
|
add_session_state_to_context: Optional[bool] = None,
|
|
2588
2768
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2589
2769
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2590
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
2770
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2591
2771
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2592
2772
|
yield_run_output: Optional[bool] = None,
|
|
2593
2773
|
debug_mode: Optional[bool] = None,
|
|
@@ -2616,7 +2796,7 @@ class Agent:
|
|
|
2616
2796
|
add_session_state_to_context: Optional[bool] = None,
|
|
2617
2797
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2618
2798
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2619
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
2799
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2620
2800
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2621
2801
|
yield_run_output: Optional[bool] = None,
|
|
2622
2802
|
debug_mode: Optional[bool] = None,
|
|
@@ -2764,82 +2944,37 @@ class Agent:
|
|
|
2764
2944
|
|
|
2765
2945
|
yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
|
|
2766
2946
|
|
|
2767
|
-
#
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
session_id=session_id,
|
|
2799
|
-
add_history_to_context=add_history,
|
|
2800
|
-
add_dependencies_to_context=add_dependencies,
|
|
2801
|
-
add_session_state_to_context=add_session_state,
|
|
2802
|
-
debug_mode=debug_mode,
|
|
2803
|
-
background_tasks=background_tasks,
|
|
2804
|
-
**kwargs,
|
|
2805
|
-
)
|
|
2806
|
-
except (InputCheckError, OutputCheckError) as e:
|
|
2807
|
-
log_error(f"Validation failed: {str(e)} | Check trigger: {e.check_trigger}")
|
|
2808
|
-
raise e
|
|
2809
|
-
except KeyboardInterrupt:
|
|
2810
|
-
run_response.content = "Operation cancelled by user"
|
|
2811
|
-
run_response.status = RunStatus.cancelled
|
|
2812
|
-
|
|
2813
|
-
if stream:
|
|
2814
|
-
return async_generator_wrapper( # type: ignore
|
|
2815
|
-
create_run_cancelled_event(
|
|
2816
|
-
from_run_response=run_response,
|
|
2817
|
-
reason="Operation cancelled by user",
|
|
2818
|
-
)
|
|
2819
|
-
)
|
|
2820
|
-
else:
|
|
2821
|
-
return run_response
|
|
2822
|
-
except Exception as e:
|
|
2823
|
-
# Check if this is the last attempt
|
|
2824
|
-
if attempt < num_attempts - 1:
|
|
2825
|
-
# Calculate delay with exponential backoff if enabled
|
|
2826
|
-
if self.exponential_backoff:
|
|
2827
|
-
delay = self.delay_between_retries * (2**attempt)
|
|
2828
|
-
else:
|
|
2829
|
-
delay = self.delay_between_retries
|
|
2830
|
-
|
|
2831
|
-
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2832
|
-
time.sleep(delay)
|
|
2833
|
-
continue
|
|
2834
|
-
else:
|
|
2835
|
-
# Final attempt failed - re-raise the exception
|
|
2836
|
-
log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
|
|
2837
|
-
if stream:
|
|
2838
|
-
return async_generator_wrapper(create_run_error_event(run_response, error=str(e))) # type: ignore
|
|
2839
|
-
raise e
|
|
2840
|
-
|
|
2841
|
-
# If we get here, all retries failed
|
|
2842
|
-
raise Exception(f"Failed after {num_attempts} attempts.")
|
|
2947
|
+
# Pass the new run_response to _arun
|
|
2948
|
+
if stream:
|
|
2949
|
+
return self._arun_stream( # type: ignore
|
|
2950
|
+
run_response=run_response,
|
|
2951
|
+
run_context=run_context,
|
|
2952
|
+
user_id=user_id,
|
|
2953
|
+
response_format=response_format,
|
|
2954
|
+
stream_events=stream_events,
|
|
2955
|
+
yield_run_output=yield_run_output,
|
|
2956
|
+
session_id=session_id,
|
|
2957
|
+
add_history_to_context=add_history,
|
|
2958
|
+
add_dependencies_to_context=add_dependencies,
|
|
2959
|
+
add_session_state_to_context=add_session_state,
|
|
2960
|
+
debug_mode=debug_mode,
|
|
2961
|
+
background_tasks=background_tasks,
|
|
2962
|
+
**kwargs,
|
|
2963
|
+
) # type: ignore[assignment]
|
|
2964
|
+
else:
|
|
2965
|
+
return self._arun( # type: ignore
|
|
2966
|
+
run_response=run_response,
|
|
2967
|
+
run_context=run_context,
|
|
2968
|
+
user_id=user_id,
|
|
2969
|
+
response_format=response_format,
|
|
2970
|
+
session_id=session_id,
|
|
2971
|
+
add_history_to_context=add_history,
|
|
2972
|
+
add_dependencies_to_context=add_dependencies,
|
|
2973
|
+
add_session_state_to_context=add_session_state,
|
|
2974
|
+
debug_mode=debug_mode,
|
|
2975
|
+
background_tasks=background_tasks,
|
|
2976
|
+
**kwargs,
|
|
2977
|
+
)
|
|
2843
2978
|
|
|
2844
2979
|
@overload
|
|
2845
2980
|
def continue_run(
|
|
@@ -2970,7 +3105,7 @@ class Agent:
|
|
|
2970
3105
|
num_attempts = self.retries + 1
|
|
2971
3106
|
|
|
2972
3107
|
for attempt in range(num_attempts):
|
|
2973
|
-
if
|
|
3108
|
+
if num_attempts > 1:
|
|
2974
3109
|
log_debug(f"Retrying Agent continue_run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2975
3110
|
|
|
2976
3111
|
try:
|
|
@@ -3611,7 +3746,7 @@ class Agent:
|
|
|
3611
3746
|
)
|
|
3612
3747
|
|
|
3613
3748
|
for attempt in range(num_attempts):
|
|
3614
|
-
if
|
|
3749
|
+
if num_attempts > 1:
|
|
3615
3750
|
log_debug(f"Retrying Agent acontinue_run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
3616
3751
|
|
|
3617
3752
|
try:
|
|
@@ -4689,22 +4824,34 @@ class Agent:
|
|
|
4689
4824
|
output_schema = run_context.output_schema if run_context else None
|
|
4690
4825
|
|
|
4691
4826
|
# Convert the response to the structured format if needed
|
|
4692
|
-
if output_schema is not None
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
run_response.content = structured_output
|
|
4827
|
+
if output_schema is not None:
|
|
4828
|
+
# If the output schema is a dict, do not convert it into a BaseModel
|
|
4829
|
+
if isinstance(output_schema, dict):
|
|
4830
|
+
if isinstance(run_response.content, str):
|
|
4831
|
+
parsed_dict = parse_response_dict_str(run_response.content)
|
|
4832
|
+
if parsed_dict is not None:
|
|
4833
|
+
run_response.content = parsed_dict
|
|
4700
4834
|
if isinstance(run_response, RunOutput):
|
|
4701
|
-
run_response.content_type =
|
|
4835
|
+
run_response.content_type = "dict"
|
|
4702
4836
|
else:
|
|
4703
|
-
log_warning("Failed to
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4837
|
+
log_warning("Failed to parse JSON response against the provided output schema.")
|
|
4838
|
+
# If the output schema is a Pydantic model and parse_response is True, parse it into a BaseModel
|
|
4839
|
+
elif not isinstance(run_response.content, output_schema):
|
|
4840
|
+
if isinstance(run_response.content, str) and self.parse_response:
|
|
4841
|
+
try:
|
|
4842
|
+
structured_output = parse_response_model_str(run_response.content, output_schema)
|
|
4843
|
+
|
|
4844
|
+
# Update RunOutput
|
|
4845
|
+
if structured_output is not None:
|
|
4846
|
+
run_response.content = structured_output
|
|
4847
|
+
if isinstance(run_response, RunOutput):
|
|
4848
|
+
run_response.content_type = output_schema.__name__
|
|
4849
|
+
else:
|
|
4850
|
+
log_warning("Failed to convert response to output_schema")
|
|
4851
|
+
except Exception as e:
|
|
4852
|
+
log_warning(f"Failed to convert response to output model: {e}")
|
|
4853
|
+
else:
|
|
4854
|
+
log_warning("Something went wrong. Run response content is not a string")
|
|
4708
4855
|
|
|
4709
4856
|
def _handle_external_execution_update(self, run_messages: RunMessages, tool: ToolExecution):
|
|
4710
4857
|
self.model = cast(Model, self.model)
|
|
@@ -5053,7 +5200,7 @@ class Agent:
|
|
|
5053
5200
|
# Update the run_response content with the structured output
|
|
5054
5201
|
run_response.content = model_response.parsed
|
|
5055
5202
|
# Update the run_response content_type with the structured output class name
|
|
5056
|
-
run_response.content_type = output_schema.__name__
|
|
5203
|
+
run_response.content_type = "dict" if isinstance(output_schema, dict) else output_schema.__name__
|
|
5057
5204
|
else:
|
|
5058
5205
|
# Update the run_response content with the model response content
|
|
5059
5206
|
run_response.content = model_response.content
|
|
@@ -5339,7 +5486,7 @@ class Agent:
|
|
|
5339
5486
|
|
|
5340
5487
|
# Get output_schema from run_context
|
|
5341
5488
|
output_schema = run_context.output_schema if run_context else None
|
|
5342
|
-
content_type = output_schema.__name__ # type: ignore
|
|
5489
|
+
content_type = "dict" if isinstance(output_schema, dict) else output_schema.__name__ # type: ignore
|
|
5343
5490
|
run_response.content = model_response.content
|
|
5344
5491
|
run_response.content_type = content_type
|
|
5345
5492
|
else:
|
|
@@ -6109,6 +6256,10 @@ class Agent:
|
|
|
6109
6256
|
elif model.supports_json_schema_outputs:
|
|
6110
6257
|
if self.use_json_mode or (not self.structured_outputs):
|
|
6111
6258
|
log_debug("Setting Model.response_format to JSON response mode")
|
|
6259
|
+
# Handle JSON schema - pass through directly (user provides full provider format)
|
|
6260
|
+
if isinstance(output_schema, dict):
|
|
6261
|
+
return output_schema
|
|
6262
|
+
# Handle Pydantic schema
|
|
6112
6263
|
return {
|
|
6113
6264
|
"type": "json_schema",
|
|
6114
6265
|
"json_schema": {
|
|
@@ -7521,8 +7672,8 @@ class Agent:
|
|
|
7521
7672
|
):
|
|
7522
7673
|
system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
7523
7674
|
|
|
7524
|
-
# 3.3.14 Add the response model format prompt if output_schema is provided
|
|
7525
|
-
if output_schema is not None and self.parser_model is not None:
|
|
7675
|
+
# 3.3.14 Add the response model format prompt if output_schema is provided (Pydantic only)
|
|
7676
|
+
if output_schema is not None and self.parser_model is not None and not isinstance(output_schema, dict):
|
|
7526
7677
|
system_message_content += f"{get_response_model_format_prompt(output_schema)}"
|
|
7527
7678
|
|
|
7528
7679
|
# 3.3.15 Add the session state to the system message
|
|
@@ -7868,8 +8019,8 @@ class Agent:
|
|
|
7868
8019
|
):
|
|
7869
8020
|
system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
7870
8021
|
|
|
7871
|
-
# 3.3.14 Add the response model format prompt if output_schema is provided
|
|
7872
|
-
if output_schema is not None and self.parser_model is not None:
|
|
8022
|
+
# 3.3.14 Add the response model format prompt if output_schema is provided (Pydantic only)
|
|
8023
|
+
if output_schema is not None and self.parser_model is not None and not isinstance(output_schema, dict):
|
|
7873
8024
|
system_message_content += f"{get_response_model_format_prompt(output_schema)}"
|
|
7874
8025
|
|
|
7875
8026
|
# 3.3.15 Add the session state to the system message
|
|
@@ -9176,292 +9327,78 @@ class Agent:
|
|
|
9176
9327
|
|
|
9177
9328
|
return updated_reasoning_content
|
|
9178
9329
|
|
|
9179
|
-
def
|
|
9180
|
-
self,
|
|
9330
|
+
def _handle_reasoning_event(
|
|
9331
|
+
self,
|
|
9332
|
+
event: "ReasoningEvent", # type: ignore # noqa: F821
|
|
9333
|
+
run_response: RunOutput,
|
|
9334
|
+
stream_events: Optional[bool] = None,
|
|
9181
9335
|
) -> Iterator[RunOutputEvent]:
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
yield handle_event( # type: ignore
|
|
9185
|
-
create_reasoning_started_event(from_run_response=run_response),
|
|
9186
|
-
run_response,
|
|
9187
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
9188
|
-
store_events=self.store_events,
|
|
9189
|
-
)
|
|
9190
|
-
|
|
9191
|
-
use_default_reasoning = False
|
|
9192
|
-
|
|
9193
|
-
# Get the reasoning model
|
|
9194
|
-
reasoning_model: Optional[Model] = self.reasoning_model
|
|
9195
|
-
reasoning_model_provided = reasoning_model is not None
|
|
9196
|
-
if reasoning_model is None and self.model is not None:
|
|
9197
|
-
from copy import deepcopy
|
|
9198
|
-
|
|
9199
|
-
reasoning_model = deepcopy(self.model)
|
|
9200
|
-
if reasoning_model is None:
|
|
9201
|
-
log_warning("Reasoning error. Reasoning model is None, continuing regular session...")
|
|
9202
|
-
return
|
|
9203
|
-
|
|
9204
|
-
# If a reasoning model is provided, use it to generate reasoning
|
|
9205
|
-
if reasoning_model_provided:
|
|
9206
|
-
from agno.reasoning.anthropic import is_anthropic_reasoning_model
|
|
9207
|
-
from agno.reasoning.azure_ai_foundry import is_ai_foundry_reasoning_model
|
|
9208
|
-
from agno.reasoning.deepseek import is_deepseek_reasoning_model
|
|
9209
|
-
from agno.reasoning.gemini import is_gemini_reasoning_model
|
|
9210
|
-
from agno.reasoning.groq import is_groq_reasoning_model
|
|
9211
|
-
from agno.reasoning.helpers import get_reasoning_agent
|
|
9212
|
-
from agno.reasoning.ollama import is_ollama_reasoning_model
|
|
9213
|
-
from agno.reasoning.openai import is_openai_reasoning_model
|
|
9214
|
-
from agno.reasoning.vertexai import is_vertexai_reasoning_model
|
|
9215
|
-
|
|
9216
|
-
reasoning_agent = self.reasoning_agent or get_reasoning_agent(
|
|
9217
|
-
reasoning_model=reasoning_model,
|
|
9218
|
-
telemetry=self.telemetry,
|
|
9219
|
-
debug_mode=self.debug_mode,
|
|
9220
|
-
debug_level=self.debug_level,
|
|
9221
|
-
session_state=self.session_state,
|
|
9222
|
-
dependencies=self.dependencies,
|
|
9223
|
-
metadata=self.metadata,
|
|
9224
|
-
)
|
|
9225
|
-
is_deepseek = is_deepseek_reasoning_model(reasoning_model)
|
|
9226
|
-
is_groq = is_groq_reasoning_model(reasoning_model)
|
|
9227
|
-
is_openai = is_openai_reasoning_model(reasoning_model)
|
|
9228
|
-
is_ollama = is_ollama_reasoning_model(reasoning_model)
|
|
9229
|
-
is_ai_foundry = is_ai_foundry_reasoning_model(reasoning_model)
|
|
9230
|
-
is_gemini = is_gemini_reasoning_model(reasoning_model)
|
|
9231
|
-
is_anthropic = is_anthropic_reasoning_model(reasoning_model)
|
|
9232
|
-
is_vertexai = is_vertexai_reasoning_model(reasoning_model)
|
|
9233
|
-
|
|
9234
|
-
if (
|
|
9235
|
-
is_deepseek
|
|
9236
|
-
or is_groq
|
|
9237
|
-
or is_openai
|
|
9238
|
-
or is_ollama
|
|
9239
|
-
or is_ai_foundry
|
|
9240
|
-
or is_gemini
|
|
9241
|
-
or is_anthropic
|
|
9242
|
-
or is_vertexai
|
|
9243
|
-
):
|
|
9244
|
-
reasoning_message: Optional[Message] = None
|
|
9245
|
-
if is_deepseek:
|
|
9246
|
-
from agno.reasoning.deepseek import get_deepseek_reasoning
|
|
9247
|
-
|
|
9248
|
-
log_debug("Starting DeepSeek Reasoning", center=True, symbol="=")
|
|
9249
|
-
reasoning_message = get_deepseek_reasoning(
|
|
9250
|
-
reasoning_agent=reasoning_agent,
|
|
9251
|
-
messages=run_messages.get_input_messages(),
|
|
9252
|
-
)
|
|
9253
|
-
elif is_groq:
|
|
9254
|
-
from agno.reasoning.groq import get_groq_reasoning
|
|
9255
|
-
|
|
9256
|
-
log_debug("Starting Groq Reasoning", center=True, symbol="=")
|
|
9257
|
-
reasoning_message = get_groq_reasoning(
|
|
9258
|
-
reasoning_agent=reasoning_agent,
|
|
9259
|
-
messages=run_messages.get_input_messages(),
|
|
9260
|
-
)
|
|
9261
|
-
elif is_openai:
|
|
9262
|
-
from agno.reasoning.openai import get_openai_reasoning
|
|
9263
|
-
|
|
9264
|
-
log_debug("Starting OpenAI Reasoning", center=True, symbol="=")
|
|
9265
|
-
reasoning_message = get_openai_reasoning(
|
|
9266
|
-
reasoning_agent=reasoning_agent,
|
|
9267
|
-
messages=run_messages.get_input_messages(),
|
|
9268
|
-
)
|
|
9269
|
-
elif is_ollama:
|
|
9270
|
-
from agno.reasoning.ollama import get_ollama_reasoning
|
|
9271
|
-
|
|
9272
|
-
log_debug("Starting Ollama Reasoning", center=True, symbol="=")
|
|
9273
|
-
reasoning_message = get_ollama_reasoning(
|
|
9274
|
-
reasoning_agent=reasoning_agent,
|
|
9275
|
-
messages=run_messages.get_input_messages(),
|
|
9276
|
-
)
|
|
9277
|
-
elif is_ai_foundry:
|
|
9278
|
-
from agno.reasoning.azure_ai_foundry import get_ai_foundry_reasoning
|
|
9279
|
-
|
|
9280
|
-
log_debug("Starting Azure AI Foundry Reasoning", center=True, symbol="=")
|
|
9281
|
-
reasoning_message = get_ai_foundry_reasoning(
|
|
9282
|
-
reasoning_agent=reasoning_agent,
|
|
9283
|
-
messages=run_messages.get_input_messages(),
|
|
9284
|
-
)
|
|
9285
|
-
elif is_gemini:
|
|
9286
|
-
from agno.reasoning.gemini import get_gemini_reasoning
|
|
9336
|
+
"""
|
|
9337
|
+
Convert a ReasoningEvent from the ReasoningManager to Agent-specific RunOutputEvents.
|
|
9287
9338
|
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
)
|
|
9293
|
-
elif is_anthropic:
|
|
9294
|
-
from agno.reasoning.anthropic import get_anthropic_reasoning
|
|
9339
|
+
This method handles the conversion of generic reasoning events to Agent events,
|
|
9340
|
+
keeping the Agent._reason() method clean and simple.
|
|
9341
|
+
"""
|
|
9342
|
+
from agno.reasoning.manager import ReasoningEventType
|
|
9295
9343
|
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9344
|
+
if event.event_type == ReasoningEventType.started:
|
|
9345
|
+
if stream_events:
|
|
9346
|
+
yield handle_event( # type: ignore
|
|
9347
|
+
create_reasoning_started_event(from_run_response=run_response),
|
|
9348
|
+
run_response,
|
|
9349
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
9350
|
+
store_events=self.store_events,
|
|
9351
|
+
)
|
|
9303
9352
|
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9353
|
+
elif event.event_type == ReasoningEventType.content_delta:
|
|
9354
|
+
if stream_events and event.reasoning_content:
|
|
9355
|
+
yield handle_event( # type: ignore
|
|
9356
|
+
create_reasoning_content_delta_event(
|
|
9357
|
+
from_run_response=run_response,
|
|
9358
|
+
reasoning_content=event.reasoning_content,
|
|
9359
|
+
),
|
|
9360
|
+
run_response,
|
|
9361
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
9362
|
+
store_events=self.store_events,
|
|
9363
|
+
)
|
|
9309
9364
|
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
run_messages.messages.append(reasoning_message)
|
|
9314
|
-
# Add reasoning step to the Agent's run_response
|
|
9365
|
+
elif event.event_type == ReasoningEventType.step:
|
|
9366
|
+
if event.reasoning_step:
|
|
9367
|
+
# Update run_response with this step
|
|
9315
9368
|
update_run_output_with_reasoning(
|
|
9316
9369
|
run_response=run_response,
|
|
9317
|
-
reasoning_steps=[
|
|
9318
|
-
reasoning_agent_messages=[
|
|
9370
|
+
reasoning_steps=[event.reasoning_step],
|
|
9371
|
+
reasoning_agent_messages=[],
|
|
9319
9372
|
)
|
|
9320
9373
|
if stream_events:
|
|
9374
|
+
updated_reasoning_content = self._format_reasoning_step_content(
|
|
9375
|
+
run_response=run_response,
|
|
9376
|
+
reasoning_step=event.reasoning_step,
|
|
9377
|
+
)
|
|
9321
9378
|
yield handle_event( # type: ignore
|
|
9322
|
-
|
|
9379
|
+
create_reasoning_step_event(
|
|
9323
9380
|
from_run_response=run_response,
|
|
9324
|
-
|
|
9325
|
-
|
|
9381
|
+
reasoning_step=event.reasoning_step,
|
|
9382
|
+
reasoning_content=updated_reasoning_content,
|
|
9326
9383
|
),
|
|
9327
9384
|
run_response,
|
|
9328
9385
|
events_to_skip=self.events_to_skip, # type: ignore
|
|
9329
9386
|
store_events=self.store_events,
|
|
9330
9387
|
)
|
|
9331
|
-
else:
|
|
9332
|
-
log_info(
|
|
9333
|
-
f"Reasoning model: {reasoning_model.__class__.__name__} is not a native reasoning model, defaulting to manual Chain-of-Thought reasoning"
|
|
9334
|
-
)
|
|
9335
|
-
use_default_reasoning = True
|
|
9336
|
-
# If no reasoning model is provided, use default reasoning
|
|
9337
|
-
else:
|
|
9338
|
-
use_default_reasoning = True
|
|
9339
|
-
|
|
9340
|
-
if use_default_reasoning:
|
|
9341
|
-
from agno.reasoning.default import get_default_reasoning_agent
|
|
9342
|
-
from agno.reasoning.helpers import (
|
|
9343
|
-
get_next_action,
|
|
9344
|
-
update_messages_with_reasoning,
|
|
9345
|
-
)
|
|
9346
|
-
|
|
9347
|
-
# Get default reasoning agent
|
|
9348
|
-
reasoning_agent: Optional[Agent] = self.reasoning_agent # type: ignore
|
|
9349
|
-
if reasoning_agent is None:
|
|
9350
|
-
reasoning_agent = get_default_reasoning_agent(
|
|
9351
|
-
reasoning_model=reasoning_model,
|
|
9352
|
-
min_steps=self.reasoning_min_steps,
|
|
9353
|
-
max_steps=self.reasoning_max_steps,
|
|
9354
|
-
tools=self.tools,
|
|
9355
|
-
tool_call_limit=self.tool_call_limit,
|
|
9356
|
-
use_json_mode=self.use_json_mode,
|
|
9357
|
-
telemetry=self.telemetry,
|
|
9358
|
-
debug_mode=self.debug_mode,
|
|
9359
|
-
debug_level=self.debug_level,
|
|
9360
|
-
session_state=self.session_state,
|
|
9361
|
-
dependencies=self.dependencies,
|
|
9362
|
-
metadata=self.metadata,
|
|
9363
|
-
)
|
|
9364
|
-
|
|
9365
|
-
# Validate reasoning agent
|
|
9366
|
-
if reasoning_agent is None:
|
|
9367
|
-
log_warning("Reasoning error. Reasoning agent is None, continuing regular session...")
|
|
9368
|
-
return
|
|
9369
|
-
# Ensure the reasoning agent response model is ReasoningSteps
|
|
9370
|
-
if (
|
|
9371
|
-
reasoning_agent.output_schema is not None
|
|
9372
|
-
and not isinstance(reasoning_agent.output_schema, type)
|
|
9373
|
-
and not issubclass(reasoning_agent.output_schema, ReasoningSteps)
|
|
9374
|
-
):
|
|
9375
|
-
log_warning("Reasoning agent response model should be `ReasoningSteps`, continuing regular session...")
|
|
9376
|
-
return
|
|
9377
|
-
|
|
9378
|
-
step_count = 1
|
|
9379
|
-
next_action = NextAction.CONTINUE
|
|
9380
|
-
reasoning_messages: List[Message] = []
|
|
9381
|
-
all_reasoning_steps: List[ReasoningStep] = []
|
|
9382
|
-
log_debug("Starting Reasoning", center=True, symbol="=")
|
|
9383
|
-
while next_action == NextAction.CONTINUE and step_count < self.reasoning_max_steps:
|
|
9384
|
-
log_debug(f"Step {step_count}", center=True, symbol="=")
|
|
9385
|
-
try:
|
|
9386
|
-
# Run the reasoning agent
|
|
9387
|
-
reasoning_agent_response: RunOutput = reasoning_agent.run(input=run_messages.get_input_messages())
|
|
9388
|
-
if reasoning_agent_response.content is None or reasoning_agent_response.messages is None:
|
|
9389
|
-
log_warning("Reasoning error. Reasoning response is empty, continuing regular session...")
|
|
9390
|
-
break
|
|
9391
|
-
|
|
9392
|
-
if isinstance(reasoning_agent_response.content, str):
|
|
9393
|
-
log_warning(
|
|
9394
|
-
"Reasoning error. Content is a string, not structured output. Continuing regular session..."
|
|
9395
|
-
)
|
|
9396
|
-
break
|
|
9397
|
-
|
|
9398
|
-
if reasoning_agent_response.content is not None and (
|
|
9399
|
-
reasoning_agent_response.content.reasoning_steps is None
|
|
9400
|
-
or len(reasoning_agent_response.content.reasoning_steps) == 0
|
|
9401
|
-
):
|
|
9402
|
-
log_warning("Reasoning error. Reasoning steps are empty, continuing regular session...")
|
|
9403
|
-
break
|
|
9404
|
-
|
|
9405
|
-
reasoning_steps: List[ReasoningStep] = reasoning_agent_response.content.reasoning_steps
|
|
9406
|
-
all_reasoning_steps.extend(reasoning_steps)
|
|
9407
|
-
# Yield reasoning steps
|
|
9408
|
-
if stream_events:
|
|
9409
|
-
for reasoning_step in reasoning_steps:
|
|
9410
|
-
updated_reasoning_content = self._format_reasoning_step_content(
|
|
9411
|
-
run_response=run_response,
|
|
9412
|
-
reasoning_step=reasoning_step,
|
|
9413
|
-
)
|
|
9414
|
-
|
|
9415
|
-
yield handle_event( # type: ignore
|
|
9416
|
-
create_reasoning_step_event(
|
|
9417
|
-
from_run_response=run_response,
|
|
9418
|
-
reasoning_step=reasoning_step,
|
|
9419
|
-
reasoning_content=updated_reasoning_content,
|
|
9420
|
-
),
|
|
9421
|
-
run_response,
|
|
9422
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
9423
|
-
store_events=self.store_events,
|
|
9424
|
-
)
|
|
9425
|
-
|
|
9426
|
-
# Find the index of the first assistant message
|
|
9427
|
-
first_assistant_index = next(
|
|
9428
|
-
(i for i, m in enumerate(reasoning_agent_response.messages) if m.role == "assistant"),
|
|
9429
|
-
len(reasoning_agent_response.messages),
|
|
9430
|
-
)
|
|
9431
|
-
# Extract reasoning messages starting from the message after the first assistant message
|
|
9432
|
-
reasoning_messages = reasoning_agent_response.messages[first_assistant_index:]
|
|
9433
|
-
|
|
9434
|
-
# Add reasoning step to the Agent's run_response
|
|
9435
|
-
update_run_output_with_reasoning(
|
|
9436
|
-
run_response=run_response,
|
|
9437
|
-
reasoning_steps=reasoning_steps,
|
|
9438
|
-
reasoning_agent_messages=reasoning_agent_response.messages,
|
|
9439
|
-
)
|
|
9440
|
-
# Get the next action
|
|
9441
|
-
next_action = get_next_action(reasoning_steps[-1])
|
|
9442
|
-
if next_action == NextAction.FINAL_ANSWER:
|
|
9443
|
-
break
|
|
9444
|
-
except Exception as e:
|
|
9445
|
-
log_error(f"Reasoning error: {e}")
|
|
9446
|
-
break
|
|
9447
|
-
|
|
9448
|
-
step_count += 1
|
|
9449
|
-
|
|
9450
|
-
log_debug(f"Total Reasoning steps: {len(all_reasoning_steps)}")
|
|
9451
|
-
log_debug("Reasoning finished", center=True, symbol="=")
|
|
9452
|
-
|
|
9453
|
-
# Update the messages_for_model to include reasoning messages
|
|
9454
|
-
update_messages_with_reasoning(
|
|
9455
|
-
run_messages=run_messages,
|
|
9456
|
-
reasoning_messages=reasoning_messages,
|
|
9457
|
-
)
|
|
9458
9388
|
|
|
9459
|
-
|
|
9389
|
+
elif event.event_type == ReasoningEventType.completed:
|
|
9390
|
+
if event.message and event.reasoning_steps:
|
|
9391
|
+
# This is from native reasoning - update with the message and steps
|
|
9392
|
+
update_run_output_with_reasoning(
|
|
9393
|
+
run_response=run_response,
|
|
9394
|
+
reasoning_steps=event.reasoning_steps,
|
|
9395
|
+
reasoning_agent_messages=event.reasoning_messages,
|
|
9396
|
+
)
|
|
9460
9397
|
if stream_events:
|
|
9461
9398
|
yield handle_event( # type: ignore
|
|
9462
9399
|
create_reasoning_completed_event(
|
|
9463
9400
|
from_run_response=run_response,
|
|
9464
|
-
content=ReasoningSteps(reasoning_steps=
|
|
9401
|
+
content=ReasoningSteps(reasoning_steps=event.reasoning_steps),
|
|
9465
9402
|
content_type=ReasoningSteps.__name__,
|
|
9466
9403
|
),
|
|
9467
9404
|
run_response,
|
|
@@ -9469,45 +9406,37 @@ class Agent:
|
|
|
9469
9406
|
store_events=self.store_events,
|
|
9470
9407
|
)
|
|
9471
9408
|
|
|
9472
|
-
|
|
9409
|
+
elif event.event_type == ReasoningEventType.error:
|
|
9410
|
+
log_warning(f"Reasoning error. {event.error}, continuing regular session...")
|
|
9411
|
+
|
|
9412
|
+
def _reason(
|
|
9473
9413
|
self, run_response: RunOutput, run_messages: RunMessages, stream_events: Optional[bool] = None
|
|
9474
|
-
) ->
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
yield handle_event( # type: ignore
|
|
9478
|
-
create_reasoning_started_event(from_run_response=run_response),
|
|
9479
|
-
run_response,
|
|
9480
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
9481
|
-
store_events=self.store_events,
|
|
9482
|
-
)
|
|
9414
|
+
) -> Iterator[RunOutputEvent]:
|
|
9415
|
+
"""
|
|
9416
|
+
Run reasoning using the ReasoningManager.
|
|
9483
9417
|
|
|
9484
|
-
|
|
9418
|
+
Handles both native reasoning models (DeepSeek, Anthropic, etc.) and
|
|
9419
|
+
default Chain-of-Thought reasoning with a clean, unified interface.
|
|
9420
|
+
"""
|
|
9421
|
+
from agno.reasoning.manager import ReasoningConfig, ReasoningManager
|
|
9485
9422
|
|
|
9486
|
-
# Get the reasoning model
|
|
9423
|
+
# Get the reasoning model (use copy of main model if not provided)
|
|
9487
9424
|
reasoning_model: Optional[Model] = self.reasoning_model
|
|
9488
|
-
reasoning_model_provided = reasoning_model is not None
|
|
9489
9425
|
if reasoning_model is None and self.model is not None:
|
|
9490
9426
|
from copy import deepcopy
|
|
9491
9427
|
|
|
9492
9428
|
reasoning_model = deepcopy(self.model)
|
|
9493
|
-
if reasoning_model is None:
|
|
9494
|
-
log_warning("Reasoning error. Reasoning model is None, continuing regular session...")
|
|
9495
|
-
return
|
|
9496
9429
|
|
|
9497
|
-
#
|
|
9498
|
-
|
|
9499
|
-
|
|
9500
|
-
from agno.reasoning.azure_ai_foundry import is_ai_foundry_reasoning_model
|
|
9501
|
-
from agno.reasoning.deepseek import is_deepseek_reasoning_model
|
|
9502
|
-
from agno.reasoning.gemini import is_gemini_reasoning_model
|
|
9503
|
-
from agno.reasoning.groq import is_groq_reasoning_model
|
|
9504
|
-
from agno.reasoning.helpers import get_reasoning_agent
|
|
9505
|
-
from agno.reasoning.ollama import is_ollama_reasoning_model
|
|
9506
|
-
from agno.reasoning.openai import is_openai_reasoning_model
|
|
9507
|
-
from agno.reasoning.vertexai import is_vertexai_reasoning_model
|
|
9508
|
-
|
|
9509
|
-
reasoning_agent = self.reasoning_agent or get_reasoning_agent(
|
|
9430
|
+
# Create reasoning manager with config
|
|
9431
|
+
manager = ReasoningManager(
|
|
9432
|
+
ReasoningConfig(
|
|
9510
9433
|
reasoning_model=reasoning_model,
|
|
9434
|
+
reasoning_agent=self.reasoning_agent,
|
|
9435
|
+
min_steps=self.reasoning_min_steps,
|
|
9436
|
+
max_steps=self.reasoning_max_steps,
|
|
9437
|
+
tools=self.tools,
|
|
9438
|
+
tool_call_limit=self.tool_call_limit,
|
|
9439
|
+
use_json_mode=self.use_json_mode,
|
|
9511
9440
|
telemetry=self.telemetry,
|
|
9512
9441
|
debug_mode=self.debug_mode,
|
|
9513
9442
|
debug_level=self.debug_level,
|
|
@@ -9515,252 +9444,53 @@ class Agent:
|
|
|
9515
9444
|
dependencies=self.dependencies,
|
|
9516
9445
|
metadata=self.metadata,
|
|
9517
9446
|
)
|
|
9518
|
-
|
|
9519
|
-
is_groq = is_groq_reasoning_model(reasoning_model)
|
|
9520
|
-
is_openai = is_openai_reasoning_model(reasoning_model)
|
|
9521
|
-
is_ollama = is_ollama_reasoning_model(reasoning_model)
|
|
9522
|
-
is_ai_foundry = is_ai_foundry_reasoning_model(reasoning_model)
|
|
9523
|
-
is_gemini = is_gemini_reasoning_model(reasoning_model)
|
|
9524
|
-
is_anthropic = is_anthropic_reasoning_model(reasoning_model)
|
|
9525
|
-
is_vertexai = is_vertexai_reasoning_model(reasoning_model)
|
|
9526
|
-
|
|
9527
|
-
if (
|
|
9528
|
-
is_deepseek
|
|
9529
|
-
or is_groq
|
|
9530
|
-
or is_openai
|
|
9531
|
-
or is_ollama
|
|
9532
|
-
or is_ai_foundry
|
|
9533
|
-
or is_gemini
|
|
9534
|
-
or is_anthropic
|
|
9535
|
-
or is_vertexai
|
|
9536
|
-
):
|
|
9537
|
-
reasoning_message: Optional[Message] = None
|
|
9538
|
-
if is_deepseek:
|
|
9539
|
-
from agno.reasoning.deepseek import aget_deepseek_reasoning
|
|
9540
|
-
|
|
9541
|
-
log_debug("Starting DeepSeek Reasoning", center=True, symbol="=")
|
|
9542
|
-
reasoning_message = await aget_deepseek_reasoning(
|
|
9543
|
-
reasoning_agent=reasoning_agent,
|
|
9544
|
-
messages=run_messages.get_input_messages(),
|
|
9545
|
-
)
|
|
9546
|
-
elif is_groq:
|
|
9547
|
-
from agno.reasoning.groq import aget_groq_reasoning
|
|
9548
|
-
|
|
9549
|
-
log_debug("Starting Groq Reasoning", center=True, symbol="=")
|
|
9550
|
-
reasoning_message = await aget_groq_reasoning(
|
|
9551
|
-
reasoning_agent=reasoning_agent,
|
|
9552
|
-
messages=run_messages.get_input_messages(),
|
|
9553
|
-
)
|
|
9554
|
-
elif is_openai:
|
|
9555
|
-
from agno.reasoning.openai import aget_openai_reasoning
|
|
9556
|
-
|
|
9557
|
-
log_debug("Starting OpenAI Reasoning", center=True, symbol="=")
|
|
9558
|
-
reasoning_message = await aget_openai_reasoning(
|
|
9559
|
-
reasoning_agent=reasoning_agent,
|
|
9560
|
-
messages=run_messages.get_input_messages(),
|
|
9561
|
-
)
|
|
9562
|
-
elif is_ollama:
|
|
9563
|
-
from agno.reasoning.ollama import get_ollama_reasoning
|
|
9564
|
-
|
|
9565
|
-
log_debug("Starting Ollama Reasoning", center=True, symbol="=")
|
|
9566
|
-
reasoning_message = get_ollama_reasoning(
|
|
9567
|
-
reasoning_agent=reasoning_agent,
|
|
9568
|
-
messages=run_messages.get_input_messages(),
|
|
9569
|
-
)
|
|
9570
|
-
elif is_ai_foundry:
|
|
9571
|
-
from agno.reasoning.azure_ai_foundry import get_ai_foundry_reasoning
|
|
9572
|
-
|
|
9573
|
-
log_debug("Starting Azure AI Foundry Reasoning", center=True, symbol="=")
|
|
9574
|
-
reasoning_message = get_ai_foundry_reasoning(
|
|
9575
|
-
reasoning_agent=reasoning_agent,
|
|
9576
|
-
messages=run_messages.get_input_messages(),
|
|
9577
|
-
)
|
|
9578
|
-
elif is_gemini:
|
|
9579
|
-
from agno.reasoning.gemini import aget_gemini_reasoning
|
|
9580
|
-
|
|
9581
|
-
log_debug("Starting Gemini Reasoning", center=True, symbol="=")
|
|
9582
|
-
reasoning_message = await aget_gemini_reasoning(
|
|
9583
|
-
reasoning_agent=reasoning_agent,
|
|
9584
|
-
messages=run_messages.get_input_messages(),
|
|
9585
|
-
)
|
|
9586
|
-
elif is_anthropic:
|
|
9587
|
-
from agno.reasoning.anthropic import aget_anthropic_reasoning
|
|
9588
|
-
|
|
9589
|
-
log_debug("Starting Anthropic Claude Reasoning", center=True, symbol="=")
|
|
9590
|
-
reasoning_message = await aget_anthropic_reasoning(
|
|
9591
|
-
reasoning_agent=reasoning_agent,
|
|
9592
|
-
messages=run_messages.get_input_messages(),
|
|
9593
|
-
)
|
|
9594
|
-
elif is_vertexai:
|
|
9595
|
-
from agno.reasoning.vertexai import aget_vertexai_reasoning
|
|
9596
|
-
|
|
9597
|
-
log_debug("Starting VertexAI Reasoning", center=True, symbol="=")
|
|
9598
|
-
reasoning_message = await aget_vertexai_reasoning(
|
|
9599
|
-
reasoning_agent=reasoning_agent,
|
|
9600
|
-
messages=run_messages.get_input_messages(),
|
|
9601
|
-
)
|
|
9602
|
-
|
|
9603
|
-
if reasoning_message is None:
|
|
9604
|
-
log_warning("Reasoning error. Reasoning response is None, continuing regular session...")
|
|
9605
|
-
return
|
|
9606
|
-
run_messages.messages.append(reasoning_message)
|
|
9607
|
-
# Add reasoning step to the Agent's run_response
|
|
9608
|
-
update_run_output_with_reasoning(
|
|
9609
|
-
run_response=run_response,
|
|
9610
|
-
reasoning_steps=[ReasoningStep(result=reasoning_message.content)],
|
|
9611
|
-
reasoning_agent_messages=[reasoning_message],
|
|
9612
|
-
)
|
|
9613
|
-
if stream_events:
|
|
9614
|
-
yield handle_event(
|
|
9615
|
-
create_reasoning_completed_event(
|
|
9616
|
-
from_run_response=run_response,
|
|
9617
|
-
content=ReasoningSteps(reasoning_steps=[ReasoningStep(result=reasoning_message.content)]),
|
|
9618
|
-
content_type=ReasoningSteps.__name__,
|
|
9619
|
-
),
|
|
9620
|
-
run_response,
|
|
9621
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
9622
|
-
store_events=self.store_events,
|
|
9623
|
-
)
|
|
9624
|
-
else:
|
|
9625
|
-
log_info(
|
|
9626
|
-
f"Reasoning model: {reasoning_model.__class__.__name__} is not a native reasoning model, defaulting to manual Chain-of-Thought reasoning"
|
|
9627
|
-
)
|
|
9628
|
-
use_default_reasoning = True
|
|
9629
|
-
# If no reasoning model is provided, use default reasoning
|
|
9630
|
-
else:
|
|
9631
|
-
use_default_reasoning = True
|
|
9632
|
-
|
|
9633
|
-
if use_default_reasoning:
|
|
9634
|
-
from agno.reasoning.default import get_default_reasoning_agent
|
|
9635
|
-
from agno.reasoning.helpers import (
|
|
9636
|
-
get_next_action,
|
|
9637
|
-
update_messages_with_reasoning,
|
|
9638
|
-
)
|
|
9639
|
-
|
|
9640
|
-
# Get default reasoning agent
|
|
9641
|
-
reasoning_agent: Optional[Agent] = self.reasoning_agent # type: ignore
|
|
9642
|
-
if reasoning_agent is None:
|
|
9643
|
-
reasoning_agent = get_default_reasoning_agent(
|
|
9644
|
-
reasoning_model=reasoning_model,
|
|
9645
|
-
min_steps=self.reasoning_min_steps,
|
|
9646
|
-
max_steps=self.reasoning_max_steps,
|
|
9647
|
-
tools=self.tools,
|
|
9648
|
-
tool_call_limit=self.tool_call_limit,
|
|
9649
|
-
use_json_mode=self.use_json_mode,
|
|
9650
|
-
telemetry=self.telemetry,
|
|
9651
|
-
debug_mode=self.debug_mode,
|
|
9652
|
-
debug_level=self.debug_level,
|
|
9653
|
-
session_state=self.session_state,
|
|
9654
|
-
dependencies=self.dependencies,
|
|
9655
|
-
metadata=self.metadata,
|
|
9656
|
-
)
|
|
9657
|
-
|
|
9658
|
-
# Validate reasoning agent
|
|
9659
|
-
if reasoning_agent is None:
|
|
9660
|
-
log_warning("Reasoning error. Reasoning agent is None, continuing regular session...")
|
|
9661
|
-
return
|
|
9662
|
-
# Ensure the reasoning agent response model is ReasoningSteps
|
|
9663
|
-
if (
|
|
9664
|
-
reasoning_agent.output_schema is not None
|
|
9665
|
-
and not isinstance(reasoning_agent.output_schema, type)
|
|
9666
|
-
and not issubclass(reasoning_agent.output_schema, ReasoningSteps)
|
|
9667
|
-
):
|
|
9668
|
-
log_warning("Reasoning agent response model should be `ReasoningSteps`, continuing regular session...")
|
|
9669
|
-
return
|
|
9670
|
-
|
|
9671
|
-
step_count = 1
|
|
9672
|
-
next_action = NextAction.CONTINUE
|
|
9673
|
-
reasoning_messages: List[Message] = []
|
|
9674
|
-
all_reasoning_steps: List[ReasoningStep] = []
|
|
9675
|
-
log_debug("Starting Reasoning", center=True, symbol="=")
|
|
9676
|
-
while next_action == NextAction.CONTINUE and step_count < self.reasoning_max_steps:
|
|
9677
|
-
log_debug(f"Step {step_count}", center=True, symbol="=")
|
|
9678
|
-
step_count += 1
|
|
9679
|
-
try:
|
|
9680
|
-
# Run the reasoning agent
|
|
9681
|
-
reasoning_agent_response: RunOutput = await reasoning_agent.arun(
|
|
9682
|
-
input=run_messages.get_input_messages()
|
|
9683
|
-
)
|
|
9684
|
-
if reasoning_agent_response.content is None or reasoning_agent_response.messages is None:
|
|
9685
|
-
log_warning("Reasoning error. Reasoning response is empty, continuing regular session...")
|
|
9686
|
-
break
|
|
9687
|
-
|
|
9688
|
-
if isinstance(reasoning_agent_response.content, str):
|
|
9689
|
-
log_warning(
|
|
9690
|
-
"Reasoning error. Content is a string, not structured output. Continuing regular session..."
|
|
9691
|
-
)
|
|
9692
|
-
break
|
|
9693
|
-
|
|
9694
|
-
if reasoning_agent_response.content.reasoning_steps is None:
|
|
9695
|
-
log_warning("Reasoning error. Reasoning steps are empty, continuing regular session...")
|
|
9696
|
-
break
|
|
9697
|
-
|
|
9698
|
-
reasoning_steps: List[ReasoningStep] = reasoning_agent_response.content.reasoning_steps
|
|
9699
|
-
all_reasoning_steps.extend(reasoning_steps)
|
|
9700
|
-
# Yield reasoning steps
|
|
9701
|
-
if stream_events:
|
|
9702
|
-
for reasoning_step in reasoning_steps:
|
|
9703
|
-
updated_reasoning_content = self._format_reasoning_step_content(
|
|
9704
|
-
run_response=run_response,
|
|
9705
|
-
reasoning_step=reasoning_step,
|
|
9706
|
-
)
|
|
9447
|
+
)
|
|
9707
9448
|
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
from_run_response=run_response,
|
|
9712
|
-
reasoning_step=reasoning_step,
|
|
9713
|
-
reasoning_content=updated_reasoning_content,
|
|
9714
|
-
),
|
|
9715
|
-
run_response,
|
|
9716
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
9717
|
-
store_events=self.store_events,
|
|
9718
|
-
)
|
|
9449
|
+
# Use the unified reason() method and convert events
|
|
9450
|
+
for event in manager.reason(run_messages, stream=bool(stream_events)):
|
|
9451
|
+
yield from self._handle_reasoning_event(event, run_response, stream_events)
|
|
9719
9452
|
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
# Extract reasoning messages starting from the message after the first assistant message
|
|
9726
|
-
reasoning_messages = reasoning_agent_response.messages[first_assistant_index:]
|
|
9453
|
+
async def _areason(
|
|
9454
|
+
self, run_response: RunOutput, run_messages: RunMessages, stream_events: Optional[bool] = None
|
|
9455
|
+
) -> Any:
|
|
9456
|
+
"""
|
|
9457
|
+
Run reasoning asynchronously using the ReasoningManager.
|
|
9727
9458
|
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
reasoning_agent_messages=reasoning_agent_response.messages,
|
|
9733
|
-
)
|
|
9459
|
+
Handles both native reasoning models (DeepSeek, Anthropic, etc.) and
|
|
9460
|
+
default Chain-of-Thought reasoning with a clean, unified interface.
|
|
9461
|
+
"""
|
|
9462
|
+
from agno.reasoning.manager import ReasoningConfig, ReasoningManager
|
|
9734
9463
|
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
except Exception as e:
|
|
9740
|
-
log_error(f"Reasoning error: {e}")
|
|
9741
|
-
break
|
|
9464
|
+
# Get the reasoning model (use copy of main model if not provided)
|
|
9465
|
+
reasoning_model: Optional[Model] = self.reasoning_model
|
|
9466
|
+
if reasoning_model is None and self.model is not None:
|
|
9467
|
+
from copy import deepcopy
|
|
9742
9468
|
|
|
9743
|
-
|
|
9744
|
-
log_debug("Reasoning finished", center=True, symbol="=")
|
|
9469
|
+
reasoning_model = deepcopy(self.model)
|
|
9745
9470
|
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9471
|
+
# Create reasoning manager with config
|
|
9472
|
+
manager = ReasoningManager(
|
|
9473
|
+
ReasoningConfig(
|
|
9474
|
+
reasoning_model=reasoning_model,
|
|
9475
|
+
reasoning_agent=self.reasoning_agent,
|
|
9476
|
+
min_steps=self.reasoning_min_steps,
|
|
9477
|
+
max_steps=self.reasoning_max_steps,
|
|
9478
|
+
tools=self.tools,
|
|
9479
|
+
tool_call_limit=self.tool_call_limit,
|
|
9480
|
+
use_json_mode=self.use_json_mode,
|
|
9481
|
+
telemetry=self.telemetry,
|
|
9482
|
+
debug_mode=self.debug_mode,
|
|
9483
|
+
debug_level=self.debug_level,
|
|
9484
|
+
session_state=self.session_state,
|
|
9485
|
+
dependencies=self.dependencies,
|
|
9486
|
+
metadata=self.metadata,
|
|
9750
9487
|
)
|
|
9488
|
+
)
|
|
9751
9489
|
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
from_run_response=run_response,
|
|
9757
|
-
content=ReasoningSteps(reasoning_steps=all_reasoning_steps),
|
|
9758
|
-
content_type=ReasoningSteps.__name__,
|
|
9759
|
-
),
|
|
9760
|
-
run_response,
|
|
9761
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
9762
|
-
store_events=self.store_events,
|
|
9763
|
-
)
|
|
9490
|
+
# Use the unified areason() method and convert events
|
|
9491
|
+
async for event in manager.areason(run_messages, stream=bool(stream_events)):
|
|
9492
|
+
for output_event in self._handle_reasoning_event(event, run_response, stream_events):
|
|
9493
|
+
yield output_event
|
|
9764
9494
|
|
|
9765
9495
|
def _process_parser_response(
|
|
9766
9496
|
self,
|
|
@@ -10912,8 +10642,7 @@ class Agent:
|
|
|
10912
10642
|
session: AgentSession,
|
|
10913
10643
|
run_context: Optional[RunContext] = None,
|
|
10914
10644
|
user_id: Optional[str] = None,
|
|
10915
|
-
) -> None:
|
|
10916
|
-
# Scrub the stored run based on storage flags
|
|
10645
|
+
) -> None: # Scrub the stored run based on storage flags
|
|
10917
10646
|
self._scrub_run_output_for_storage(run_response)
|
|
10918
10647
|
|
|
10919
10648
|
# Stop the timer for the Run duration
|