agno 2.3.13__py3-none-any.whl → 2.3.15__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 +1149 -1392
- agno/db/migrations/manager.py +3 -3
- 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 +9 -4
- agno/models/base.py +8 -4
- agno/models/metrics.py +12 -0
- agno/models/openai/chat.py +2 -0
- agno/models/openai/responses.py +2 -2
- agno/os/app.py +59 -2
- agno/os/auth.py +40 -3
- 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/router.py +1 -57
- agno/os/routers/agents/schema.py +14 -1
- agno/os/routers/database.py +150 -0
- agno/os/routers/teams/schema.py +14 -1
- agno/os/settings.py +3 -0
- 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 +21 -0
- agno/run/base.py +20 -1
- agno/run/team.py +21 -0
- agno/session/team.py +0 -3
- agno/team/team.py +1211 -1445
- agno/tools/toolkit.py +119 -8
- agno/utils/events.py +99 -4
- 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.15.dist-info}/METADATA +3 -2
- {agno-2.3.13.dist-info → agno-2.3.15.dist-info}/RECORD +49 -47
- {agno-2.3.13.dist-info → agno-2.3.15.dist-info}/WHEEL +0 -0
- {agno-2.3.13.dist-info → agno-2.3.15.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.13.dist-info → agno-2.3.15.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,
|
|
@@ -132,6 +131,7 @@ from agno.utils.events import (
|
|
|
132
131
|
create_session_summary_completed_event,
|
|
133
132
|
create_session_summary_started_event,
|
|
134
133
|
create_tool_call_completed_event,
|
|
134
|
+
create_tool_call_error_event,
|
|
135
135
|
create_tool_call_started_event,
|
|
136
136
|
handle_event,
|
|
137
137
|
)
|
|
@@ -173,7 +173,7 @@ from agno.utils.response import (
|
|
|
173
173
|
get_paused_content,
|
|
174
174
|
)
|
|
175
175
|
from agno.utils.safe_formatter import SafeFormatter
|
|
176
|
-
from agno.utils.string import generate_id_from_name, parse_response_model_str
|
|
176
|
+
from agno.utils.string import generate_id_from_name, parse_response_dict_str, parse_response_model_str
|
|
177
177
|
from agno.utils.timer import Timer
|
|
178
178
|
|
|
179
179
|
|
|
@@ -280,9 +280,9 @@ class Agent:
|
|
|
280
280
|
|
|
281
281
|
# --- Agent Hooks ---
|
|
282
282
|
# Functions called right after agent-session is loaded, before processing starts
|
|
283
|
-
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
283
|
+
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None
|
|
284
284
|
# Functions called after output is generated but before the response is returned
|
|
285
|
-
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
285
|
+
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None
|
|
286
286
|
# If True, run hooks as FastAPI background tasks (non-blocking). Set by AgentOS.
|
|
287
287
|
_run_hooks_in_background: Optional[bool] = None
|
|
288
288
|
|
|
@@ -369,8 +369,9 @@ class Agent:
|
|
|
369
369
|
# --- Agent Response Model Settings ---
|
|
370
370
|
# Provide an input schema to validate the input
|
|
371
371
|
input_schema: Optional[Type[BaseModel]] = None
|
|
372
|
-
# Provide a response model to get the response
|
|
373
|
-
|
|
372
|
+
# Provide a response model to get the response in the implied format.
|
|
373
|
+
# You can use a Pydantic model or a JSON fitting the provider's expected schema.
|
|
374
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None
|
|
374
375
|
# Provide a secondary model to parse the response from the primary model
|
|
375
376
|
parser_model: Optional[Model] = None
|
|
376
377
|
# Provide a prompt for the parser model
|
|
@@ -487,8 +488,8 @@ class Agent:
|
|
|
487
488
|
tool_call_limit: Optional[int] = None,
|
|
488
489
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
489
490
|
tool_hooks: Optional[List[Callable]] = None,
|
|
490
|
-
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
491
|
-
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
491
|
+
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None,
|
|
492
|
+
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None,
|
|
492
493
|
reasoning: bool = False,
|
|
493
494
|
reasoning_model: Optional[Union[Model, str]] = None,
|
|
494
495
|
reasoning_agent: Optional[Agent] = None,
|
|
@@ -522,7 +523,7 @@ class Agent:
|
|
|
522
523
|
parser_model: Optional[Union[Model, str]] = None,
|
|
523
524
|
parser_model_prompt: Optional[str] = None,
|
|
524
525
|
input_schema: Optional[Type[BaseModel]] = None,
|
|
525
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
526
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
526
527
|
parse_response: bool = True,
|
|
527
528
|
output_model: Optional[Union[Model, str]] = None,
|
|
528
529
|
output_model_prompt: Optional[str] = None,
|
|
@@ -1041,90 +1042,91 @@ class Agent:
|
|
|
1041
1042
|
12. Create session summary
|
|
1042
1043
|
13. Cleanup and store the run response and session
|
|
1043
1044
|
"""
|
|
1045
|
+
memory_future = None
|
|
1046
|
+
cultural_knowledge_future = None
|
|
1044
1047
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1048
|
+
try:
|
|
1049
|
+
# 1. Execute pre-hooks
|
|
1050
|
+
run_input = cast(RunInput, run_response.input)
|
|
1051
|
+
self.model = cast(Model, self.model)
|
|
1052
|
+
if self.pre_hooks is not None:
|
|
1053
|
+
# Can modify the run input
|
|
1054
|
+
pre_hook_iterator = self._execute_pre_hooks(
|
|
1055
|
+
hooks=self.pre_hooks, # type: ignore
|
|
1056
|
+
run_response=run_response,
|
|
1057
|
+
run_input=run_input,
|
|
1058
|
+
run_context=run_context,
|
|
1059
|
+
session=session,
|
|
1060
|
+
user_id=user_id,
|
|
1061
|
+
debug_mode=debug_mode,
|
|
1062
|
+
background_tasks=background_tasks,
|
|
1063
|
+
**kwargs,
|
|
1064
|
+
)
|
|
1065
|
+
# Consume the generator without yielding
|
|
1066
|
+
deque(pre_hook_iterator, maxlen=0)
|
|
1067
|
+
|
|
1068
|
+
# 2. Determine tools for model
|
|
1069
|
+
processed_tools = self.get_tools(
|
|
1052
1070
|
run_response=run_response,
|
|
1053
|
-
run_input=run_input,
|
|
1054
1071
|
run_context=run_context,
|
|
1055
1072
|
session=session,
|
|
1056
1073
|
user_id=user_id,
|
|
1057
|
-
debug_mode=debug_mode,
|
|
1058
|
-
background_tasks=background_tasks,
|
|
1059
|
-
**kwargs,
|
|
1060
1074
|
)
|
|
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
|
-
)
|
|
1075
|
+
_tools = self._determine_tools_for_model(
|
|
1076
|
+
model=self.model,
|
|
1077
|
+
processed_tools=processed_tools,
|
|
1078
|
+
run_response=run_response,
|
|
1079
|
+
session=session,
|
|
1080
|
+
run_context=run_context,
|
|
1081
|
+
)
|
|
1078
1082
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1083
|
+
# 3. Prepare run messages
|
|
1084
|
+
run_messages: RunMessages = self._get_run_messages(
|
|
1085
|
+
run_response=run_response,
|
|
1086
|
+
run_context=run_context,
|
|
1087
|
+
input=run_input.input_content,
|
|
1088
|
+
session=session,
|
|
1089
|
+
user_id=user_id,
|
|
1090
|
+
audio=run_input.audios,
|
|
1091
|
+
images=run_input.images,
|
|
1092
|
+
videos=run_input.videos,
|
|
1093
|
+
files=run_input.files,
|
|
1094
|
+
add_history_to_context=add_history_to_context,
|
|
1095
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1096
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1097
|
+
tools=_tools,
|
|
1098
|
+
**kwargs,
|
|
1099
|
+
)
|
|
1100
|
+
if len(run_messages.messages) == 0:
|
|
1101
|
+
log_error("No messages to be sent to the model.")
|
|
1098
1102
|
|
|
1099
|
-
|
|
1103
|
+
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
1100
1104
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1105
|
+
# Start memory creation on a separate thread (runs concurrently with the main execution loop)
|
|
1106
|
+
memory_future = None
|
|
1107
|
+
# 4. Start memory creation in background thread if memory manager is enabled and agentic memory is disabled
|
|
1108
|
+
if (
|
|
1109
|
+
run_messages.user_message is not None
|
|
1110
|
+
and self.memory_manager is not None
|
|
1111
|
+
and self.enable_user_memories
|
|
1112
|
+
and not self.enable_agentic_memory
|
|
1113
|
+
):
|
|
1114
|
+
log_debug("Starting memory creation in background thread.")
|
|
1115
|
+
memory_future = self.background_executor.submit(
|
|
1116
|
+
self._make_memories, run_messages=run_messages, user_id=user_id
|
|
1117
|
+
)
|
|
1114
1118
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
)
|
|
1119
|
+
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
1120
|
+
if (
|
|
1121
|
+
run_messages.user_message is not None
|
|
1122
|
+
and self.culture_manager is not None
|
|
1123
|
+
and self.update_cultural_knowledge
|
|
1124
|
+
):
|
|
1125
|
+
log_debug("Starting cultural knowledge creation in background thread.")
|
|
1126
|
+
cultural_knowledge_future = self.background_executor.submit(
|
|
1127
|
+
self._make_cultural_knowledge, run_messages=run_messages
|
|
1128
|
+
)
|
|
1126
1129
|
|
|
1127
|
-
try:
|
|
1128
1130
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1129
1131
|
|
|
1130
1132
|
# 5. Reason about the task
|
|
@@ -1215,18 +1217,6 @@ class Agent:
|
|
|
1215
1217
|
|
|
1216
1218
|
log_debug(f"Agent Run End: {run_response.run_id}", center=True, symbol="*")
|
|
1217
1219
|
|
|
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
1220
|
return run_response
|
|
1231
1221
|
finally:
|
|
1232
1222
|
# Always disconnect connectable tools
|
|
@@ -1264,91 +1254,92 @@ class Agent:
|
|
|
1264
1254
|
9. Create session summary
|
|
1265
1255
|
10. Cleanup and store the run response and session
|
|
1266
1256
|
"""
|
|
1257
|
+
memory_future = None
|
|
1258
|
+
cultural_knowledge_future = None
|
|
1267
1259
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1260
|
+
try:
|
|
1261
|
+
# 1. Execute pre-hooks
|
|
1262
|
+
run_input = cast(RunInput, run_response.input)
|
|
1263
|
+
self.model = cast(Model, self.model)
|
|
1264
|
+
if self.pre_hooks is not None:
|
|
1265
|
+
# Can modify the run input
|
|
1266
|
+
pre_hook_iterator = self._execute_pre_hooks(
|
|
1267
|
+
hooks=self.pre_hooks, # type: ignore
|
|
1268
|
+
run_response=run_response,
|
|
1269
|
+
run_input=run_input,
|
|
1270
|
+
run_context=run_context,
|
|
1271
|
+
session=session,
|
|
1272
|
+
user_id=user_id,
|
|
1273
|
+
debug_mode=debug_mode,
|
|
1274
|
+
stream_events=stream_events,
|
|
1275
|
+
background_tasks=background_tasks,
|
|
1276
|
+
**kwargs,
|
|
1277
|
+
)
|
|
1278
|
+
for event in pre_hook_iterator:
|
|
1279
|
+
yield event
|
|
1280
|
+
|
|
1281
|
+
# 2. Determine tools for model
|
|
1282
|
+
processed_tools = self.get_tools(
|
|
1275
1283
|
run_response=run_response,
|
|
1276
|
-
run_input=run_input,
|
|
1277
1284
|
run_context=run_context,
|
|
1278
1285
|
session=session,
|
|
1279
1286
|
user_id=user_id,
|
|
1280
|
-
debug_mode=debug_mode,
|
|
1281
|
-
stream_events=stream_events,
|
|
1282
|
-
background_tasks=background_tasks,
|
|
1283
|
-
**kwargs,
|
|
1284
1287
|
)
|
|
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
|
-
)
|
|
1288
|
+
_tools = self._determine_tools_for_model(
|
|
1289
|
+
model=self.model,
|
|
1290
|
+
processed_tools=processed_tools,
|
|
1291
|
+
run_response=run_response,
|
|
1292
|
+
session=session,
|
|
1293
|
+
run_context=run_context,
|
|
1294
|
+
)
|
|
1302
1295
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1296
|
+
# 3. Prepare run messages
|
|
1297
|
+
run_messages: RunMessages = self._get_run_messages(
|
|
1298
|
+
run_response=run_response,
|
|
1299
|
+
input=run_input.input_content,
|
|
1300
|
+
session=session,
|
|
1301
|
+
run_context=run_context,
|
|
1302
|
+
user_id=user_id,
|
|
1303
|
+
audio=run_input.audios,
|
|
1304
|
+
images=run_input.images,
|
|
1305
|
+
videos=run_input.videos,
|
|
1306
|
+
files=run_input.files,
|
|
1307
|
+
add_history_to_context=add_history_to_context,
|
|
1308
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1309
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1310
|
+
tools=_tools,
|
|
1311
|
+
**kwargs,
|
|
1312
|
+
)
|
|
1313
|
+
if len(run_messages.messages) == 0:
|
|
1314
|
+
log_error("No messages to be sent to the model.")
|
|
1322
1315
|
|
|
1323
|
-
|
|
1316
|
+
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
1324
1317
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1318
|
+
# Start memory creation on a separate thread (runs concurrently with the main execution loop)
|
|
1319
|
+
memory_future = None
|
|
1320
|
+
# 4. Start memory creation in background thread if memory manager is enabled and agentic memory is disabled
|
|
1321
|
+
if (
|
|
1322
|
+
run_messages.user_message is not None
|
|
1323
|
+
and self.memory_manager is not None
|
|
1324
|
+
and self.enable_user_memories
|
|
1325
|
+
and not self.enable_agentic_memory
|
|
1326
|
+
):
|
|
1327
|
+
log_debug("Starting memory creation in background thread.")
|
|
1328
|
+
memory_future = self.background_executor.submit(
|
|
1329
|
+
self._make_memories, run_messages=run_messages, user_id=user_id
|
|
1330
|
+
)
|
|
1338
1331
|
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
)
|
|
1332
|
+
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
1333
|
+
if (
|
|
1334
|
+
run_messages.user_message is not None
|
|
1335
|
+
and self.culture_manager is not None
|
|
1336
|
+
and self.update_cultural_knowledge
|
|
1337
|
+
):
|
|
1338
|
+
log_debug("Starting cultural knowledge creation in background thread.")
|
|
1339
|
+
cultural_knowledge_future = self.background_executor.submit(
|
|
1340
|
+
self._make_cultural_knowledge, run_messages=run_messages
|
|
1341
|
+
)
|
|
1350
1342
|
|
|
1351
|
-
try:
|
|
1352
1343
|
# Start the Run by yielding a RunStarted event
|
|
1353
1344
|
if stream_events:
|
|
1354
1345
|
yield handle_event( # type: ignore
|
|
@@ -1531,28 +1522,6 @@ class Agent:
|
|
|
1531
1522
|
self._log_agent_telemetry(session_id=session.session_id, run_id=run_response.run_id)
|
|
1532
1523
|
|
|
1533
1524
|
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
1525
|
finally:
|
|
1557
1526
|
# Always disconnect connectable tools
|
|
1558
1527
|
self._disconnect_connectable_tools()
|
|
@@ -1582,7 +1551,7 @@ class Agent:
|
|
|
1582
1551
|
add_session_state_to_context: Optional[bool] = None,
|
|
1583
1552
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1584
1553
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1585
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
1554
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
1586
1555
|
debug_mode: Optional[bool] = None,
|
|
1587
1556
|
**kwargs: Any,
|
|
1588
1557
|
) -> RunOutput: ...
|
|
@@ -1610,7 +1579,7 @@ class Agent:
|
|
|
1610
1579
|
add_session_state_to_context: Optional[bool] = None,
|
|
1611
1580
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1612
1581
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1613
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
1582
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
1614
1583
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
1615
1584
|
yield_run_output: bool = False,
|
|
1616
1585
|
debug_mode: Optional[bool] = None,
|
|
@@ -1639,7 +1608,7 @@ class Agent:
|
|
|
1639
1608
|
add_session_state_to_context: Optional[bool] = None,
|
|
1640
1609
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1641
1610
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1642
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
1611
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
1643
1612
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
1644
1613
|
yield_run_output: Optional[bool] = None,
|
|
1645
1614
|
debug_mode: Optional[bool] = None,
|
|
@@ -1651,9 +1620,10 @@ class Agent:
|
|
|
1651
1620
|
"`run` method is not supported with an async database. Please use `arun` method instead."
|
|
1652
1621
|
)
|
|
1653
1622
|
|
|
1654
|
-
#
|
|
1623
|
+
# Initialize session early for error handling
|
|
1624
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
1625
|
+
# Set the id for the run
|
|
1655
1626
|
run_id = run_id or str(uuid4())
|
|
1656
|
-
register_run(run_id)
|
|
1657
1627
|
|
|
1658
1628
|
if (add_history_to_context or self.add_history_to_context) and not self.db and not self.team_id:
|
|
1659
1629
|
log_warning(
|
|
@@ -1667,82 +1637,84 @@ class Agent:
|
|
|
1667
1637
|
stacklevel=2,
|
|
1668
1638
|
)
|
|
1669
1639
|
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1640
|
+
# Set up retry logic
|
|
1641
|
+
num_attempts = self.retries + 1
|
|
1642
|
+
for attempt in range(num_attempts):
|
|
1643
|
+
if num_attempts > 1:
|
|
1644
|
+
log_debug(f"Retrying Agent run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
1673
1645
|
|
|
1674
|
-
|
|
1646
|
+
try:
|
|
1647
|
+
# Register run for cancellation tracking
|
|
1648
|
+
register_run(run_id)
|
|
1675
1649
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1650
|
+
background_tasks = kwargs.pop("background_tasks", None)
|
|
1651
|
+
if background_tasks is not None:
|
|
1652
|
+
from fastapi import BackgroundTasks
|
|
1678
1653
|
|
|
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
|
|
1654
|
+
background_tasks: BackgroundTasks = background_tasks # type: ignore
|
|
1686
1655
|
|
|
1687
|
-
|
|
1656
|
+
# Validate input against input_schema if provided
|
|
1657
|
+
validated_input = self._validate_input(input)
|
|
1688
1658
|
|
|
1689
|
-
|
|
1690
|
-
|
|
1659
|
+
# Normalise hook & guardails
|
|
1660
|
+
if not self._hooks_normalised:
|
|
1661
|
+
if self.pre_hooks:
|
|
1662
|
+
self.pre_hooks = normalize_pre_hooks(self.pre_hooks) # type: ignore
|
|
1663
|
+
if self.post_hooks:
|
|
1664
|
+
self.post_hooks = normalize_post_hooks(self.post_hooks) # type: ignore
|
|
1665
|
+
self._hooks_normalised = True
|
|
1691
1666
|
|
|
1692
|
-
|
|
1693
|
-
images=images, videos=videos, audios=audio, files=files
|
|
1694
|
-
)
|
|
1667
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
1695
1668
|
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
input_content=validated_input,
|
|
1699
|
-
images=image_artifacts,
|
|
1700
|
-
videos=video_artifacts,
|
|
1701
|
-
audios=audio_artifacts,
|
|
1702
|
-
files=file_artifacts,
|
|
1703
|
-
)
|
|
1669
|
+
# Initialize the Agent
|
|
1670
|
+
self.initialize_agent(debug_mode=debug_mode)
|
|
1704
1671
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1672
|
+
image_artifacts, video_artifacts, audio_artifacts, file_artifacts = validate_media_object_id(
|
|
1673
|
+
images=images, videos=videos, audios=audio, files=files
|
|
1674
|
+
)
|
|
1708
1675
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
session_state = self._load_session_state(session=agent_session, session_state=session_state)
|
|
1676
|
+
# Create RunInput to capture the original user input
|
|
1677
|
+
run_input = RunInput(
|
|
1678
|
+
input_content=validated_input,
|
|
1679
|
+
images=image_artifacts,
|
|
1680
|
+
videos=video_artifacts,
|
|
1681
|
+
audios=audio_artifacts,
|
|
1682
|
+
files=file_artifacts,
|
|
1683
|
+
)
|
|
1718
1684
|
|
|
1719
|
-
|
|
1720
|
-
|
|
1685
|
+
# Read existing session from database
|
|
1686
|
+
agent_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
1687
|
+
self._update_metadata(session=agent_session)
|
|
1721
1688
|
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1689
|
+
# Initialize session state
|
|
1690
|
+
session_state = self._initialize_session_state(
|
|
1691
|
+
session_state=session_state if session_state is not None else {},
|
|
1692
|
+
user_id=user_id,
|
|
1693
|
+
session_id=session_id,
|
|
1694
|
+
run_id=run_id,
|
|
1695
|
+
)
|
|
1696
|
+
# Update session state from DB
|
|
1697
|
+
session_state = self._load_session_state(session=agent_session, session_state=session_state)
|
|
1725
1698
|
|
|
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
|
|
1699
|
+
# Determine runtime dependencies
|
|
1700
|
+
dependencies = dependencies if dependencies is not None else self.dependencies
|
|
1737
1701
|
|
|
1738
|
-
|
|
1739
|
-
|
|
1702
|
+
# Resolve output_schema parameter takes precedence, then fall back to self.output_schema
|
|
1703
|
+
if output_schema is None:
|
|
1704
|
+
output_schema = self.output_schema
|
|
1740
1705
|
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1706
|
+
# Initialize run context
|
|
1707
|
+
run_context = run_context or RunContext(
|
|
1708
|
+
run_id=run_id,
|
|
1709
|
+
session_id=session_id,
|
|
1710
|
+
user_id=user_id,
|
|
1711
|
+
session_state=session_state,
|
|
1712
|
+
dependencies=dependencies,
|
|
1713
|
+
output_schema=output_schema,
|
|
1714
|
+
)
|
|
1715
|
+
# output_schema parameter takes priority, even if run_context was provided
|
|
1716
|
+
run_context.output_schema = output_schema
|
|
1744
1717
|
|
|
1745
|
-
try:
|
|
1746
1718
|
# Resolve dependencies
|
|
1747
1719
|
if run_context.dependencies is not None:
|
|
1748
1720
|
self._resolve_run_dependencies(run_context=run_context)
|
|
@@ -1849,23 +1821,64 @@ class Agent:
|
|
|
1849
1821
|
)
|
|
1850
1822
|
return response
|
|
1851
1823
|
except (InputCheckError, OutputCheckError) as e:
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1824
|
+
# Handle exceptions during streaming
|
|
1825
|
+
run_response.status = RunStatus.error
|
|
1826
|
+
# Add error event to list of events
|
|
1827
|
+
run_error = create_run_error_event(
|
|
1828
|
+
run_response,
|
|
1829
|
+
error=str(e),
|
|
1830
|
+
error_id=e.error_id,
|
|
1831
|
+
error_type=e.type,
|
|
1832
|
+
additional_data=e.additional_data,
|
|
1833
|
+
)
|
|
1834
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
1835
|
+
|
|
1836
|
+
# If the content is None, set it to the error message
|
|
1837
|
+
if run_response.content is None:
|
|
1838
|
+
run_response.content = str(e)
|
|
1839
|
+
|
|
1840
|
+
log_error(f"Validation failed: {str(e)} | Check trigger: {e.check_trigger}")
|
|
1841
|
+
|
|
1842
|
+
self._cleanup_and_store(
|
|
1843
|
+
run_response=run_response, session=agent_session, run_context=run_context, user_id=user_id
|
|
1844
|
+
)
|
|
1845
|
+
|
|
1846
|
+
if stream:
|
|
1847
|
+
return generator_wrapper(run_error) # type: ignore
|
|
1848
|
+
else:
|
|
1849
|
+
return run_response
|
|
1850
|
+
except RunCancelledException as e:
|
|
1851
|
+
# Handle run cancellation during streaming
|
|
1852
|
+
log_info(f"Run {run_response.run_id} was cancelled during streaming")
|
|
1853
|
+
run_response.content = str(e)
|
|
1856
1854
|
run_response.status = RunStatus.cancelled
|
|
1855
|
+
cancelled_run_error = handle_event(
|
|
1856
|
+
create_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
1857
|
+
run_response,
|
|
1858
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
1859
|
+
store_events=self.store_events,
|
|
1860
|
+
)
|
|
1861
|
+
|
|
1862
|
+
# Cleanup and store the run response and session
|
|
1863
|
+
self._cleanup_and_store(
|
|
1864
|
+
run_response=run_response, session=agent_session, run_context=run_context, user_id=user_id
|
|
1865
|
+
)
|
|
1857
1866
|
|
|
1867
|
+
if stream:
|
|
1868
|
+
return generator_wrapper(cancelled_run_error) # type: ignore
|
|
1869
|
+
else:
|
|
1870
|
+
return run_response
|
|
1871
|
+
except KeyboardInterrupt:
|
|
1858
1872
|
if stream:
|
|
1859
1873
|
return generator_wrapper( # type: ignore
|
|
1860
|
-
create_run_cancelled_event(
|
|
1861
|
-
from_run_response=run_response,
|
|
1862
|
-
reason="Operation cancelled by user",
|
|
1863
|
-
)
|
|
1874
|
+
create_run_cancelled_event(run_response, "Operation cancelled by user") # type: ignore
|
|
1864
1875
|
)
|
|
1865
1876
|
else:
|
|
1866
|
-
|
|
1877
|
+
run_response.content = "Operation cancelled by user" # type: ignore
|
|
1878
|
+
run_response.status = RunStatus.cancelled # type: ignore
|
|
1879
|
+
return run_response # type: ignore
|
|
1880
|
+
|
|
1867
1881
|
except Exception as e:
|
|
1868
|
-
# Check if this is the last attempt
|
|
1869
1882
|
if attempt < num_attempts - 1:
|
|
1870
1883
|
# Calculate delay with exponential backoff if enabled
|
|
1871
1884
|
if self.exponential_backoff:
|
|
@@ -1876,12 +1889,27 @@ class Agent:
|
|
|
1876
1889
|
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
1877
1890
|
time.sleep(delay)
|
|
1878
1891
|
continue
|
|
1892
|
+
|
|
1893
|
+
run_response.status = RunStatus.error
|
|
1894
|
+
# Add error event to list of events
|
|
1895
|
+
run_error = create_run_error_event(run_response, error=str(e))
|
|
1896
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
1897
|
+
|
|
1898
|
+
# If the content is None, set it to the error message
|
|
1899
|
+
if run_response.content is None:
|
|
1900
|
+
run_response.content = str(e)
|
|
1901
|
+
|
|
1902
|
+
log_error(f"Error in Agent run: {str(e)}")
|
|
1903
|
+
|
|
1904
|
+
# Cleanup and store the run response and session
|
|
1905
|
+
self._cleanup_and_store(
|
|
1906
|
+
run_response=run_response, session=agent_session, run_context=run_context, user_id=user_id
|
|
1907
|
+
)
|
|
1908
|
+
|
|
1909
|
+
if stream:
|
|
1910
|
+
return generator_wrapper(run_error) # type: ignore
|
|
1879
1911
|
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
|
|
1912
|
+
return run_response
|
|
1885
1913
|
|
|
1886
1914
|
# If we get here, all retries failed (shouldn't happen with current logic)
|
|
1887
1915
|
raise Exception(f"Failed after {num_attempts} attempts.")
|
|
@@ -1922,246 +1950,321 @@ class Agent:
|
|
|
1922
1950
|
"""
|
|
1923
1951
|
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
1924
1952
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1953
|
+
cultural_knowledge_task = None
|
|
1954
|
+
memory_task = None
|
|
1927
1955
|
|
|
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
|
-
)
|
|
1956
|
+
# Set up retry logic
|
|
1957
|
+
num_attempts = self.retries + 1
|
|
1942
1958
|
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1959
|
+
for attempt in range(num_attempts):
|
|
1960
|
+
if num_attempts > 1:
|
|
1961
|
+
log_debug(f"Retrying Agent run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
1946
1962
|
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
pass
|
|
1963
|
+
try:
|
|
1964
|
+
# 1. Read or create session. Reads from the database if provided.
|
|
1965
|
+
agent_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
1966
|
+
|
|
1967
|
+
# 2. Update metadata and session state
|
|
1968
|
+
self._update_metadata(session=agent_session)
|
|
1969
|
+
# Initialize session state
|
|
1970
|
+
run_context.session_state = self._initialize_session_state(
|
|
1971
|
+
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
1972
|
+
user_id=user_id,
|
|
1973
|
+
session_id=session_id,
|
|
1974
|
+
run_id=run_response.run_id,
|
|
1975
|
+
)
|
|
1976
|
+
# Update session state from DB
|
|
1977
|
+
if run_context.session_state is not None:
|
|
1978
|
+
run_context.session_state = self._load_session_state(
|
|
1979
|
+
session=agent_session, session_state=run_context.session_state
|
|
1980
|
+
)
|
|
1966
1981
|
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
run_response=run_response,
|
|
1971
|
-
run_context=run_context,
|
|
1972
|
-
session=agent_session,
|
|
1973
|
-
user_id=user_id,
|
|
1974
|
-
)
|
|
1982
|
+
# 3. Resolve dependencies
|
|
1983
|
+
if run_context.dependencies is not None:
|
|
1984
|
+
await self._aresolve_run_dependencies(run_context=run_context)
|
|
1975
1985
|
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1986
|
+
# 4. Execute pre-hooks
|
|
1987
|
+
run_input = cast(RunInput, run_response.input)
|
|
1988
|
+
self.model = cast(Model, self.model)
|
|
1989
|
+
if self.pre_hooks is not None:
|
|
1990
|
+
# Can modify the run input
|
|
1991
|
+
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
1992
|
+
hooks=self.pre_hooks, # type: ignore
|
|
1993
|
+
run_response=run_response,
|
|
1994
|
+
run_context=run_context,
|
|
1995
|
+
run_input=run_input,
|
|
1996
|
+
session=agent_session,
|
|
1997
|
+
user_id=user_id,
|
|
1998
|
+
debug_mode=debug_mode,
|
|
1999
|
+
background_tasks=background_tasks,
|
|
2000
|
+
**kwargs,
|
|
2001
|
+
)
|
|
2002
|
+
# Consume the async iterator without yielding
|
|
2003
|
+
async for _ in pre_hook_iterator:
|
|
2004
|
+
pass
|
|
1983
2005
|
|
|
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.")
|
|
2006
|
+
# 5. Determine tools for model
|
|
2007
|
+
self.model = cast(Model, self.model)
|
|
2008
|
+
processed_tools = await self.aget_tools(
|
|
2009
|
+
run_response=run_response,
|
|
2010
|
+
run_context=run_context,
|
|
2011
|
+
session=agent_session,
|
|
2012
|
+
user_id=user_id,
|
|
2013
|
+
)
|
|
2003
2014
|
|
|
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))
|
|
2015
|
+
_tools = self._determine_tools_for_model(
|
|
2016
|
+
model=self.model,
|
|
2017
|
+
processed_tools=processed_tools,
|
|
2018
|
+
run_response=run_response,
|
|
2019
|
+
run_context=run_context,
|
|
2020
|
+
session=agent_session,
|
|
2021
|
+
)
|
|
2014
2022
|
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2023
|
+
# 6. Prepare run messages
|
|
2024
|
+
run_messages: RunMessages = await self._aget_run_messages(
|
|
2025
|
+
run_response=run_response,
|
|
2026
|
+
run_context=run_context,
|
|
2027
|
+
input=run_input.input_content,
|
|
2028
|
+
session=agent_session,
|
|
2029
|
+
user_id=user_id,
|
|
2030
|
+
audio=run_input.audios,
|
|
2031
|
+
images=run_input.images,
|
|
2032
|
+
videos=run_input.videos,
|
|
2033
|
+
files=run_input.files,
|
|
2034
|
+
add_history_to_context=add_history_to_context,
|
|
2035
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2036
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2037
|
+
tools=_tools,
|
|
2038
|
+
**kwargs,
|
|
2039
|
+
)
|
|
2040
|
+
if len(run_messages.messages) == 0:
|
|
2041
|
+
log_error("No messages to be sent to the model.")
|
|
2024
2042
|
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2043
|
+
# 7. Start memory creation as a background task (runs concurrently with the main execution)
|
|
2044
|
+
memory_task = None
|
|
2045
|
+
if (
|
|
2046
|
+
run_messages.user_message is not None
|
|
2047
|
+
and self.memory_manager is not None
|
|
2048
|
+
and self.enable_user_memories
|
|
2049
|
+
and not self.enable_agentic_memory
|
|
2050
|
+
):
|
|
2051
|
+
log_debug("Starting memory creation in background task.")
|
|
2052
|
+
memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2028
2053
|
|
|
2029
|
-
|
|
2030
|
-
|
|
2054
|
+
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
2055
|
+
if (
|
|
2056
|
+
run_messages.user_message is not None
|
|
2057
|
+
and self.culture_manager is not None
|
|
2058
|
+
and self.update_cultural_knowledge
|
|
2059
|
+
):
|
|
2060
|
+
log_debug("Starting cultural knowledge creation in background thread.")
|
|
2061
|
+
cultural_knowledge_task = create_task(self._acreate_cultural_knowledge(run_messages=run_messages))
|
|
2031
2062
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2063
|
+
# Check for cancellation before model call
|
|
2064
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2034
2065
|
|
|
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
|
-
)
|
|
2066
|
+
# 8. Reason about the task if reasoning is enabled
|
|
2067
|
+
await self._ahandle_reasoning(run_response=run_response, run_messages=run_messages)
|
|
2046
2068
|
|
|
2047
|
-
|
|
2048
|
-
|
|
2069
|
+
# Check for cancellation before model call
|
|
2070
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2049
2071
|
|
|
2050
|
-
|
|
2051
|
-
|
|
2072
|
+
# 9. Generate a response from the Model (includes running function calls)
|
|
2073
|
+
model_response: ModelResponse = await self.model.aresponse(
|
|
2074
|
+
messages=run_messages.messages,
|
|
2075
|
+
tools=_tools,
|
|
2076
|
+
tool_choice=self.tool_choice,
|
|
2077
|
+
tool_call_limit=self.tool_call_limit,
|
|
2078
|
+
response_format=response_format,
|
|
2079
|
+
send_media_to_model=self.send_media_to_model,
|
|
2080
|
+
run_response=run_response,
|
|
2081
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
2082
|
+
)
|
|
2052
2083
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
model_response=model_response, run_messages=run_messages, run_context=run_context
|
|
2056
|
-
)
|
|
2084
|
+
# Check for cancellation after model call
|
|
2085
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2057
2086
|
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
run_messages=run_messages,
|
|
2063
|
-
run_context=run_context,
|
|
2064
|
-
)
|
|
2087
|
+
# If an output model is provided, generate output using the output model
|
|
2088
|
+
await self._agenerate_response_with_output_model(
|
|
2089
|
+
model_response=model_response, run_messages=run_messages
|
|
2090
|
+
)
|
|
2065
2091
|
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
return await self._ahandle_agent_run_paused(
|
|
2070
|
-
run_response=run_response, session=agent_session, user_id=user_id
|
|
2092
|
+
# If a parser model is provided, structure the response separately
|
|
2093
|
+
await self._aparse_response_with_parser_model(
|
|
2094
|
+
model_response=model_response, run_messages=run_messages, run_context=run_context
|
|
2071
2095
|
)
|
|
2072
2096
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2097
|
+
# 10. Update the RunOutput with the model response
|
|
2098
|
+
self._update_run_response(
|
|
2099
|
+
model_response=model_response,
|
|
2100
|
+
run_response=run_response,
|
|
2101
|
+
run_messages=run_messages,
|
|
2102
|
+
run_context=run_context,
|
|
2103
|
+
)
|
|
2075
2104
|
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2105
|
+
# We should break out of the run function
|
|
2106
|
+
if any(tool_call.is_paused for tool_call in run_response.tools or []):
|
|
2107
|
+
await await_for_open_threads(
|
|
2108
|
+
memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task
|
|
2109
|
+
)
|
|
2110
|
+
return await self._ahandle_agent_run_paused(
|
|
2111
|
+
run_response=run_response, session=agent_session, user_id=user_id
|
|
2112
|
+
)
|
|
2079
2113
|
|
|
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
|
|
2114
|
+
# 11. Convert the response to the structured format if needed
|
|
2115
|
+
self._convert_response_to_structured_format(run_response, run_context=run_context)
|
|
2093
2116
|
|
|
2094
|
-
|
|
2095
|
-
|
|
2117
|
+
# 12. Store media if enabled
|
|
2118
|
+
if self.store_media:
|
|
2119
|
+
store_media_util(run_response, model_response)
|
|
2096
2120
|
|
|
2097
|
-
|
|
2098
|
-
|
|
2121
|
+
# 13. Execute post-hooks (after output is generated but before response is returned)
|
|
2122
|
+
if self.post_hooks is not None:
|
|
2123
|
+
async for _ in self._aexecute_post_hooks(
|
|
2124
|
+
hooks=self.post_hooks, # type: ignore
|
|
2125
|
+
run_output=run_response,
|
|
2126
|
+
run_context=run_context,
|
|
2127
|
+
session=agent_session,
|
|
2128
|
+
user_id=user_id,
|
|
2129
|
+
debug_mode=debug_mode,
|
|
2130
|
+
background_tasks=background_tasks,
|
|
2131
|
+
**kwargs,
|
|
2132
|
+
):
|
|
2133
|
+
pass
|
|
2099
2134
|
|
|
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)}")
|
|
2135
|
+
# Check for cancellation
|
|
2136
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2108
2137
|
|
|
2109
|
-
|
|
2138
|
+
# 14. Wait for background memory creation
|
|
2139
|
+
await await_for_open_threads(memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task)
|
|
2110
2140
|
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2141
|
+
# 15. Create session summary
|
|
2142
|
+
if self.session_summary_manager is not None and self.enable_session_summaries:
|
|
2143
|
+
# Upsert the RunOutput to Agent Session before creating the session summary
|
|
2144
|
+
agent_session.upsert_run(run=run_response)
|
|
2145
|
+
try:
|
|
2146
|
+
await self.session_summary_manager.acreate_session_summary(session=agent_session)
|
|
2147
|
+
except Exception as e:
|
|
2148
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2118
2149
|
|
|
2119
|
-
|
|
2120
|
-
await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
|
|
2150
|
+
run_response.status = RunStatus.completed
|
|
2121
2151
|
|
|
2122
|
-
|
|
2152
|
+
# 16. Cleanup and store the run response and session
|
|
2153
|
+
await self._acleanup_and_store(
|
|
2154
|
+
run_response=run_response,
|
|
2155
|
+
session=agent_session,
|
|
2156
|
+
run_context=run_context,
|
|
2157
|
+
user_id=user_id,
|
|
2158
|
+
)
|
|
2123
2159
|
|
|
2124
|
-
|
|
2160
|
+
# Log Agent Telemetry
|
|
2161
|
+
await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
|
|
2125
2162
|
|
|
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
|
|
2163
|
+
log_debug(f"Agent Run End: {run_response.run_id}", center=True, symbol="*")
|
|
2131
2164
|
|
|
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
|
-
)
|
|
2165
|
+
return run_response
|
|
2139
2166
|
|
|
2140
|
-
|
|
2167
|
+
except RunCancelledException as e:
|
|
2168
|
+
# Handle run cancellation
|
|
2169
|
+
log_info(f"Run {run_response.run_id} was cancelled")
|
|
2170
|
+
run_response.content = str(e)
|
|
2171
|
+
run_response.status = RunStatus.cancelled
|
|
2141
2172
|
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2173
|
+
# Cleanup and store the run response and session
|
|
2174
|
+
await self._acleanup_and_store(
|
|
2175
|
+
run_response=run_response,
|
|
2176
|
+
session=agent_session,
|
|
2177
|
+
run_context=run_context,
|
|
2178
|
+
user_id=user_id,
|
|
2179
|
+
)
|
|
2147
2180
|
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
pass
|
|
2181
|
+
return run_response
|
|
2182
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2183
|
+
# Handle exceptions during streaming
|
|
2184
|
+
run_response.status = RunStatus.error
|
|
2185
|
+
# Add error event to list of events
|
|
2186
|
+
run_error = create_run_error_event(
|
|
2187
|
+
run_response,
|
|
2188
|
+
error=str(e),
|
|
2189
|
+
error_id=e.error_id,
|
|
2190
|
+
error_type=e.type,
|
|
2191
|
+
additional_data=e.additional_data,
|
|
2192
|
+
)
|
|
2193
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
2162
2194
|
|
|
2163
|
-
|
|
2164
|
-
|
|
2195
|
+
# If the content is None, set it to the error message
|
|
2196
|
+
if run_response.content is None:
|
|
2197
|
+
run_response.content = str(e)
|
|
2198
|
+
|
|
2199
|
+
log_error(f"Validation failed: {str(e)} | Check trigger: {e.check_trigger}")
|
|
2200
|
+
|
|
2201
|
+
await self._acleanup_and_store(
|
|
2202
|
+
run_response=run_response,
|
|
2203
|
+
session=agent_session,
|
|
2204
|
+
run_context=run_context,
|
|
2205
|
+
user_id=user_id,
|
|
2206
|
+
)
|
|
2207
|
+
|
|
2208
|
+
return run_response
|
|
2209
|
+
except Exception as e:
|
|
2210
|
+
# Check if this is the last attempt
|
|
2211
|
+
if attempt < num_attempts - 1:
|
|
2212
|
+
# Calculate delay with exponential backoff if enabled
|
|
2213
|
+
if self.exponential_backoff:
|
|
2214
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2215
|
+
else:
|
|
2216
|
+
delay = self.delay_between_retries
|
|
2217
|
+
|
|
2218
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2219
|
+
time.sleep(delay)
|
|
2220
|
+
continue
|
|
2221
|
+
|
|
2222
|
+
run_response.status = RunStatus.error
|
|
2223
|
+
# Add error event to list of events
|
|
2224
|
+
run_error = create_run_error_event(run_response, error=str(e))
|
|
2225
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
2226
|
+
|
|
2227
|
+
# If the content is None, set it to the error message
|
|
2228
|
+
if run_response.content is None:
|
|
2229
|
+
run_response.content = str(e)
|
|
2230
|
+
|
|
2231
|
+
log_error(f"Error in Agent run: {str(e)}")
|
|
2232
|
+
|
|
2233
|
+
# Cleanup and store the run response and session
|
|
2234
|
+
await self._acleanup_and_store(
|
|
2235
|
+
run_response=run_response,
|
|
2236
|
+
session=agent_session,
|
|
2237
|
+
run_context=run_context,
|
|
2238
|
+
user_id=user_id,
|
|
2239
|
+
)
|
|
2240
|
+
|
|
2241
|
+
return run_response
|
|
2242
|
+
|
|
2243
|
+
finally:
|
|
2244
|
+
# Always disconnect connectable tools
|
|
2245
|
+
self._disconnect_connectable_tools()
|
|
2246
|
+
# Always disconnect MCP tools
|
|
2247
|
+
await self._disconnect_mcp_tools()
|
|
2248
|
+
|
|
2249
|
+
# Cancel the memory task if it's still running
|
|
2250
|
+
if memory_task is not None and not memory_task.done():
|
|
2251
|
+
memory_task.cancel()
|
|
2252
|
+
try:
|
|
2253
|
+
await memory_task
|
|
2254
|
+
except CancelledError:
|
|
2255
|
+
pass
|
|
2256
|
+
# Cancel the cultural knowledge task if it's still running
|
|
2257
|
+
if cultural_knowledge_task is not None and not cultural_knowledge_task.done():
|
|
2258
|
+
cultural_knowledge_task.cancel()
|
|
2259
|
+
try:
|
|
2260
|
+
await cultural_knowledge_task
|
|
2261
|
+
except CancelledError:
|
|
2262
|
+
pass
|
|
2263
|
+
|
|
2264
|
+
# Always clean up the run tracking
|
|
2265
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
2266
|
+
|
|
2267
|
+
return run_response
|
|
2165
2268
|
|
|
2166
2269
|
async def _arun_stream(
|
|
2167
2270
|
self,
|
|
@@ -2198,344 +2301,422 @@ class Agent:
|
|
|
2198
2301
|
"""
|
|
2199
2302
|
log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
|
|
2200
2303
|
|
|
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
|
-
)
|
|
2304
|
+
memory_task = None
|
|
2305
|
+
cultural_knowledge_task = None
|
|
2209
2306
|
|
|
2210
2307
|
# 1. Read or create session. Reads from the database if provided.
|
|
2211
2308
|
agent_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
2212
2309
|
|
|
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
|
-
)
|
|
2227
|
-
|
|
2228
|
-
# 3. Resolve dependencies
|
|
2229
|
-
if run_context.dependencies is not None:
|
|
2230
|
-
await self._aresolve_run_dependencies(run_context=run_context)
|
|
2231
|
-
|
|
2232
|
-
# 4. Execute pre-hooks
|
|
2233
|
-
run_input = cast(RunInput, run_response.input)
|
|
2234
|
-
self.model = cast(Model, self.model)
|
|
2235
|
-
if self.pre_hooks is not None:
|
|
2236
|
-
# Can modify the run input
|
|
2237
|
-
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
2238
|
-
hooks=self.pre_hooks, # type: ignore
|
|
2239
|
-
run_response=run_response,
|
|
2240
|
-
run_context=run_context,
|
|
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
|
|
2251
|
-
|
|
2252
|
-
# 5. Determine tools for model
|
|
2253
|
-
self.model = cast(Model, self.model)
|
|
2254
|
-
processed_tools = await self.aget_tools(
|
|
2255
|
-
run_response=run_response,
|
|
2256
|
-
run_context=run_context,
|
|
2257
|
-
session=agent_session,
|
|
2258
|
-
user_id=user_id,
|
|
2259
|
-
)
|
|
2310
|
+
# Set up retry logic
|
|
2311
|
+
num_attempts = self.retries + 1
|
|
2260
2312
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
run_response=run_response,
|
|
2265
|
-
run_context=run_context,
|
|
2266
|
-
session=agent_session,
|
|
2267
|
-
)
|
|
2313
|
+
for attempt in range(num_attempts):
|
|
2314
|
+
if num_attempts > 1:
|
|
2315
|
+
log_debug(f"Retrying Agent run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2268
2316
|
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
videos=run_input.videos,
|
|
2279
|
-
files=run_input.files,
|
|
2280
|
-
add_history_to_context=add_history_to_context,
|
|
2281
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
2282
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
2283
|
-
tools=_tools,
|
|
2284
|
-
**kwargs,
|
|
2285
|
-
)
|
|
2286
|
-
if len(run_messages.messages) == 0:
|
|
2287
|
-
log_error("No messages to be sent to the model.")
|
|
2317
|
+
try:
|
|
2318
|
+
# Start the Run by yielding a RunStarted event
|
|
2319
|
+
if stream_events:
|
|
2320
|
+
yield handle_event( # type: ignore
|
|
2321
|
+
create_run_started_event(run_response),
|
|
2322
|
+
run_response,
|
|
2323
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2324
|
+
store_events=self.store_events,
|
|
2325
|
+
)
|
|
2288
2326
|
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2327
|
+
# 2. Update metadata and session state
|
|
2328
|
+
self._update_metadata(session=agent_session)
|
|
2329
|
+
# Initialize session state
|
|
2330
|
+
run_context.session_state = self._initialize_session_state(
|
|
2331
|
+
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
2332
|
+
user_id=user_id,
|
|
2333
|
+
session_id=session_id,
|
|
2334
|
+
run_id=run_response.run_id,
|
|
2335
|
+
)
|
|
2336
|
+
# Update session state from DB
|
|
2337
|
+
if run_context.session_state is not None:
|
|
2338
|
+
run_context.session_state = self._load_session_state(
|
|
2339
|
+
session=agent_session, session_state=run_context.session_state
|
|
2340
|
+
)
|
|
2299
2341
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
run_messages.user_message is not None
|
|
2304
|
-
and self.culture_manager is not None
|
|
2305
|
-
and self.update_cultural_knowledge
|
|
2306
|
-
):
|
|
2307
|
-
log_debug("Starting cultural knowledge creation in background task.")
|
|
2308
|
-
cultural_knowledge_task = create_task(self._acreate_cultural_knowledge(run_messages=run_messages))
|
|
2342
|
+
# 3. Resolve dependencies
|
|
2343
|
+
if run_context.dependencies is not None:
|
|
2344
|
+
await self._aresolve_run_dependencies(run_context=run_context)
|
|
2309
2345
|
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2346
|
+
# 4. Execute pre-hooks
|
|
2347
|
+
run_input = cast(RunInput, run_response.input)
|
|
2348
|
+
self.model = cast(Model, self.model)
|
|
2349
|
+
if self.pre_hooks is not None:
|
|
2350
|
+
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
2351
|
+
hooks=self.pre_hooks, # type: ignore
|
|
2352
|
+
run_response=run_response,
|
|
2353
|
+
run_context=run_context,
|
|
2354
|
+
run_input=run_input,
|
|
2355
|
+
session=agent_session,
|
|
2356
|
+
user_id=user_id,
|
|
2357
|
+
debug_mode=debug_mode,
|
|
2358
|
+
stream_events=stream_events,
|
|
2359
|
+
background_tasks=background_tasks,
|
|
2360
|
+
**kwargs,
|
|
2361
|
+
)
|
|
2362
|
+
async for event in pre_hook_iterator:
|
|
2363
|
+
yield event
|
|
2319
2364
|
|
|
2320
|
-
|
|
2365
|
+
# 5. Determine tools for model
|
|
2366
|
+
self.model = cast(Model, self.model)
|
|
2367
|
+
processed_tools = await self.aget_tools(
|
|
2368
|
+
run_response=run_response,
|
|
2369
|
+
run_context=run_context,
|
|
2370
|
+
session=agent_session,
|
|
2371
|
+
user_id=user_id,
|
|
2372
|
+
)
|
|
2321
2373
|
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2374
|
+
_tools = self._determine_tools_for_model(
|
|
2375
|
+
model=self.model,
|
|
2376
|
+
processed_tools=processed_tools,
|
|
2377
|
+
run_response=run_response,
|
|
2378
|
+
run_context=run_context,
|
|
2325
2379
|
session=agent_session,
|
|
2380
|
+
)
|
|
2381
|
+
|
|
2382
|
+
# 6. Prepare run messages
|
|
2383
|
+
run_messages: RunMessages = await self._aget_run_messages(
|
|
2326
2384
|
run_response=run_response,
|
|
2327
|
-
run_messages=run_messages,
|
|
2328
|
-
tools=_tools,
|
|
2329
|
-
response_format=response_format,
|
|
2330
|
-
stream_events=stream_events,
|
|
2331
|
-
session_state=run_context.session_state,
|
|
2332
2385
|
run_context=run_context,
|
|
2386
|
+
input=run_input.input_content,
|
|
2387
|
+
session=agent_session,
|
|
2388
|
+
user_id=user_id,
|
|
2389
|
+
audio=run_input.audios,
|
|
2390
|
+
images=run_input.images,
|
|
2391
|
+
videos=run_input.videos,
|
|
2392
|
+
files=run_input.files,
|
|
2393
|
+
add_history_to_context=add_history_to_context,
|
|
2394
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2395
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2396
|
+
tools=_tools,
|
|
2397
|
+
**kwargs,
|
|
2398
|
+
)
|
|
2399
|
+
if len(run_messages.messages) == 0:
|
|
2400
|
+
log_error("No messages to be sent to the model.")
|
|
2401
|
+
|
|
2402
|
+
# 7. Start memory creation as a background task (runs concurrently with the main execution)
|
|
2403
|
+
memory_task = None
|
|
2404
|
+
if (
|
|
2405
|
+
run_messages.user_message is not None
|
|
2406
|
+
and self.memory_manager is not None
|
|
2407
|
+
and self.enable_user_memories
|
|
2408
|
+
and not self.enable_agentic_memory
|
|
2333
2409
|
):
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
else:
|
|
2337
|
-
from agno.run.agent import (
|
|
2338
|
-
IntermediateRunContentEvent,
|
|
2339
|
-
RunContentEvent,
|
|
2340
|
-
) # type: ignore
|
|
2410
|
+
log_debug("Starting memory creation in background task.")
|
|
2411
|
+
memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2341
2412
|
|
|
2342
|
-
|
|
2343
|
-
|
|
2413
|
+
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
2414
|
+
if (
|
|
2415
|
+
run_messages.user_message is not None
|
|
2416
|
+
and self.culture_manager is not None
|
|
2417
|
+
and self.update_cultural_knowledge
|
|
2418
|
+
):
|
|
2419
|
+
log_debug("Starting cultural knowledge creation in background task.")
|
|
2420
|
+
cultural_knowledge_task = create_task(self._acreate_cultural_knowledge(run_messages=run_messages))
|
|
2421
|
+
|
|
2422
|
+
# 8. Reason about the task if reasoning is enabled
|
|
2423
|
+
async for item in self._ahandle_reasoning_stream(
|
|
2344
2424
|
run_response=run_response,
|
|
2345
2425
|
run_messages=run_messages,
|
|
2346
|
-
tools=_tools,
|
|
2347
|
-
response_format=response_format,
|
|
2348
2426
|
stream_events=stream_events,
|
|
2349
|
-
session_state=run_context.session_state,
|
|
2350
|
-
run_context=run_context,
|
|
2351
2427
|
):
|
|
2352
2428
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2429
|
+
yield item
|
|
2430
|
+
|
|
2431
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2432
|
+
|
|
2433
|
+
# 9. Generate a response from the Model
|
|
2434
|
+
if self.output_model is None:
|
|
2435
|
+
async for event in self._ahandle_model_response_stream(
|
|
2436
|
+
session=agent_session,
|
|
2437
|
+
run_response=run_response,
|
|
2438
|
+
run_messages=run_messages,
|
|
2439
|
+
tools=_tools,
|
|
2440
|
+
response_format=response_format,
|
|
2441
|
+
stream_events=stream_events,
|
|
2442
|
+
session_state=run_context.session_state,
|
|
2443
|
+
run_context=run_context,
|
|
2444
|
+
):
|
|
2445
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2360
2446
|
yield event
|
|
2447
|
+
else:
|
|
2448
|
+
from agno.run.agent import (
|
|
2449
|
+
IntermediateRunContentEvent,
|
|
2450
|
+
RunContentEvent,
|
|
2451
|
+
) # type: ignore
|
|
2361
2452
|
|
|
2362
|
-
|
|
2363
|
-
|
|
2453
|
+
async for event in self._ahandle_model_response_stream(
|
|
2454
|
+
session=agent_session,
|
|
2455
|
+
run_response=run_response,
|
|
2456
|
+
run_messages=run_messages,
|
|
2457
|
+
tools=_tools,
|
|
2458
|
+
response_format=response_format,
|
|
2459
|
+
stream_events=stream_events,
|
|
2460
|
+
session_state=run_context.session_state,
|
|
2461
|
+
run_context=run_context,
|
|
2462
|
+
):
|
|
2463
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2464
|
+
if isinstance(event, RunContentEvent):
|
|
2465
|
+
if stream_events:
|
|
2466
|
+
yield IntermediateRunContentEvent(
|
|
2467
|
+
content=event.content,
|
|
2468
|
+
content_type=event.content_type,
|
|
2469
|
+
)
|
|
2470
|
+
else:
|
|
2471
|
+
yield event
|
|
2472
|
+
|
|
2473
|
+
# If an output model is provided, generate output using the output model
|
|
2474
|
+
async for event in self._agenerate_response_with_output_model_stream(
|
|
2475
|
+
session=agent_session,
|
|
2476
|
+
run_response=run_response,
|
|
2477
|
+
run_messages=run_messages,
|
|
2478
|
+
stream_events=stream_events,
|
|
2479
|
+
):
|
|
2480
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2481
|
+
yield event
|
|
2482
|
+
|
|
2483
|
+
# Check for cancellation after model processing
|
|
2484
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2485
|
+
|
|
2486
|
+
# 10. Parse response with parser model if provided
|
|
2487
|
+
async for event in self._aparse_response_with_parser_model_stream(
|
|
2364
2488
|
session=agent_session,
|
|
2365
2489
|
run_response=run_response,
|
|
2366
|
-
run_messages=run_messages,
|
|
2367
2490
|
stream_events=stream_events,
|
|
2491
|
+
run_context=run_context,
|
|
2368
2492
|
):
|
|
2369
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2370
2493
|
yield event
|
|
2371
2494
|
|
|
2372
|
-
|
|
2373
|
-
|
|
2495
|
+
if stream_events:
|
|
2496
|
+
yield handle_event( # type: ignore
|
|
2497
|
+
create_run_content_completed_event(from_run_response=run_response),
|
|
2498
|
+
run_response,
|
|
2499
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2500
|
+
store_events=self.store_events,
|
|
2501
|
+
)
|
|
2374
2502
|
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2503
|
+
# Break out of the run function if a tool call is paused
|
|
2504
|
+
if any(tool_call.is_paused for tool_call in run_response.tools or []):
|
|
2505
|
+
async for item in await_for_thread_tasks_stream(
|
|
2506
|
+
memory_task=memory_task,
|
|
2507
|
+
cultural_knowledge_task=cultural_knowledge_task,
|
|
2508
|
+
stream_events=stream_events,
|
|
2509
|
+
run_response=run_response,
|
|
2510
|
+
):
|
|
2511
|
+
yield item
|
|
2380
2512
|
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
)
|
|
2513
|
+
async for item in self._ahandle_agent_run_paused_stream(
|
|
2514
|
+
run_response=run_response, session=agent_session, user_id=user_id
|
|
2515
|
+
):
|
|
2516
|
+
yield item
|
|
2517
|
+
return
|
|
2518
|
+
|
|
2519
|
+
# Execute post-hooks (after output is generated but before response is returned)
|
|
2520
|
+
if self.post_hooks is not None:
|
|
2521
|
+
async for event in self._aexecute_post_hooks(
|
|
2522
|
+
hooks=self.post_hooks, # type: ignore
|
|
2523
|
+
run_output=run_response,
|
|
2524
|
+
run_context=run_context,
|
|
2525
|
+
session=agent_session,
|
|
2526
|
+
user_id=user_id,
|
|
2527
|
+
debug_mode=debug_mode,
|
|
2528
|
+
stream_events=stream_events,
|
|
2529
|
+
background_tasks=background_tasks,
|
|
2530
|
+
**kwargs,
|
|
2531
|
+
):
|
|
2532
|
+
yield event
|
|
2388
2533
|
|
|
2389
|
-
|
|
2390
|
-
if any(tool_call.is_paused for tool_call in run_response.tools or []):
|
|
2534
|
+
# 11. Wait for background memory creation
|
|
2391
2535
|
async for item in await_for_thread_tasks_stream(
|
|
2392
2536
|
memory_task=memory_task,
|
|
2393
2537
|
cultural_knowledge_task=cultural_knowledge_task,
|
|
2394
2538
|
stream_events=stream_events,
|
|
2395
2539
|
run_response=run_response,
|
|
2540
|
+
events_to_skip=self.events_to_skip,
|
|
2541
|
+
store_events=self.store_events,
|
|
2396
2542
|
):
|
|
2397
2543
|
yield item
|
|
2398
2544
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
return
|
|
2545
|
+
# 12. Create session summary
|
|
2546
|
+
if self.session_summary_manager is not None and self.enable_session_summaries:
|
|
2547
|
+
# Upsert the RunOutput to Agent Session before creating the session summary
|
|
2548
|
+
agent_session.upsert_run(run=run_response)
|
|
2404
2549
|
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2550
|
+
if stream_events:
|
|
2551
|
+
yield handle_event( # type: ignore
|
|
2552
|
+
create_session_summary_started_event(from_run_response=run_response),
|
|
2553
|
+
run_response,
|
|
2554
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2555
|
+
store_events=self.store_events,
|
|
2556
|
+
)
|
|
2557
|
+
try:
|
|
2558
|
+
await self.session_summary_manager.acreate_session_summary(session=agent_session)
|
|
2559
|
+
except Exception as e:
|
|
2560
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2561
|
+
if stream_events:
|
|
2562
|
+
yield handle_event( # type: ignore
|
|
2563
|
+
create_session_summary_completed_event(
|
|
2564
|
+
from_run_response=run_response, session_summary=agent_session.summary
|
|
2565
|
+
),
|
|
2566
|
+
run_response,
|
|
2567
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2568
|
+
store_events=self.store_events,
|
|
2569
|
+
)
|
|
2570
|
+
|
|
2571
|
+
# Update run_response.session_state before creating RunCompletedEvent
|
|
2572
|
+
# This ensures the event has the final state after all tool modifications
|
|
2573
|
+
if agent_session.session_data is not None and "session_state" in agent_session.session_data:
|
|
2574
|
+
run_response.session_state = agent_session.session_data["session_state"]
|
|
2575
|
+
|
|
2576
|
+
# Create the run completed event
|
|
2577
|
+
completed_event = handle_event(
|
|
2578
|
+
create_run_completed_event(from_run_response=run_response),
|
|
2579
|
+
run_response,
|
|
2580
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2581
|
+
store_events=self.store_events,
|
|
2582
|
+
)
|
|
2583
|
+
|
|
2584
|
+
# Set the run status to completed
|
|
2585
|
+
run_response.status = RunStatus.completed
|
|
2586
|
+
|
|
2587
|
+
# 13. Cleanup and store the run response and session
|
|
2588
|
+
await self._acleanup_and_store(
|
|
2589
|
+
run_response=run_response,
|
|
2411
2590
|
session=agent_session,
|
|
2591
|
+
run_context=run_context,
|
|
2412
2592
|
user_id=user_id,
|
|
2413
|
-
|
|
2414
|
-
stream_events=stream_events,
|
|
2415
|
-
background_tasks=background_tasks,
|
|
2416
|
-
**kwargs,
|
|
2417
|
-
):
|
|
2418
|
-
yield event
|
|
2593
|
+
)
|
|
2419
2594
|
|
|
2420
|
-
|
|
2421
|
-
|
|
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
|
|
2595
|
+
if stream_events:
|
|
2596
|
+
yield completed_event # type: ignore
|
|
2430
2597
|
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
# Upsert the RunOutput to Agent Session before creating the session summary
|
|
2434
|
-
agent_session.upsert_run(run=run_response)
|
|
2598
|
+
if yield_run_output:
|
|
2599
|
+
yield run_response
|
|
2435
2600
|
|
|
2436
|
-
|
|
2437
|
-
|
|
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
|
-
)
|
|
2601
|
+
# Log Agent Telemetry
|
|
2602
|
+
await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
|
|
2456
2603
|
|
|
2457
|
-
|
|
2458
|
-
# This ensures the event has the final state after all tool modifications
|
|
2459
|
-
if agent_session.session_data is not None and "session_state" in agent_session.session_data:
|
|
2460
|
-
run_response.session_state = agent_session.session_data["session_state"]
|
|
2604
|
+
log_debug(f"Agent Run End: {run_response.run_id}", center=True, symbol="*")
|
|
2461
2605
|
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
run_response
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2606
|
+
except RunCancelledException as e:
|
|
2607
|
+
# Handle run cancellation during async streaming
|
|
2608
|
+
log_info(f"Run {run_response.run_id} was cancelled during async streaming")
|
|
2609
|
+
run_response.status = RunStatus.cancelled
|
|
2610
|
+
# Don't overwrite content - preserve any partial content that was streamed
|
|
2611
|
+
# Only set content if it's empty
|
|
2612
|
+
if not run_response.content:
|
|
2613
|
+
run_response.content = str(e)
|
|
2469
2614
|
|
|
2470
|
-
|
|
2471
|
-
|
|
2615
|
+
# Yield the cancellation event
|
|
2616
|
+
yield handle_event( # type: ignore
|
|
2617
|
+
create_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
2618
|
+
run_response,
|
|
2619
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2620
|
+
store_events=self.store_events,
|
|
2621
|
+
)
|
|
2472
2622
|
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2623
|
+
# Cleanup and store the run response and session
|
|
2624
|
+
await self._acleanup_and_store(
|
|
2625
|
+
run_response=run_response,
|
|
2626
|
+
session=agent_session,
|
|
2627
|
+
run_context=run_context,
|
|
2628
|
+
user_id=user_id,
|
|
2629
|
+
)
|
|
2480
2630
|
|
|
2481
|
-
|
|
2482
|
-
|
|
2631
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2632
|
+
# Handle exceptions during async streaming
|
|
2633
|
+
run_response.status = RunStatus.error
|
|
2634
|
+
# Add error event to list of events
|
|
2635
|
+
run_error = create_run_error_event(
|
|
2636
|
+
run_response,
|
|
2637
|
+
error=str(e),
|
|
2638
|
+
error_id=e.error_id,
|
|
2639
|
+
error_type=e.type,
|
|
2640
|
+
additional_data=e.additional_data,
|
|
2641
|
+
)
|
|
2642
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
2483
2643
|
|
|
2484
|
-
|
|
2485
|
-
|
|
2644
|
+
# If the content is None, set it to the error message
|
|
2645
|
+
if run_response.content is None:
|
|
2646
|
+
run_response.content = str(e)
|
|
2486
2647
|
|
|
2487
|
-
|
|
2488
|
-
await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
|
|
2648
|
+
log_error(f"Validation failed: {str(e)} | Check trigger: {e.check_trigger}")
|
|
2489
2649
|
|
|
2490
|
-
|
|
2650
|
+
# Cleanup and store the run response and session
|
|
2651
|
+
await self._acleanup_and_store(
|
|
2652
|
+
run_response=run_response,
|
|
2653
|
+
session=agent_session,
|
|
2654
|
+
run_context=run_context,
|
|
2655
|
+
user_id=user_id,
|
|
2656
|
+
)
|
|
2491
2657
|
|
|
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)
|
|
2658
|
+
# Yield the error event
|
|
2659
|
+
yield run_error
|
|
2660
|
+
break
|
|
2500
2661
|
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2662
|
+
except Exception as e:
|
|
2663
|
+
# Check if this is the last attempt
|
|
2664
|
+
if attempt < num_attempts - 1:
|
|
2665
|
+
# Calculate delay with exponential backoff if enabled
|
|
2666
|
+
if self.exponential_backoff:
|
|
2667
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2668
|
+
else:
|
|
2669
|
+
delay = self.delay_between_retries
|
|
2508
2670
|
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
session=agent_session,
|
|
2513
|
-
run_context=run_context,
|
|
2514
|
-
user_id=user_id,
|
|
2515
|
-
)
|
|
2516
|
-
finally:
|
|
2517
|
-
# Always disconnect connectable tools
|
|
2518
|
-
self._disconnect_connectable_tools()
|
|
2519
|
-
# Always disconnect MCP tools
|
|
2520
|
-
await self._disconnect_mcp_tools()
|
|
2671
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2672
|
+
time.sleep(delay)
|
|
2673
|
+
continue
|
|
2521
2674
|
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
except CancelledError:
|
|
2528
|
-
pass
|
|
2675
|
+
# Handle exceptions during async streaming
|
|
2676
|
+
run_response.status = RunStatus.error
|
|
2677
|
+
# Add error event to list of events
|
|
2678
|
+
run_error = create_run_error_event(run_response, error=str(e))
|
|
2679
|
+
run_response.events = add_error_event(error=run_error, events=run_response.events)
|
|
2529
2680
|
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
await cultural_knowledge_task
|
|
2534
|
-
except CancelledError:
|
|
2535
|
-
pass
|
|
2681
|
+
# If the content is None, set it to the error message
|
|
2682
|
+
if run_response.content is None:
|
|
2683
|
+
run_response.content = str(e)
|
|
2536
2684
|
|
|
2537
|
-
|
|
2538
|
-
|
|
2685
|
+
log_error(f"Error in Agent run: {str(e)}")
|
|
2686
|
+
|
|
2687
|
+
# Cleanup and store the run response and session
|
|
2688
|
+
await self._acleanup_and_store(
|
|
2689
|
+
run_response=run_response,
|
|
2690
|
+
session=agent_session,
|
|
2691
|
+
run_context=run_context,
|
|
2692
|
+
user_id=user_id,
|
|
2693
|
+
)
|
|
2694
|
+
|
|
2695
|
+
# Yield the error event
|
|
2696
|
+
yield run_error
|
|
2697
|
+
finally:
|
|
2698
|
+
# Always disconnect connectable tools
|
|
2699
|
+
self._disconnect_connectable_tools()
|
|
2700
|
+
# Always disconnect MCP tools
|
|
2701
|
+
await self._disconnect_mcp_tools()
|
|
2702
|
+
|
|
2703
|
+
# Cancel the memory task if it's still running
|
|
2704
|
+
if memory_task is not None and not memory_task.done():
|
|
2705
|
+
memory_task.cancel()
|
|
2706
|
+
try:
|
|
2707
|
+
await memory_task
|
|
2708
|
+
except CancelledError:
|
|
2709
|
+
pass
|
|
2710
|
+
|
|
2711
|
+
if cultural_knowledge_task is not None and not cultural_knowledge_task.done():
|
|
2712
|
+
cultural_knowledge_task.cancel()
|
|
2713
|
+
try:
|
|
2714
|
+
await cultural_knowledge_task
|
|
2715
|
+
except CancelledError:
|
|
2716
|
+
pass
|
|
2717
|
+
|
|
2718
|
+
# Always clean up the run tracking
|
|
2719
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
2539
2720
|
|
|
2540
2721
|
@overload
|
|
2541
2722
|
async def arun(
|
|
@@ -2560,7 +2741,7 @@ class Agent:
|
|
|
2560
2741
|
add_session_state_to_context: Optional[bool] = None,
|
|
2561
2742
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2562
2743
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2563
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
2744
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2564
2745
|
debug_mode: Optional[bool] = None,
|
|
2565
2746
|
**kwargs: Any,
|
|
2566
2747
|
) -> RunOutput: ...
|
|
@@ -2587,7 +2768,7 @@ class Agent:
|
|
|
2587
2768
|
add_session_state_to_context: Optional[bool] = None,
|
|
2588
2769
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2589
2770
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2590
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
2771
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2591
2772
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2592
2773
|
yield_run_output: Optional[bool] = None,
|
|
2593
2774
|
debug_mode: Optional[bool] = None,
|
|
@@ -2616,7 +2797,7 @@ class Agent:
|
|
|
2616
2797
|
add_session_state_to_context: Optional[bool] = None,
|
|
2617
2798
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2618
2799
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2619
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
2800
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2620
2801
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2621
2802
|
yield_run_output: Optional[bool] = None,
|
|
2622
2803
|
debug_mode: Optional[bool] = None,
|
|
@@ -2764,82 +2945,37 @@ class Agent:
|
|
|
2764
2945
|
|
|
2765
2946
|
yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
|
|
2766
2947
|
|
|
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.")
|
|
2948
|
+
# Pass the new run_response to _arun
|
|
2949
|
+
if stream:
|
|
2950
|
+
return self._arun_stream( # type: ignore
|
|
2951
|
+
run_response=run_response,
|
|
2952
|
+
run_context=run_context,
|
|
2953
|
+
user_id=user_id,
|
|
2954
|
+
response_format=response_format,
|
|
2955
|
+
stream_events=stream_events,
|
|
2956
|
+
yield_run_output=yield_run_output,
|
|
2957
|
+
session_id=session_id,
|
|
2958
|
+
add_history_to_context=add_history,
|
|
2959
|
+
add_dependencies_to_context=add_dependencies,
|
|
2960
|
+
add_session_state_to_context=add_session_state,
|
|
2961
|
+
debug_mode=debug_mode,
|
|
2962
|
+
background_tasks=background_tasks,
|
|
2963
|
+
**kwargs,
|
|
2964
|
+
) # type: ignore[assignment]
|
|
2965
|
+
else:
|
|
2966
|
+
return self._arun( # type: ignore
|
|
2967
|
+
run_response=run_response,
|
|
2968
|
+
run_context=run_context,
|
|
2969
|
+
user_id=user_id,
|
|
2970
|
+
response_format=response_format,
|
|
2971
|
+
session_id=session_id,
|
|
2972
|
+
add_history_to_context=add_history,
|
|
2973
|
+
add_dependencies_to_context=add_dependencies,
|
|
2974
|
+
add_session_state_to_context=add_session_state,
|
|
2975
|
+
debug_mode=debug_mode,
|
|
2976
|
+
background_tasks=background_tasks,
|
|
2977
|
+
**kwargs,
|
|
2978
|
+
)
|
|
2843
2979
|
|
|
2844
2980
|
@overload
|
|
2845
2981
|
def continue_run(
|
|
@@ -2970,7 +3106,7 @@ class Agent:
|
|
|
2970
3106
|
num_attempts = self.retries + 1
|
|
2971
3107
|
|
|
2972
3108
|
for attempt in range(num_attempts):
|
|
2973
|
-
if
|
|
3109
|
+
if num_attempts > 1:
|
|
2974
3110
|
log_debug(f"Retrying Agent continue_run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2975
3111
|
|
|
2976
3112
|
try:
|
|
@@ -3611,7 +3747,7 @@ class Agent:
|
|
|
3611
3747
|
)
|
|
3612
3748
|
|
|
3613
3749
|
for attempt in range(num_attempts):
|
|
3614
|
-
if
|
|
3750
|
+
if num_attempts > 1:
|
|
3615
3751
|
log_debug(f"Retrying Agent acontinue_run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
3616
3752
|
|
|
3617
3753
|
try:
|
|
@@ -4689,22 +4825,34 @@ class Agent:
|
|
|
4689
4825
|
output_schema = run_context.output_schema if run_context else None
|
|
4690
4826
|
|
|
4691
4827
|
# 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
|
|
4828
|
+
if output_schema is not None:
|
|
4829
|
+
# If the output schema is a dict, do not convert it into a BaseModel
|
|
4830
|
+
if isinstance(output_schema, dict):
|
|
4831
|
+
if isinstance(run_response.content, str):
|
|
4832
|
+
parsed_dict = parse_response_dict_str(run_response.content)
|
|
4833
|
+
if parsed_dict is not None:
|
|
4834
|
+
run_response.content = parsed_dict
|
|
4700
4835
|
if isinstance(run_response, RunOutput):
|
|
4701
|
-
run_response.content_type =
|
|
4836
|
+
run_response.content_type = "dict"
|
|
4702
4837
|
else:
|
|
4703
|
-
log_warning("Failed to
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4838
|
+
log_warning("Failed to parse JSON response against the provided output schema.")
|
|
4839
|
+
# If the output schema is a Pydantic model and parse_response is True, parse it into a BaseModel
|
|
4840
|
+
elif not isinstance(run_response.content, output_schema):
|
|
4841
|
+
if isinstance(run_response.content, str) and self.parse_response:
|
|
4842
|
+
try:
|
|
4843
|
+
structured_output = parse_response_model_str(run_response.content, output_schema)
|
|
4844
|
+
|
|
4845
|
+
# Update RunOutput
|
|
4846
|
+
if structured_output is not None:
|
|
4847
|
+
run_response.content = structured_output
|
|
4848
|
+
if isinstance(run_response, RunOutput):
|
|
4849
|
+
run_response.content_type = output_schema.__name__
|
|
4850
|
+
else:
|
|
4851
|
+
log_warning("Failed to convert response to output_schema")
|
|
4852
|
+
except Exception as e:
|
|
4853
|
+
log_warning(f"Failed to convert response to output model: {e}")
|
|
4854
|
+
else:
|
|
4855
|
+
log_warning("Something went wrong. Run response content is not a string")
|
|
4708
4856
|
|
|
4709
4857
|
def _handle_external_execution_update(self, run_messages: RunMessages, tool: ToolExecution):
|
|
4710
4858
|
self.model = cast(Model, self.model)
|
|
@@ -4799,6 +4947,15 @@ class Agent:
|
|
|
4799
4947
|
events_to_skip=self.events_to_skip, # type: ignore
|
|
4800
4948
|
store_events=self.store_events,
|
|
4801
4949
|
)
|
|
4950
|
+
if tool.tool_call_error:
|
|
4951
|
+
yield handle_event( # type: ignore
|
|
4952
|
+
create_tool_call_error_event(
|
|
4953
|
+
from_run_response=run_response, tool=tool, error=str(tool.result)
|
|
4954
|
+
),
|
|
4955
|
+
run_response,
|
|
4956
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
4957
|
+
store_events=self.store_events,
|
|
4958
|
+
)
|
|
4802
4959
|
|
|
4803
4960
|
if len(function_call_results) > 0:
|
|
4804
4961
|
run_messages.messages.extend(function_call_results)
|
|
@@ -4856,6 +5013,15 @@ class Agent:
|
|
|
4856
5013
|
events_to_skip=self.events_to_skip, # type: ignore
|
|
4857
5014
|
store_events=self.store_events,
|
|
4858
5015
|
)
|
|
5016
|
+
if tool.tool_call_error:
|
|
5017
|
+
yield handle_event( # type: ignore
|
|
5018
|
+
create_tool_call_error_event(
|
|
5019
|
+
from_run_response=run_response, tool=tool, error=str(tool.result)
|
|
5020
|
+
),
|
|
5021
|
+
run_response,
|
|
5022
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
5023
|
+
store_events=self.store_events,
|
|
5024
|
+
)
|
|
4859
5025
|
if len(function_call_results) > 0:
|
|
4860
5026
|
run_messages.messages.extend(function_call_results)
|
|
4861
5027
|
|
|
@@ -5053,7 +5219,7 @@ class Agent:
|
|
|
5053
5219
|
# Update the run_response content with the structured output
|
|
5054
5220
|
run_response.content = model_response.parsed
|
|
5055
5221
|
# Update the run_response content_type with the structured output class name
|
|
5056
|
-
run_response.content_type = output_schema.__name__
|
|
5222
|
+
run_response.content_type = "dict" if isinstance(output_schema, dict) else output_schema.__name__
|
|
5057
5223
|
else:
|
|
5058
5224
|
# Update the run_response content with the model response content
|
|
5059
5225
|
run_response.content = model_response.content
|
|
@@ -5339,7 +5505,7 @@ class Agent:
|
|
|
5339
5505
|
|
|
5340
5506
|
# Get output_schema from run_context
|
|
5341
5507
|
output_schema = run_context.output_schema if run_context else None
|
|
5342
|
-
content_type = output_schema.__name__ # type: ignore
|
|
5508
|
+
content_type = "dict" if isinstance(output_schema, dict) else output_schema.__name__ # type: ignore
|
|
5343
5509
|
run_response.content = model_response.content
|
|
5344
5510
|
run_response.content_type = content_type
|
|
5345
5511
|
else:
|
|
@@ -5609,6 +5775,15 @@ class Agent:
|
|
|
5609
5775
|
events_to_skip=self.events_to_skip, # type: ignore
|
|
5610
5776
|
store_events=self.store_events,
|
|
5611
5777
|
)
|
|
5778
|
+
if tool_call.tool_call_error:
|
|
5779
|
+
yield handle_event( # type: ignore
|
|
5780
|
+
create_tool_call_error_event(
|
|
5781
|
+
from_run_response=run_response, tool=tool_call, error=str(tool_call.result)
|
|
5782
|
+
),
|
|
5783
|
+
run_response,
|
|
5784
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
5785
|
+
store_events=self.store_events,
|
|
5786
|
+
)
|
|
5612
5787
|
|
|
5613
5788
|
if stream_events:
|
|
5614
5789
|
if reasoning_step is not None:
|
|
@@ -6109,6 +6284,10 @@ class Agent:
|
|
|
6109
6284
|
elif model.supports_json_schema_outputs:
|
|
6110
6285
|
if self.use_json_mode or (not self.structured_outputs):
|
|
6111
6286
|
log_debug("Setting Model.response_format to JSON response mode")
|
|
6287
|
+
# Handle JSON schema - pass through directly (user provides full provider format)
|
|
6288
|
+
if isinstance(output_schema, dict):
|
|
6289
|
+
return output_schema
|
|
6290
|
+
# Handle Pydantic schema
|
|
6112
6291
|
return {
|
|
6113
6292
|
"type": "json_schema",
|
|
6114
6293
|
"json_schema": {
|
|
@@ -7521,8 +7700,8 @@ class Agent:
|
|
|
7521
7700
|
):
|
|
7522
7701
|
system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
7523
7702
|
|
|
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:
|
|
7703
|
+
# 3.3.14 Add the response model format prompt if output_schema is provided (Pydantic only)
|
|
7704
|
+
if output_schema is not None and self.parser_model is not None and not isinstance(output_schema, dict):
|
|
7526
7705
|
system_message_content += f"{get_response_model_format_prompt(output_schema)}"
|
|
7527
7706
|
|
|
7528
7707
|
# 3.3.15 Add the session state to the system message
|
|
@@ -7868,8 +8047,8 @@ class Agent:
|
|
|
7868
8047
|
):
|
|
7869
8048
|
system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
7870
8049
|
|
|
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:
|
|
8050
|
+
# 3.3.14 Add the response model format prompt if output_schema is provided (Pydantic only)
|
|
8051
|
+
if output_schema is not None and self.parser_model is not None and not isinstance(output_schema, dict):
|
|
7873
8052
|
system_message_content += f"{get_response_model_format_prompt(output_schema)}"
|
|
7874
8053
|
|
|
7875
8054
|
# 3.3.15 Add the session state to the system message
|
|
@@ -9176,292 +9355,78 @@ class Agent:
|
|
|
9176
9355
|
|
|
9177
9356
|
return updated_reasoning_content
|
|
9178
9357
|
|
|
9179
|
-
def
|
|
9180
|
-
self,
|
|
9358
|
+
def _handle_reasoning_event(
|
|
9359
|
+
self,
|
|
9360
|
+
event: "ReasoningEvent", # type: ignore # noqa: F821
|
|
9361
|
+
run_response: RunOutput,
|
|
9362
|
+
stream_events: Optional[bool] = None,
|
|
9181
9363
|
) -> 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
|
|
9364
|
+
"""
|
|
9365
|
+
Convert a ReasoningEvent from the ReasoningManager to Agent-specific RunOutputEvents.
|
|
9287
9366
|
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
)
|
|
9293
|
-
elif is_anthropic:
|
|
9294
|
-
from agno.reasoning.anthropic import get_anthropic_reasoning
|
|
9367
|
+
This method handles the conversion of generic reasoning events to Agent events,
|
|
9368
|
+
keeping the Agent._reason() method clean and simple.
|
|
9369
|
+
"""
|
|
9370
|
+
from agno.reasoning.manager import ReasoningEventType
|
|
9295
9371
|
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9372
|
+
if event.event_type == ReasoningEventType.started:
|
|
9373
|
+
if stream_events:
|
|
9374
|
+
yield handle_event( # type: ignore
|
|
9375
|
+
create_reasoning_started_event(from_run_response=run_response),
|
|
9376
|
+
run_response,
|
|
9377
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
9378
|
+
store_events=self.store_events,
|
|
9379
|
+
)
|
|
9303
9380
|
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9381
|
+
elif event.event_type == ReasoningEventType.content_delta:
|
|
9382
|
+
if stream_events and event.reasoning_content:
|
|
9383
|
+
yield handle_event( # type: ignore
|
|
9384
|
+
create_reasoning_content_delta_event(
|
|
9385
|
+
from_run_response=run_response,
|
|
9386
|
+
reasoning_content=event.reasoning_content,
|
|
9387
|
+
),
|
|
9388
|
+
run_response,
|
|
9389
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
9390
|
+
store_events=self.store_events,
|
|
9391
|
+
)
|
|
9309
9392
|
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
run_messages.messages.append(reasoning_message)
|
|
9314
|
-
# Add reasoning step to the Agent's run_response
|
|
9393
|
+
elif event.event_type == ReasoningEventType.step:
|
|
9394
|
+
if event.reasoning_step:
|
|
9395
|
+
# Update run_response with this step
|
|
9315
9396
|
update_run_output_with_reasoning(
|
|
9316
9397
|
run_response=run_response,
|
|
9317
|
-
reasoning_steps=[
|
|
9318
|
-
reasoning_agent_messages=[
|
|
9398
|
+
reasoning_steps=[event.reasoning_step],
|
|
9399
|
+
reasoning_agent_messages=[],
|
|
9319
9400
|
)
|
|
9320
9401
|
if stream_events:
|
|
9402
|
+
updated_reasoning_content = self._format_reasoning_step_content(
|
|
9403
|
+
run_response=run_response,
|
|
9404
|
+
reasoning_step=event.reasoning_step,
|
|
9405
|
+
)
|
|
9321
9406
|
yield handle_event( # type: ignore
|
|
9322
|
-
|
|
9407
|
+
create_reasoning_step_event(
|
|
9323
9408
|
from_run_response=run_response,
|
|
9324
|
-
|
|
9325
|
-
|
|
9409
|
+
reasoning_step=event.reasoning_step,
|
|
9410
|
+
reasoning_content=updated_reasoning_content,
|
|
9326
9411
|
),
|
|
9327
9412
|
run_response,
|
|
9328
9413
|
events_to_skip=self.events_to_skip, # type: ignore
|
|
9329
9414
|
store_events=self.store_events,
|
|
9330
9415
|
)
|
|
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
9416
|
|
|
9459
|
-
|
|
9417
|
+
elif event.event_type == ReasoningEventType.completed:
|
|
9418
|
+
if event.message and event.reasoning_steps:
|
|
9419
|
+
# This is from native reasoning - update with the message and steps
|
|
9420
|
+
update_run_output_with_reasoning(
|
|
9421
|
+
run_response=run_response,
|
|
9422
|
+
reasoning_steps=event.reasoning_steps,
|
|
9423
|
+
reasoning_agent_messages=event.reasoning_messages,
|
|
9424
|
+
)
|
|
9460
9425
|
if stream_events:
|
|
9461
9426
|
yield handle_event( # type: ignore
|
|
9462
9427
|
create_reasoning_completed_event(
|
|
9463
9428
|
from_run_response=run_response,
|
|
9464
|
-
content=ReasoningSteps(reasoning_steps=
|
|
9429
|
+
content=ReasoningSteps(reasoning_steps=event.reasoning_steps),
|
|
9465
9430
|
content_type=ReasoningSteps.__name__,
|
|
9466
9431
|
),
|
|
9467
9432
|
run_response,
|
|
@@ -9469,45 +9434,37 @@ class Agent:
|
|
|
9469
9434
|
store_events=self.store_events,
|
|
9470
9435
|
)
|
|
9471
9436
|
|
|
9472
|
-
|
|
9437
|
+
elif event.event_type == ReasoningEventType.error:
|
|
9438
|
+
log_warning(f"Reasoning error. {event.error}, continuing regular session...")
|
|
9439
|
+
|
|
9440
|
+
def _reason(
|
|
9473
9441
|
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
|
-
)
|
|
9442
|
+
) -> Iterator[RunOutputEvent]:
|
|
9443
|
+
"""
|
|
9444
|
+
Run reasoning using the ReasoningManager.
|
|
9483
9445
|
|
|
9484
|
-
|
|
9446
|
+
Handles both native reasoning models (DeepSeek, Anthropic, etc.) and
|
|
9447
|
+
default Chain-of-Thought reasoning with a clean, unified interface.
|
|
9448
|
+
"""
|
|
9449
|
+
from agno.reasoning.manager import ReasoningConfig, ReasoningManager
|
|
9485
9450
|
|
|
9486
|
-
# Get the reasoning model
|
|
9451
|
+
# Get the reasoning model (use copy of main model if not provided)
|
|
9487
9452
|
reasoning_model: Optional[Model] = self.reasoning_model
|
|
9488
|
-
reasoning_model_provided = reasoning_model is not None
|
|
9489
9453
|
if reasoning_model is None and self.model is not None:
|
|
9490
9454
|
from copy import deepcopy
|
|
9491
9455
|
|
|
9492
9456
|
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
9457
|
|
|
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(
|
|
9458
|
+
# Create reasoning manager with config
|
|
9459
|
+
manager = ReasoningManager(
|
|
9460
|
+
ReasoningConfig(
|
|
9510
9461
|
reasoning_model=reasoning_model,
|
|
9462
|
+
reasoning_agent=self.reasoning_agent,
|
|
9463
|
+
min_steps=self.reasoning_min_steps,
|
|
9464
|
+
max_steps=self.reasoning_max_steps,
|
|
9465
|
+
tools=self.tools,
|
|
9466
|
+
tool_call_limit=self.tool_call_limit,
|
|
9467
|
+
use_json_mode=self.use_json_mode,
|
|
9511
9468
|
telemetry=self.telemetry,
|
|
9512
9469
|
debug_mode=self.debug_mode,
|
|
9513
9470
|
debug_level=self.debug_level,
|
|
@@ -9515,252 +9472,53 @@ class Agent:
|
|
|
9515
9472
|
dependencies=self.dependencies,
|
|
9516
9473
|
metadata=self.metadata,
|
|
9517
9474
|
)
|
|
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
|
-
)
|
|
9475
|
+
)
|
|
9707
9476
|
|
|
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
|
-
)
|
|
9477
|
+
# Use the unified reason() method and convert events
|
|
9478
|
+
for event in manager.reason(run_messages, stream=bool(stream_events)):
|
|
9479
|
+
yield from self._handle_reasoning_event(event, run_response, stream_events)
|
|
9719
9480
|
|
|
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:]
|
|
9481
|
+
async def _areason(
|
|
9482
|
+
self, run_response: RunOutput, run_messages: RunMessages, stream_events: Optional[bool] = None
|
|
9483
|
+
) -> Any:
|
|
9484
|
+
"""
|
|
9485
|
+
Run reasoning asynchronously using the ReasoningManager.
|
|
9727
9486
|
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
reasoning_agent_messages=reasoning_agent_response.messages,
|
|
9733
|
-
)
|
|
9487
|
+
Handles both native reasoning models (DeepSeek, Anthropic, etc.) and
|
|
9488
|
+
default Chain-of-Thought reasoning with a clean, unified interface.
|
|
9489
|
+
"""
|
|
9490
|
+
from agno.reasoning.manager import ReasoningConfig, ReasoningManager
|
|
9734
9491
|
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
except Exception as e:
|
|
9740
|
-
log_error(f"Reasoning error: {e}")
|
|
9741
|
-
break
|
|
9492
|
+
# Get the reasoning model (use copy of main model if not provided)
|
|
9493
|
+
reasoning_model: Optional[Model] = self.reasoning_model
|
|
9494
|
+
if reasoning_model is None and self.model is not None:
|
|
9495
|
+
from copy import deepcopy
|
|
9742
9496
|
|
|
9743
|
-
|
|
9744
|
-
log_debug("Reasoning finished", center=True, symbol="=")
|
|
9497
|
+
reasoning_model = deepcopy(self.model)
|
|
9745
9498
|
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9499
|
+
# Create reasoning manager with config
|
|
9500
|
+
manager = ReasoningManager(
|
|
9501
|
+
ReasoningConfig(
|
|
9502
|
+
reasoning_model=reasoning_model,
|
|
9503
|
+
reasoning_agent=self.reasoning_agent,
|
|
9504
|
+
min_steps=self.reasoning_min_steps,
|
|
9505
|
+
max_steps=self.reasoning_max_steps,
|
|
9506
|
+
tools=self.tools,
|
|
9507
|
+
tool_call_limit=self.tool_call_limit,
|
|
9508
|
+
use_json_mode=self.use_json_mode,
|
|
9509
|
+
telemetry=self.telemetry,
|
|
9510
|
+
debug_mode=self.debug_mode,
|
|
9511
|
+
debug_level=self.debug_level,
|
|
9512
|
+
session_state=self.session_state,
|
|
9513
|
+
dependencies=self.dependencies,
|
|
9514
|
+
metadata=self.metadata,
|
|
9750
9515
|
)
|
|
9516
|
+
)
|
|
9751
9517
|
|
|
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
|
-
)
|
|
9518
|
+
# Use the unified areason() method and convert events
|
|
9519
|
+
async for event in manager.areason(run_messages, stream=bool(stream_events)):
|
|
9520
|
+
for output_event in self._handle_reasoning_event(event, run_response, stream_events):
|
|
9521
|
+
yield output_event
|
|
9764
9522
|
|
|
9765
9523
|
def _process_parser_response(
|
|
9766
9524
|
self,
|
|
@@ -10912,8 +10670,7 @@ class Agent:
|
|
|
10912
10670
|
session: AgentSession,
|
|
10913
10671
|
run_context: Optional[RunContext] = None,
|
|
10914
10672
|
user_id: Optional[str] = None,
|
|
10915
|
-
) -> None:
|
|
10916
|
-
# Scrub the stored run based on storage flags
|
|
10673
|
+
) -> None: # Scrub the stored run based on storage flags
|
|
10917
10674
|
self._scrub_run_output_for_storage(run_response)
|
|
10918
10675
|
|
|
10919
10676
|
# Stop the timer for the Run duration
|