agno 2.3.2__py3-none-any.whl → 2.3.3__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 +513 -185
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +176 -0
- agno/db/dynamo/dynamo.py +11 -0
- agno/db/firestore/firestore.py +5 -1
- agno/db/gcs_json/gcs_json_db.py +5 -2
- agno/db/in_memory/in_memory_db.py +5 -2
- agno/db/json/json_db.py +5 -1
- agno/db/migrations/manager.py +4 -4
- agno/db/mongo/async_mongo.py +158 -34
- agno/db/mongo/mongo.py +6 -2
- agno/db/mysql/mysql.py +48 -54
- agno/db/postgres/async_postgres.py +61 -51
- agno/db/postgres/postgres.py +42 -50
- agno/db/redis/redis.py +5 -0
- agno/db/redis/utils.py +5 -5
- agno/db/singlestore/singlestore.py +99 -108
- agno/db/sqlite/async_sqlite.py +29 -27
- agno/db/sqlite/sqlite.py +30 -26
- agno/knowledge/reader/pdf_reader.py +2 -2
- agno/knowledge/reader/tavily_reader.py +0 -1
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +217 -4
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +67 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/anthropic/claude.py +84 -80
- agno/models/aws/bedrock.py +38 -16
- agno/models/aws/claude.py +97 -277
- agno/models/azure/ai_foundry.py +8 -4
- agno/models/base.py +101 -14
- agno/models/cerebras/cerebras.py +18 -7
- agno/models/cerebras/cerebras_openai.py +4 -2
- agno/models/cohere/chat.py +8 -4
- agno/models/google/gemini.py +578 -20
- agno/models/groq/groq.py +18 -5
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/litellm/chat.py +17 -7
- agno/models/message.py +19 -5
- agno/models/meta/llama.py +20 -4
- agno/models/mistral/mistral.py +8 -4
- agno/models/ollama/chat.py +17 -6
- agno/models/openai/chat.py +17 -6
- agno/models/openai/responses.py +23 -9
- agno/models/vertexai/claude.py +99 -5
- agno/os/interfaces/agui/router.py +1 -0
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/router.py +16 -0
- agno/os/routers/memory/memory.py +143 -0
- agno/os/routers/memory/schemas.py +26 -0
- agno/os/schema.py +21 -6
- agno/os/utils.py +134 -10
- agno/run/base.py +2 -1
- agno/run/workflow.py +1 -1
- agno/team/team.py +565 -219
- agno/tools/mcp/mcp.py +1 -1
- agno/utils/agent.py +119 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +12 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +37 -2
- agno/utils/print_response/team.py +52 -0
- agno/utils/tokens.py +41 -0
- agno/workflow/types.py +2 -2
- {agno-2.3.2.dist-info → agno-2.3.3.dist-info}/METADATA +45 -40
- {agno-2.3.2.dist-info → agno-2.3.3.dist-info}/RECORD +73 -66
- {agno-2.3.2.dist-info → agno-2.3.3.dist-info}/WHEEL +0 -0
- {agno-2.3.2.dist-info → agno-2.3.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.2.dist-info → agno-2.3.3.dist-info}/top_level.txt +0 -0
agno/agent/agent.py
CHANGED
|
@@ -29,6 +29,7 @@ from uuid import uuid4
|
|
|
29
29
|
|
|
30
30
|
from pydantic import BaseModel
|
|
31
31
|
|
|
32
|
+
from agno.compression.manager import CompressionManager
|
|
32
33
|
from agno.culture.manager import CultureManager
|
|
33
34
|
from agno.db.base import AsyncBaseDb, BaseDb, SessionType, UserMemory
|
|
34
35
|
from agno.db.schemas.culture import CulturalKnowledge
|
|
@@ -73,6 +74,8 @@ from agno.session.summary import SessionSummary
|
|
|
73
74
|
from agno.tools import Toolkit
|
|
74
75
|
from agno.tools.function import Function
|
|
75
76
|
from agno.utils.agent import (
|
|
77
|
+
aexecute_instructions,
|
|
78
|
+
aexecute_system_message,
|
|
76
79
|
aget_last_run_output_util,
|
|
77
80
|
aget_run_output_util,
|
|
78
81
|
aget_session_metrics_util,
|
|
@@ -86,6 +89,8 @@ from agno.utils.agent import (
|
|
|
86
89
|
collect_joint_files,
|
|
87
90
|
collect_joint_images,
|
|
88
91
|
collect_joint_videos,
|
|
92
|
+
execute_instructions,
|
|
93
|
+
execute_system_message,
|
|
89
94
|
get_last_run_output_util,
|
|
90
95
|
get_run_output_util,
|
|
91
96
|
get_session_metrics_util,
|
|
@@ -365,7 +370,7 @@ class Agent:
|
|
|
365
370
|
parse_response: bool = True
|
|
366
371
|
# Use model enforced structured_outputs if supported (e.g. OpenAIChat)
|
|
367
372
|
structured_outputs: Optional[bool] = None
|
|
368
|
-
#
|
|
373
|
+
# Intead of providing the model with the Pydantic output schema, add a JSON description of the output schema to the system message instead.
|
|
369
374
|
use_json_mode: bool = False
|
|
370
375
|
# Save the response to a file
|
|
371
376
|
save_response_to_file: Optional[str] = None
|
|
@@ -404,6 +409,12 @@ class Agent:
|
|
|
404
409
|
# If True, the agent adds cultural knowledge in the response
|
|
405
410
|
add_culture_to_context: Optional[bool] = None
|
|
406
411
|
|
|
412
|
+
# --- Context Compression ---
|
|
413
|
+
# If True, compress tool call results to save context
|
|
414
|
+
compress_tool_results: bool = False
|
|
415
|
+
# Compression manager for compressing tool call results
|
|
416
|
+
compression_manager: Optional[CompressionManager] = None
|
|
417
|
+
|
|
407
418
|
# --- Debug ---
|
|
408
419
|
# Enable debug logs
|
|
409
420
|
debug_mode: bool = False
|
|
@@ -443,6 +454,8 @@ class Agent:
|
|
|
443
454
|
enable_session_summaries: bool = False,
|
|
444
455
|
add_session_summary_to_context: Optional[bool] = None,
|
|
445
456
|
session_summary_manager: Optional[SessionSummaryManager] = None,
|
|
457
|
+
compress_tool_results: bool = False,
|
|
458
|
+
compression_manager: Optional[CompressionManager] = None,
|
|
446
459
|
add_history_to_context: bool = False,
|
|
447
460
|
num_history_runs: Optional[int] = None,
|
|
448
461
|
num_history_messages: Optional[int] = None,
|
|
@@ -546,6 +559,10 @@ class Agent:
|
|
|
546
559
|
self.enable_session_summaries = enable_session_summaries
|
|
547
560
|
self.add_session_summary_to_context = add_session_summary_to_context
|
|
548
561
|
|
|
562
|
+
# Context compression settings
|
|
563
|
+
self.compress_tool_results = compress_tool_results
|
|
564
|
+
self.compression_manager = compression_manager
|
|
565
|
+
|
|
549
566
|
self.add_history_to_context = add_history_to_context
|
|
550
567
|
self.num_history_runs = num_history_runs
|
|
551
568
|
self.num_history_messages = num_history_messages
|
|
@@ -684,10 +701,6 @@ class Agent:
|
|
|
684
701
|
self._background_executor = ThreadPoolExecutor(max_workers=3, thread_name_prefix="agno-bg")
|
|
685
702
|
return self._background_executor
|
|
686
703
|
|
|
687
|
-
@property
|
|
688
|
-
def should_parse_structured_output(self) -> bool:
|
|
689
|
-
return self.output_schema is not None and self.parse_response and self.parser_model is None
|
|
690
|
-
|
|
691
704
|
@property
|
|
692
705
|
def cached_session(self) -> Optional[AgentSession]:
|
|
693
706
|
return self._cached_session
|
|
@@ -823,6 +836,19 @@ class Agent:
|
|
|
823
836
|
self.enable_session_summaries or self.session_summary_manager is not None
|
|
824
837
|
)
|
|
825
838
|
|
|
839
|
+
def _set_compression_manager(self) -> None:
|
|
840
|
+
if self.compress_tool_results and self.compression_manager is None:
|
|
841
|
+
self.compression_manager = CompressionManager(
|
|
842
|
+
model=self.model,
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
if self.compression_manager is not None and self.compression_manager.model is None:
|
|
846
|
+
self.compression_manager.model = self.model
|
|
847
|
+
|
|
848
|
+
# Check compression flag on the compression manager
|
|
849
|
+
if self.compression_manager is not None and self.compression_manager.compress_tool_results:
|
|
850
|
+
self.compress_tool_results = True
|
|
851
|
+
|
|
826
852
|
def _has_async_db(self) -> bool:
|
|
827
853
|
"""Return True if the db the agent is equipped with is an Async implementation"""
|
|
828
854
|
return self.db is not None and isinstance(self.db, AsyncBaseDb)
|
|
@@ -837,6 +863,9 @@ class Agent:
|
|
|
837
863
|
if self.output_model is not None:
|
|
838
864
|
self.output_model = get_model(self.output_model)
|
|
839
865
|
|
|
866
|
+
if self.compression_manager is not None and self.compression_manager.model is None:
|
|
867
|
+
self.compression_manager.model = self.model
|
|
868
|
+
|
|
840
869
|
def initialize_agent(self, debug_mode: Optional[bool] = None) -> None:
|
|
841
870
|
self._set_default_model()
|
|
842
871
|
self._set_debug(debug_mode=debug_mode)
|
|
@@ -852,6 +881,8 @@ class Agent:
|
|
|
852
881
|
self._set_culture_manager()
|
|
853
882
|
if self.enable_session_summaries or self.session_summary_manager is not None:
|
|
854
883
|
self._set_session_summary_manager()
|
|
884
|
+
if self.compress_tool_results or self.compression_manager is not None:
|
|
885
|
+
self._set_compression_manager()
|
|
855
886
|
|
|
856
887
|
log_debug(f"Agent ID: {self.id}", center=True)
|
|
857
888
|
|
|
@@ -870,10 +901,15 @@ class Agent:
|
|
|
870
901
|
"""Connect the MCP tools to the agent."""
|
|
871
902
|
if self.tools:
|
|
872
903
|
for tool in self.tools:
|
|
873
|
-
|
|
904
|
+
# Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
|
|
905
|
+
if (
|
|
906
|
+
hasattr(type(tool), "__mro__")
|
|
907
|
+
and any(c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__)
|
|
908
|
+
and not tool.initialized # type: ignore
|
|
909
|
+
):
|
|
874
910
|
# Connect the MCP server
|
|
875
911
|
await tool.connect() # type: ignore
|
|
876
|
-
self._mcp_tools_initialized_on_run.append(tool)
|
|
912
|
+
self._mcp_tools_initialized_on_run.append(tool) # type: ignore
|
|
877
913
|
|
|
878
914
|
async def _disconnect_mcp_tools(self) -> None:
|
|
879
915
|
"""Disconnect the MCP tools from the agent."""
|
|
@@ -1041,6 +1077,7 @@ class Agent:
|
|
|
1041
1077
|
|
|
1042
1078
|
# 6. Generate a response from the Model (includes running function calls)
|
|
1043
1079
|
self.model = cast(Model, self.model)
|
|
1080
|
+
|
|
1044
1081
|
model_response: ModelResponse = self.model.response(
|
|
1045
1082
|
messages=run_messages.messages,
|
|
1046
1083
|
tools=_tools,
|
|
@@ -1049,6 +1086,7 @@ class Agent:
|
|
|
1049
1086
|
response_format=response_format,
|
|
1050
1087
|
run_response=run_response,
|
|
1051
1088
|
send_media_to_model=self.send_media_to_model,
|
|
1089
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
1052
1090
|
)
|
|
1053
1091
|
|
|
1054
1092
|
# Check for cancellation after model call
|
|
@@ -1058,7 +1096,7 @@ class Agent:
|
|
|
1058
1096
|
self._generate_response_with_output_model(model_response, run_messages)
|
|
1059
1097
|
|
|
1060
1098
|
# If a parser model is provided, structure the response separately
|
|
1061
|
-
self._parse_response_with_parser_model(model_response, run_messages)
|
|
1099
|
+
self._parse_response_with_parser_model(model_response, run_messages, run_context=run_context)
|
|
1062
1100
|
|
|
1063
1101
|
# 7. Update the RunOutput with the model response
|
|
1064
1102
|
self._update_run_response(
|
|
@@ -1078,7 +1116,7 @@ class Agent:
|
|
|
1078
1116
|
store_media_util(run_response, model_response)
|
|
1079
1117
|
|
|
1080
1118
|
# 9. Convert the response to the structured format if needed
|
|
1081
|
-
self._convert_response_to_structured_format(run_response)
|
|
1119
|
+
self._convert_response_to_structured_format(run_response, run_context=run_context)
|
|
1082
1120
|
|
|
1083
1121
|
# 10. Execute post-hooks after output is generated but before response is returned
|
|
1084
1122
|
if self.post_hooks is not None:
|
|
@@ -1183,6 +1221,7 @@ class Agent:
|
|
|
1183
1221
|
session=session,
|
|
1184
1222
|
user_id=user_id,
|
|
1185
1223
|
debug_mode=debug_mode,
|
|
1224
|
+
stream_events=stream_events,
|
|
1186
1225
|
**kwargs,
|
|
1187
1226
|
)
|
|
1188
1227
|
for event in pre_hook_iterator:
|
|
@@ -1276,6 +1315,7 @@ class Agent:
|
|
|
1276
1315
|
response_format=response_format,
|
|
1277
1316
|
stream_events=stream_events,
|
|
1278
1317
|
session_state=run_context.session_state,
|
|
1318
|
+
run_context=run_context,
|
|
1279
1319
|
):
|
|
1280
1320
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1281
1321
|
yield event
|
|
@@ -1293,6 +1333,7 @@ class Agent:
|
|
|
1293
1333
|
response_format=response_format,
|
|
1294
1334
|
stream_events=stream_events,
|
|
1295
1335
|
session_state=run_context.session_state,
|
|
1336
|
+
run_context=run_context,
|
|
1296
1337
|
):
|
|
1297
1338
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1298
1339
|
if isinstance(event, RunContentEvent):
|
|
@@ -1319,7 +1360,7 @@ class Agent:
|
|
|
1319
1360
|
|
|
1320
1361
|
# 7. Parse response with parser model if provided
|
|
1321
1362
|
yield from self._parse_response_with_parser_model_stream(
|
|
1322
|
-
session=session, run_response=run_response, stream_events=stream_events
|
|
1363
|
+
session=session, run_response=run_response, stream_events=stream_events, run_context=run_context
|
|
1323
1364
|
)
|
|
1324
1365
|
|
|
1325
1366
|
# We should break out of the run function
|
|
@@ -1357,6 +1398,7 @@ class Agent:
|
|
|
1357
1398
|
session=session,
|
|
1358
1399
|
user_id=user_id,
|
|
1359
1400
|
debug_mode=debug_mode,
|
|
1401
|
+
stream_events=stream_events,
|
|
1360
1402
|
**kwargs,
|
|
1361
1403
|
)
|
|
1362
1404
|
|
|
@@ -1474,6 +1516,7 @@ class Agent:
|
|
|
1474
1516
|
add_session_state_to_context: Optional[bool] = None,
|
|
1475
1517
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1476
1518
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1519
|
+
output_schema: Optional[Type[BaseModel]] = None,
|
|
1477
1520
|
debug_mode: Optional[bool] = None,
|
|
1478
1521
|
**kwargs: Any,
|
|
1479
1522
|
) -> RunOutput: ...
|
|
@@ -1501,6 +1544,7 @@ class Agent:
|
|
|
1501
1544
|
add_session_state_to_context: Optional[bool] = None,
|
|
1502
1545
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1503
1546
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1547
|
+
output_schema: Optional[Type[BaseModel]] = None,
|
|
1504
1548
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
1505
1549
|
yield_run_output: bool = False,
|
|
1506
1550
|
debug_mode: Optional[bool] = None,
|
|
@@ -1529,6 +1573,7 @@ class Agent:
|
|
|
1529
1573
|
add_session_state_to_context: Optional[bool] = None,
|
|
1530
1574
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
1531
1575
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1576
|
+
output_schema: Optional[Type[BaseModel]] = None,
|
|
1532
1577
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
1533
1578
|
yield_run_output: Optional[bool] = None,
|
|
1534
1579
|
debug_mode: Optional[bool] = None,
|
|
@@ -1598,6 +1643,10 @@ class Agent:
|
|
|
1598
1643
|
# Determine runtime dependencies
|
|
1599
1644
|
dependencies = dependencies if dependencies is not None else self.dependencies
|
|
1600
1645
|
|
|
1646
|
+
# Resolve output_schema parameter takes precedence, then fall back to self.output_schema
|
|
1647
|
+
if output_schema is None:
|
|
1648
|
+
output_schema = self.output_schema
|
|
1649
|
+
|
|
1601
1650
|
# Initialize run context
|
|
1602
1651
|
run_context = run_context or RunContext(
|
|
1603
1652
|
run_id=run_id,
|
|
@@ -1605,7 +1654,10 @@ class Agent:
|
|
|
1605
1654
|
user_id=user_id,
|
|
1606
1655
|
session_state=session_state,
|
|
1607
1656
|
dependencies=dependencies,
|
|
1657
|
+
output_schema=output_schema,
|
|
1608
1658
|
)
|
|
1659
|
+
# output_schema parameter takes priority, even if run_context was provided
|
|
1660
|
+
run_context.output_schema = output_schema
|
|
1609
1661
|
|
|
1610
1662
|
# Resolve dependencies
|
|
1611
1663
|
if run_context.dependencies is not None:
|
|
@@ -1649,7 +1701,7 @@ class Agent:
|
|
|
1649
1701
|
self.stream_events = self.stream_events or stream_events
|
|
1650
1702
|
|
|
1651
1703
|
# Prepare arguments for the model
|
|
1652
|
-
response_format = self._get_response_format() if self.parser_model is None else None
|
|
1704
|
+
response_format = self._get_response_format(run_context=run_context) if self.parser_model is None else None
|
|
1653
1705
|
self.model = cast(Model, self.model)
|
|
1654
1706
|
|
|
1655
1707
|
# Merge agent metadata with run metadata
|
|
@@ -1910,6 +1962,7 @@ class Agent:
|
|
|
1910
1962
|
response_format=response_format,
|
|
1911
1963
|
send_media_to_model=self.send_media_to_model,
|
|
1912
1964
|
run_response=run_response,
|
|
1965
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
1913
1966
|
)
|
|
1914
1967
|
|
|
1915
1968
|
# Check for cancellation after model call
|
|
@@ -1919,13 +1972,16 @@ class Agent:
|
|
|
1919
1972
|
await self._agenerate_response_with_output_model(model_response=model_response, run_messages=run_messages)
|
|
1920
1973
|
|
|
1921
1974
|
# If a parser model is provided, structure the response separately
|
|
1922
|
-
await self._aparse_response_with_parser_model(
|
|
1975
|
+
await self._aparse_response_with_parser_model(
|
|
1976
|
+
model_response=model_response, run_messages=run_messages, run_context=run_context
|
|
1977
|
+
)
|
|
1923
1978
|
|
|
1924
1979
|
# 10. Update the RunOutput with the model response
|
|
1925
1980
|
self._update_run_response(
|
|
1926
1981
|
model_response=model_response,
|
|
1927
1982
|
run_response=run_response,
|
|
1928
1983
|
run_messages=run_messages,
|
|
1984
|
+
run_context=run_context,
|
|
1929
1985
|
)
|
|
1930
1986
|
|
|
1931
1987
|
# We should break out of the run function
|
|
@@ -1938,7 +1994,7 @@ class Agent:
|
|
|
1938
1994
|
)
|
|
1939
1995
|
|
|
1940
1996
|
# 11. Convert the response to the structured format if needed
|
|
1941
|
-
self._convert_response_to_structured_format(run_response)
|
|
1997
|
+
self._convert_response_to_structured_format(run_response, run_context=run_context)
|
|
1942
1998
|
|
|
1943
1999
|
# 12. Store media if enabled
|
|
1944
2000
|
if self.store_media:
|
|
@@ -2105,6 +2161,7 @@ class Agent:
|
|
|
2105
2161
|
session=agent_session,
|
|
2106
2162
|
user_id=user_id,
|
|
2107
2163
|
debug_mode=debug_mode,
|
|
2164
|
+
stream_events=stream_events,
|
|
2108
2165
|
**kwargs,
|
|
2109
2166
|
)
|
|
2110
2167
|
async for event in pre_hook_iterator:
|
|
@@ -2188,6 +2245,7 @@ class Agent:
|
|
|
2188
2245
|
response_format=response_format,
|
|
2189
2246
|
stream_events=stream_events,
|
|
2190
2247
|
session_state=run_context.session_state,
|
|
2248
|
+
run_context=run_context,
|
|
2191
2249
|
):
|
|
2192
2250
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2193
2251
|
yield event
|
|
@@ -2205,6 +2263,7 @@ class Agent:
|
|
|
2205
2263
|
response_format=response_format,
|
|
2206
2264
|
stream_events=stream_events,
|
|
2207
2265
|
session_state=run_context.session_state,
|
|
2266
|
+
run_context=run_context,
|
|
2208
2267
|
):
|
|
2209
2268
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2210
2269
|
if isinstance(event, RunContentEvent):
|
|
@@ -2231,7 +2290,7 @@ class Agent:
|
|
|
2231
2290
|
|
|
2232
2291
|
# 10. Parse response with parser model if provided
|
|
2233
2292
|
async for event in self._aparse_response_with_parser_model_stream(
|
|
2234
|
-
session=agent_session, run_response=run_response, stream_events=stream_events
|
|
2293
|
+
session=agent_session, run_response=run_response, stream_events=stream_events, run_context=run_context
|
|
2235
2294
|
):
|
|
2236
2295
|
yield event
|
|
2237
2296
|
|
|
@@ -2268,6 +2327,7 @@ class Agent:
|
|
|
2268
2327
|
session=agent_session,
|
|
2269
2328
|
user_id=user_id,
|
|
2270
2329
|
debug_mode=debug_mode,
|
|
2330
|
+
stream_events=stream_events,
|
|
2271
2331
|
**kwargs,
|
|
2272
2332
|
):
|
|
2273
2333
|
yield event
|
|
@@ -2413,6 +2473,7 @@ class Agent:
|
|
|
2413
2473
|
add_session_state_to_context: Optional[bool] = None,
|
|
2414
2474
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2415
2475
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2476
|
+
output_schema: Optional[Type[BaseModel]] = None,
|
|
2416
2477
|
debug_mode: Optional[bool] = None,
|
|
2417
2478
|
**kwargs: Any,
|
|
2418
2479
|
) -> RunOutput: ...
|
|
@@ -2439,6 +2500,7 @@ class Agent:
|
|
|
2439
2500
|
add_session_state_to_context: Optional[bool] = None,
|
|
2440
2501
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2441
2502
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2503
|
+
output_schema: Optional[Type[BaseModel]] = None,
|
|
2442
2504
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2443
2505
|
yield_run_output: Optional[bool] = None,
|
|
2444
2506
|
debug_mode: Optional[bool] = None,
|
|
@@ -2467,6 +2529,7 @@ class Agent:
|
|
|
2467
2529
|
add_session_state_to_context: Optional[bool] = None,
|
|
2468
2530
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2469
2531
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2532
|
+
output_schema: Optional[Type[BaseModel]] = None,
|
|
2470
2533
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2471
2534
|
yield_run_output: Optional[bool] = None,
|
|
2472
2535
|
debug_mode: Optional[bool] = None,
|
|
@@ -2554,8 +2617,6 @@ class Agent:
|
|
|
2554
2617
|
self.stream = self.stream or stream
|
|
2555
2618
|
self.stream_events = self.stream_events or stream_events
|
|
2556
2619
|
|
|
2557
|
-
# Prepare arguments for the model
|
|
2558
|
-
response_format = self._get_response_format() if self.parser_model is None else None
|
|
2559
2620
|
self.model = cast(Model, self.model)
|
|
2560
2621
|
|
|
2561
2622
|
# Get knowledge filters
|
|
@@ -2570,6 +2631,10 @@ class Agent:
|
|
|
2570
2631
|
else:
|
|
2571
2632
|
merge_dictionaries(metadata, self.metadata)
|
|
2572
2633
|
|
|
2634
|
+
# Resolve output_schema parameter takes precedence, then fall back to self.output_schema
|
|
2635
|
+
if output_schema is None:
|
|
2636
|
+
output_schema = self.output_schema
|
|
2637
|
+
|
|
2573
2638
|
# Initialize run context
|
|
2574
2639
|
run_context = run_context or RunContext(
|
|
2575
2640
|
run_id=run_id,
|
|
@@ -2579,7 +2644,13 @@ class Agent:
|
|
|
2579
2644
|
dependencies=dependencies,
|
|
2580
2645
|
knowledge_filters=knowledge_filters,
|
|
2581
2646
|
metadata=metadata,
|
|
2647
|
+
output_schema=output_schema,
|
|
2582
2648
|
)
|
|
2649
|
+
# output_schema parameter takes priority, even if run_context was provided
|
|
2650
|
+
run_context.output_schema = output_schema
|
|
2651
|
+
|
|
2652
|
+
# Prepare arguments for the model (must be after run_context is fully initialized)
|
|
2653
|
+
response_format = self._get_response_format(run_context=run_context) if self.parser_model is None else None
|
|
2583
2654
|
|
|
2584
2655
|
# If no retries are set, use the agent's default retries
|
|
2585
2656
|
retries = retries if retries is not None else self.retries
|
|
@@ -2866,7 +2937,7 @@ class Agent:
|
|
|
2866
2937
|
|
|
2867
2938
|
# Prepare arguments for the model
|
|
2868
2939
|
self._set_default_model()
|
|
2869
|
-
response_format = self._get_response_format()
|
|
2940
|
+
response_format = self._get_response_format(run_context=run_context)
|
|
2870
2941
|
self.model = cast(Model, self.model)
|
|
2871
2942
|
|
|
2872
2943
|
processed_tools = self.get_tools(
|
|
@@ -3023,7 +3094,7 @@ class Agent:
|
|
|
3023
3094
|
return self._handle_agent_run_paused(run_response=run_response, session=session, user_id=user_id)
|
|
3024
3095
|
|
|
3025
3096
|
# 4. Convert the response to the structured format if needed
|
|
3026
|
-
self._convert_response_to_structured_format(run_response)
|
|
3097
|
+
self._convert_response_to_structured_format(run_response, run_context=run_context)
|
|
3027
3098
|
|
|
3028
3099
|
# 5. Store media if enabled
|
|
3029
3100
|
if self.store_media:
|
|
@@ -3134,6 +3205,7 @@ class Agent:
|
|
|
3134
3205
|
response_format=response_format,
|
|
3135
3206
|
stream_events=stream_events,
|
|
3136
3207
|
session_state=run_context.session_state,
|
|
3208
|
+
run_context=run_context,
|
|
3137
3209
|
):
|
|
3138
3210
|
yield event
|
|
3139
3211
|
|
|
@@ -3167,6 +3239,7 @@ class Agent:
|
|
|
3167
3239
|
run_context=run_context,
|
|
3168
3240
|
user_id=user_id,
|
|
3169
3241
|
debug_mode=debug_mode,
|
|
3242
|
+
stream_events=stream_events,
|
|
3170
3243
|
**kwargs,
|
|
3171
3244
|
)
|
|
3172
3245
|
|
|
@@ -3388,7 +3461,7 @@ class Agent:
|
|
|
3388
3461
|
merge_dictionaries(metadata, self.metadata)
|
|
3389
3462
|
|
|
3390
3463
|
# Prepare arguments for the model
|
|
3391
|
-
response_format = self._get_response_format()
|
|
3464
|
+
response_format = self._get_response_format(run_context=run_context)
|
|
3392
3465
|
self.model = cast(Model, self.model)
|
|
3393
3466
|
|
|
3394
3467
|
# Initialize run context
|
|
@@ -3585,13 +3658,16 @@ class Agent:
|
|
|
3585
3658
|
await self._agenerate_response_with_output_model(model_response=model_response, run_messages=run_messages)
|
|
3586
3659
|
|
|
3587
3660
|
# If a parser model is provided, structure the response separately
|
|
3588
|
-
await self._aparse_response_with_parser_model(
|
|
3661
|
+
await self._aparse_response_with_parser_model(
|
|
3662
|
+
model_response=model_response, run_messages=run_messages, run_context=run_context
|
|
3663
|
+
)
|
|
3589
3664
|
|
|
3590
3665
|
# 9. Update the RunOutput with the model response
|
|
3591
3666
|
self._update_run_response(
|
|
3592
3667
|
model_response=model_response,
|
|
3593
3668
|
run_response=run_response,
|
|
3594
3669
|
run_messages=run_messages,
|
|
3670
|
+
run_context=run_context,
|
|
3595
3671
|
)
|
|
3596
3672
|
|
|
3597
3673
|
# Break out of the run function if a tool call is paused
|
|
@@ -3601,7 +3677,7 @@ class Agent:
|
|
|
3601
3677
|
)
|
|
3602
3678
|
|
|
3603
3679
|
# 10. Convert the response to the structured format if needed
|
|
3604
|
-
self._convert_response_to_structured_format(run_response)
|
|
3680
|
+
self._convert_response_to_structured_format(run_response, run_context=run_context)
|
|
3605
3681
|
|
|
3606
3682
|
# 11. Store media if enabled
|
|
3607
3683
|
if self.store_media:
|
|
@@ -3797,6 +3873,7 @@ class Agent:
|
|
|
3797
3873
|
tools=_tools,
|
|
3798
3874
|
response_format=response_format,
|
|
3799
3875
|
stream_events=stream_events,
|
|
3876
|
+
run_context=run_context,
|
|
3800
3877
|
):
|
|
3801
3878
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
3802
3879
|
yield event
|
|
@@ -3813,6 +3890,7 @@ class Agent:
|
|
|
3813
3890
|
tools=_tools,
|
|
3814
3891
|
response_format=response_format,
|
|
3815
3892
|
stream_events=stream_events,
|
|
3893
|
+
run_context=run_context,
|
|
3816
3894
|
):
|
|
3817
3895
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
3818
3896
|
if isinstance(event, RunContentEvent):
|
|
@@ -3839,7 +3917,7 @@ class Agent:
|
|
|
3839
3917
|
|
|
3840
3918
|
# Parse response with parser model if provided
|
|
3841
3919
|
async for event in self._aparse_response_with_parser_model_stream(
|
|
3842
|
-
session=agent_session, run_response=run_response, stream_events=stream_events
|
|
3920
|
+
session=agent_session, run_response=run_response, stream_events=stream_events, run_context=run_context
|
|
3843
3921
|
):
|
|
3844
3922
|
yield event
|
|
3845
3923
|
|
|
@@ -3869,6 +3947,7 @@ class Agent:
|
|
|
3869
3947
|
session=agent_session,
|
|
3870
3948
|
user_id=user_id,
|
|
3871
3949
|
debug_mode=debug_mode,
|
|
3950
|
+
stream_events=stream_events,
|
|
3872
3951
|
**kwargs,
|
|
3873
3952
|
):
|
|
3874
3953
|
yield event
|
|
@@ -3969,6 +4048,7 @@ class Agent:
|
|
|
3969
4048
|
run_context: RunContext,
|
|
3970
4049
|
user_id: Optional[str] = None,
|
|
3971
4050
|
debug_mode: Optional[bool] = None,
|
|
4051
|
+
stream_events: bool = False,
|
|
3972
4052
|
**kwargs: Any,
|
|
3973
4053
|
) -> Iterator[RunOutputEvent]:
|
|
3974
4054
|
"""Execute multiple pre-hook functions in succession."""
|
|
@@ -3990,25 +4070,10 @@ class Agent:
|
|
|
3990
4070
|
all_args.update(kwargs)
|
|
3991
4071
|
|
|
3992
4072
|
for i, hook in enumerate(hooks):
|
|
3993
|
-
|
|
3994
|
-
run_response=run_response,
|
|
3995
|
-
event=create_pre_hook_started_event(
|
|
3996
|
-
from_run_response=run_response,
|
|
3997
|
-
run_input=run_input,
|
|
3998
|
-
pre_hook_name=hook.__name__,
|
|
3999
|
-
),
|
|
4000
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
4001
|
-
store_events=self.store_events,
|
|
4002
|
-
)
|
|
4003
|
-
try:
|
|
4004
|
-
# Filter arguments to only include those that the hook accepts
|
|
4005
|
-
filtered_args = filter_hook_args(hook, all_args)
|
|
4006
|
-
|
|
4007
|
-
hook(**filtered_args)
|
|
4008
|
-
|
|
4073
|
+
if stream_events:
|
|
4009
4074
|
yield handle_event( # type: ignore
|
|
4010
4075
|
run_response=run_response,
|
|
4011
|
-
event=
|
|
4076
|
+
event=create_pre_hook_started_event(
|
|
4012
4077
|
from_run_response=run_response,
|
|
4013
4078
|
run_input=run_input,
|
|
4014
4079
|
pre_hook_name=hook.__name__,
|
|
@@ -4016,6 +4081,23 @@ class Agent:
|
|
|
4016
4081
|
events_to_skip=self.events_to_skip, # type: ignore
|
|
4017
4082
|
store_events=self.store_events,
|
|
4018
4083
|
)
|
|
4084
|
+
try:
|
|
4085
|
+
# Filter arguments to only include those that the hook accepts
|
|
4086
|
+
filtered_args = filter_hook_args(hook, all_args)
|
|
4087
|
+
|
|
4088
|
+
hook(**filtered_args)
|
|
4089
|
+
|
|
4090
|
+
if stream_events:
|
|
4091
|
+
yield handle_event( # type: ignore
|
|
4092
|
+
run_response=run_response,
|
|
4093
|
+
event=create_pre_hook_completed_event(
|
|
4094
|
+
from_run_response=run_response,
|
|
4095
|
+
run_input=run_input,
|
|
4096
|
+
pre_hook_name=hook.__name__,
|
|
4097
|
+
),
|
|
4098
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
4099
|
+
store_events=self.store_events,
|
|
4100
|
+
)
|
|
4019
4101
|
|
|
4020
4102
|
except (InputCheckError, OutputCheckError) as e:
|
|
4021
4103
|
raise e
|
|
@@ -4038,6 +4120,7 @@ class Agent:
|
|
|
4038
4120
|
session: AgentSession,
|
|
4039
4121
|
user_id: Optional[str] = None,
|
|
4040
4122
|
debug_mode: Optional[bool] = None,
|
|
4123
|
+
stream_events: bool = False,
|
|
4041
4124
|
**kwargs: Any,
|
|
4042
4125
|
) -> AsyncIterator[RunOutputEvent]:
|
|
4043
4126
|
"""Execute multiple pre-hook functions in succession (async version)."""
|
|
@@ -4059,16 +4142,17 @@ class Agent:
|
|
|
4059
4142
|
all_args.update(kwargs)
|
|
4060
4143
|
|
|
4061
4144
|
for i, hook in enumerate(hooks):
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4145
|
+
if stream_events:
|
|
4146
|
+
yield handle_event( # type: ignore
|
|
4147
|
+
run_response=run_response,
|
|
4148
|
+
event=create_pre_hook_started_event(
|
|
4149
|
+
from_run_response=run_response,
|
|
4150
|
+
run_input=run_input,
|
|
4151
|
+
pre_hook_name=hook.__name__,
|
|
4152
|
+
),
|
|
4153
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
4154
|
+
store_events=self.store_events,
|
|
4155
|
+
)
|
|
4072
4156
|
try:
|
|
4073
4157
|
# Filter arguments to only include those that the hook accepts
|
|
4074
4158
|
filtered_args = filter_hook_args(hook, all_args)
|
|
@@ -4079,16 +4163,17 @@ class Agent:
|
|
|
4079
4163
|
# Synchronous function
|
|
4080
4164
|
hook(**filtered_args)
|
|
4081
4165
|
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4166
|
+
if stream_events:
|
|
4167
|
+
yield handle_event( # type: ignore
|
|
4168
|
+
run_response=run_response,
|
|
4169
|
+
event=create_pre_hook_completed_event(
|
|
4170
|
+
from_run_response=run_response,
|
|
4171
|
+
run_input=run_input,
|
|
4172
|
+
pre_hook_name=hook.__name__,
|
|
4173
|
+
),
|
|
4174
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
4175
|
+
store_events=self.store_events,
|
|
4176
|
+
)
|
|
4092
4177
|
|
|
4093
4178
|
except (InputCheckError, OutputCheckError) as e:
|
|
4094
4179
|
raise e
|
|
@@ -4110,6 +4195,7 @@ class Agent:
|
|
|
4110
4195
|
run_context: RunContext,
|
|
4111
4196
|
user_id: Optional[str] = None,
|
|
4112
4197
|
debug_mode: Optional[bool] = None,
|
|
4198
|
+
stream_events: bool = False,
|
|
4113
4199
|
**kwargs: Any,
|
|
4114
4200
|
) -> Iterator[RunOutputEvent]:
|
|
4115
4201
|
"""Execute multiple post-hook functions in succession."""
|
|
@@ -4131,30 +4217,32 @@ class Agent:
|
|
|
4131
4217
|
all_args.update(kwargs)
|
|
4132
4218
|
|
|
4133
4219
|
for i, hook in enumerate(hooks):
|
|
4134
|
-
|
|
4135
|
-
run_response=run_output,
|
|
4136
|
-
event=create_post_hook_started_event(
|
|
4137
|
-
from_run_response=run_output,
|
|
4138
|
-
post_hook_name=hook.__name__,
|
|
4139
|
-
),
|
|
4140
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
4141
|
-
store_events=self.store_events,
|
|
4142
|
-
)
|
|
4143
|
-
try:
|
|
4144
|
-
# Filter arguments to only include those that the hook accepts
|
|
4145
|
-
filtered_args = filter_hook_args(hook, all_args)
|
|
4146
|
-
|
|
4147
|
-
hook(**filtered_args)
|
|
4148
|
-
|
|
4220
|
+
if stream_events:
|
|
4149
4221
|
yield handle_event( # type: ignore
|
|
4150
4222
|
run_response=run_output,
|
|
4151
|
-
event=
|
|
4223
|
+
event=create_post_hook_started_event(
|
|
4152
4224
|
from_run_response=run_output,
|
|
4153
4225
|
post_hook_name=hook.__name__,
|
|
4154
4226
|
),
|
|
4155
4227
|
events_to_skip=self.events_to_skip, # type: ignore
|
|
4156
4228
|
store_events=self.store_events,
|
|
4157
4229
|
)
|
|
4230
|
+
try:
|
|
4231
|
+
# Filter arguments to only include those that the hook accepts
|
|
4232
|
+
filtered_args = filter_hook_args(hook, all_args)
|
|
4233
|
+
|
|
4234
|
+
hook(**filtered_args)
|
|
4235
|
+
|
|
4236
|
+
if stream_events:
|
|
4237
|
+
yield handle_event( # type: ignore
|
|
4238
|
+
run_response=run_output,
|
|
4239
|
+
event=create_post_hook_completed_event(
|
|
4240
|
+
from_run_response=run_output,
|
|
4241
|
+
post_hook_name=hook.__name__,
|
|
4242
|
+
),
|
|
4243
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
4244
|
+
store_events=self.store_events,
|
|
4245
|
+
)
|
|
4158
4246
|
except (InputCheckError, OutputCheckError) as e:
|
|
4159
4247
|
raise e
|
|
4160
4248
|
except Exception as e:
|
|
@@ -4172,6 +4260,7 @@ class Agent:
|
|
|
4172
4260
|
session: AgentSession,
|
|
4173
4261
|
user_id: Optional[str] = None,
|
|
4174
4262
|
debug_mode: Optional[bool] = None,
|
|
4263
|
+
stream_events: bool = False,
|
|
4175
4264
|
**kwargs: Any,
|
|
4176
4265
|
) -> AsyncIterator[RunOutputEvent]:
|
|
4177
4266
|
"""Execute multiple post-hook functions in succession (async version)."""
|
|
@@ -4193,15 +4282,16 @@ class Agent:
|
|
|
4193
4282
|
all_args.update(kwargs)
|
|
4194
4283
|
|
|
4195
4284
|
for i, hook in enumerate(hooks):
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4285
|
+
if stream_events:
|
|
4286
|
+
yield handle_event( # type: ignore
|
|
4287
|
+
run_response=run_output,
|
|
4288
|
+
event=create_post_hook_started_event(
|
|
4289
|
+
from_run_response=run_output,
|
|
4290
|
+
post_hook_name=hook.__name__,
|
|
4291
|
+
),
|
|
4292
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
4293
|
+
store_events=self.store_events,
|
|
4294
|
+
)
|
|
4205
4295
|
try:
|
|
4206
4296
|
# Filter arguments to only include those that the hook accepts
|
|
4207
4297
|
filtered_args = filter_hook_args(hook, all_args)
|
|
@@ -4212,15 +4302,16 @@ class Agent:
|
|
|
4212
4302
|
else:
|
|
4213
4303
|
hook(**filtered_args)
|
|
4214
4304
|
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4305
|
+
if stream_events:
|
|
4306
|
+
yield handle_event( # type: ignore
|
|
4307
|
+
run_response=run_output,
|
|
4308
|
+
event=create_post_hook_completed_event(
|
|
4309
|
+
from_run_response=run_output,
|
|
4310
|
+
post_hook_name=hook.__name__,
|
|
4311
|
+
),
|
|
4312
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
4313
|
+
store_events=self.store_events,
|
|
4314
|
+
)
|
|
4224
4315
|
|
|
4225
4316
|
except (InputCheckError, OutputCheckError) as e:
|
|
4226
4317
|
raise e
|
|
@@ -4327,18 +4418,23 @@ class Agent:
|
|
|
4327
4418
|
|
|
4328
4419
|
log_debug(f"Agent Run Paused: {run_response.run_id}", center=True, symbol="*")
|
|
4329
4420
|
|
|
4330
|
-
def _convert_response_to_structured_format(
|
|
4421
|
+
def _convert_response_to_structured_format(
|
|
4422
|
+
self, run_response: Union[RunOutput, ModelResponse], run_context: Optional[RunContext] = None
|
|
4423
|
+
):
|
|
4424
|
+
# Get output_schema from run_context
|
|
4425
|
+
output_schema = run_context.output_schema if run_context else None
|
|
4426
|
+
|
|
4331
4427
|
# Convert the response to the structured format if needed
|
|
4332
|
-
if
|
|
4428
|
+
if output_schema is not None and not isinstance(run_response.content, output_schema):
|
|
4333
4429
|
if isinstance(run_response.content, str) and self.parse_response:
|
|
4334
4430
|
try:
|
|
4335
|
-
structured_output = parse_response_model_str(run_response.content,
|
|
4431
|
+
structured_output = parse_response_model_str(run_response.content, output_schema)
|
|
4336
4432
|
|
|
4337
4433
|
# Update RunOutput
|
|
4338
4434
|
if structured_output is not None:
|
|
4339
4435
|
run_response.content = structured_output
|
|
4340
4436
|
if isinstance(run_response, RunOutput):
|
|
4341
|
-
run_response.content_type =
|
|
4437
|
+
run_response.content_type = output_schema.__name__
|
|
4342
4438
|
else:
|
|
4343
4439
|
log_warning("Failed to convert response to output_schema")
|
|
4344
4440
|
except Exception as e:
|
|
@@ -4681,15 +4777,19 @@ class Agent:
|
|
|
4681
4777
|
model_response: ModelResponse,
|
|
4682
4778
|
run_response: RunOutput,
|
|
4683
4779
|
run_messages: RunMessages,
|
|
4780
|
+
run_context: Optional[RunContext] = None,
|
|
4684
4781
|
):
|
|
4782
|
+
# Get output_schema from run_context
|
|
4783
|
+
output_schema = run_context.output_schema if run_context else None
|
|
4784
|
+
|
|
4685
4785
|
# Handle structured outputs
|
|
4686
|
-
if
|
|
4786
|
+
if output_schema is not None and model_response.parsed is not None:
|
|
4687
4787
|
# We get native structured outputs from the model
|
|
4688
|
-
if self._model_should_return_structured_output():
|
|
4788
|
+
if self._model_should_return_structured_output(run_context=run_context):
|
|
4689
4789
|
# Update the run_response content with the structured output
|
|
4690
4790
|
run_response.content = model_response.parsed
|
|
4691
4791
|
# Update the run_response content_type with the structured output class name
|
|
4692
|
-
run_response.content_type =
|
|
4792
|
+
run_response.content_type = output_schema.__name__
|
|
4693
4793
|
else:
|
|
4694
4794
|
# Update the run_response content with the model response content
|
|
4695
4795
|
run_response.content = model_response.content
|
|
@@ -4762,6 +4862,7 @@ class Agent:
|
|
|
4762
4862
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
4763
4863
|
stream_events: bool = False,
|
|
4764
4864
|
session_state: Optional[Dict[str, Any]] = None,
|
|
4865
|
+
run_context: Optional[RunContext] = None,
|
|
4765
4866
|
) -> Iterator[RunOutputEvent]:
|
|
4766
4867
|
self.model = cast(Model, self.model)
|
|
4767
4868
|
|
|
@@ -4771,8 +4872,12 @@ class Agent:
|
|
|
4771
4872
|
}
|
|
4772
4873
|
model_response = ModelResponse(content="")
|
|
4773
4874
|
|
|
4875
|
+
# Get output_schema from run_context
|
|
4876
|
+
output_schema = run_context.output_schema if run_context else None
|
|
4877
|
+
should_parse_structured_output = output_schema is not None and self.parse_response and self.parser_model is None
|
|
4878
|
+
|
|
4774
4879
|
stream_model_response = True
|
|
4775
|
-
if
|
|
4880
|
+
if should_parse_structured_output:
|
|
4776
4881
|
log_debug("Response model set, model response is not streamed.")
|
|
4777
4882
|
stream_model_response = False
|
|
4778
4883
|
|
|
@@ -4785,6 +4890,7 @@ class Agent:
|
|
|
4785
4890
|
stream_model_response=stream_model_response,
|
|
4786
4891
|
run_response=run_response,
|
|
4787
4892
|
send_media_to_model=self.send_media_to_model,
|
|
4893
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
4788
4894
|
):
|
|
4789
4895
|
yield from self._handle_model_response_chunk(
|
|
4790
4896
|
session=session,
|
|
@@ -4792,9 +4898,10 @@ class Agent:
|
|
|
4792
4898
|
model_response=model_response,
|
|
4793
4899
|
model_response_event=model_response_event,
|
|
4794
4900
|
reasoning_state=reasoning_state,
|
|
4795
|
-
parse_structured_output=
|
|
4901
|
+
parse_structured_output=should_parse_structured_output,
|
|
4796
4902
|
stream_events=stream_events,
|
|
4797
4903
|
session_state=session_state,
|
|
4904
|
+
run_context=run_context,
|
|
4798
4905
|
)
|
|
4799
4906
|
|
|
4800
4907
|
# Determine reasoning completed
|
|
@@ -4842,6 +4949,7 @@ class Agent:
|
|
|
4842
4949
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
4843
4950
|
stream_events: bool = False,
|
|
4844
4951
|
session_state: Optional[Dict[str, Any]] = None,
|
|
4952
|
+
run_context: Optional[RunContext] = None,
|
|
4845
4953
|
) -> AsyncIterator[RunOutputEvent]:
|
|
4846
4954
|
self.model = cast(Model, self.model)
|
|
4847
4955
|
|
|
@@ -4851,8 +4959,12 @@ class Agent:
|
|
|
4851
4959
|
}
|
|
4852
4960
|
model_response = ModelResponse(content="")
|
|
4853
4961
|
|
|
4962
|
+
# Get output_schema from run_context
|
|
4963
|
+
output_schema = run_context.output_schema if run_context else None
|
|
4964
|
+
should_parse_structured_output = output_schema is not None and self.parse_response and self.parser_model is None
|
|
4965
|
+
|
|
4854
4966
|
stream_model_response = True
|
|
4855
|
-
if
|
|
4967
|
+
if should_parse_structured_output:
|
|
4856
4968
|
log_debug("Response model set, model response is not streamed.")
|
|
4857
4969
|
stream_model_response = False
|
|
4858
4970
|
|
|
@@ -4865,6 +4977,7 @@ class Agent:
|
|
|
4865
4977
|
stream_model_response=stream_model_response,
|
|
4866
4978
|
run_response=run_response,
|
|
4867
4979
|
send_media_to_model=self.send_media_to_model,
|
|
4980
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
4868
4981
|
) # type: ignore
|
|
4869
4982
|
|
|
4870
4983
|
async for model_response_event in model_response_stream: # type: ignore
|
|
@@ -4874,9 +4987,10 @@ class Agent:
|
|
|
4874
4987
|
model_response=model_response,
|
|
4875
4988
|
model_response_event=model_response_event,
|
|
4876
4989
|
reasoning_state=reasoning_state,
|
|
4877
|
-
parse_structured_output=
|
|
4990
|
+
parse_structured_output=should_parse_structured_output,
|
|
4878
4991
|
stream_events=stream_events,
|
|
4879
4992
|
session_state=session_state,
|
|
4993
|
+
run_context=run_context,
|
|
4880
4994
|
):
|
|
4881
4995
|
yield event
|
|
4882
4996
|
|
|
@@ -4925,6 +5039,7 @@ class Agent:
|
|
|
4925
5039
|
parse_structured_output: bool = False,
|
|
4926
5040
|
stream_events: bool = False,
|
|
4927
5041
|
session_state: Optional[Dict[str, Any]] = None,
|
|
5042
|
+
run_context: Optional[RunContext] = None,
|
|
4928
5043
|
) -> Iterator[RunOutputEvent]:
|
|
4929
5044
|
from agno.run.workflow import WorkflowRunOutputEvent
|
|
4930
5045
|
|
|
@@ -4956,9 +5071,11 @@ class Agent:
|
|
|
4956
5071
|
if model_response_event.content is not None:
|
|
4957
5072
|
if parse_structured_output:
|
|
4958
5073
|
model_response.content = model_response_event.content
|
|
4959
|
-
self._convert_response_to_structured_format(model_response)
|
|
5074
|
+
self._convert_response_to_structured_format(model_response, run_context=run_context)
|
|
4960
5075
|
|
|
4961
|
-
|
|
5076
|
+
# Get output_schema from run_context
|
|
5077
|
+
output_schema = run_context.output_schema if run_context else None
|
|
5078
|
+
content_type = output_schema.__name__ # type: ignore
|
|
4962
5079
|
run_response.content = model_response.content
|
|
4963
5080
|
run_response.content_type = content_type
|
|
4964
5081
|
else:
|
|
@@ -5472,7 +5589,12 @@ class Agent:
|
|
|
5472
5589
|
# Add provided tools
|
|
5473
5590
|
if self.tools is not None:
|
|
5474
5591
|
for tool in self.tools:
|
|
5475
|
-
|
|
5592
|
+
# Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
|
|
5593
|
+
is_mcp_tool = hasattr(type(tool), "__mro__") and any(
|
|
5594
|
+
c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__
|
|
5595
|
+
)
|
|
5596
|
+
|
|
5597
|
+
if is_mcp_tool:
|
|
5476
5598
|
if tool.refresh_connection: # type: ignore
|
|
5477
5599
|
try:
|
|
5478
5600
|
is_alive = await tool.is_alive() # type: ignore
|
|
@@ -5492,9 +5614,8 @@ class Agent:
|
|
|
5492
5614
|
if check_mcp_tools and not tool.initialized: # type: ignore
|
|
5493
5615
|
continue
|
|
5494
5616
|
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
agent_tools.append(tool)
|
|
5617
|
+
# Add the tool (MCP tools that passed checks, or any non-MCP tool)
|
|
5618
|
+
agent_tools.append(tool)
|
|
5498
5619
|
|
|
5499
5620
|
# Add tools for accessing memory
|
|
5500
5621
|
if self.read_chat_history:
|
|
@@ -5556,10 +5677,13 @@ class Agent:
|
|
|
5556
5677
|
if processed_tools is not None and len(processed_tools) > 0:
|
|
5557
5678
|
log_debug("Processing tools for model")
|
|
5558
5679
|
|
|
5680
|
+
# Get output_schema from run_context
|
|
5681
|
+
output_schema = run_context.output_schema if run_context else None
|
|
5682
|
+
|
|
5559
5683
|
# Check if we need strict mode for the functions for the model
|
|
5560
5684
|
strict = False
|
|
5561
5685
|
if (
|
|
5562
|
-
|
|
5686
|
+
output_schema is not None
|
|
5563
5687
|
and (self.structured_outputs or (not self.use_json_mode))
|
|
5564
5688
|
and model.supports_native_structured_outputs
|
|
5565
5689
|
):
|
|
@@ -5661,17 +5785,25 @@ class Agent:
|
|
|
5661
5785
|
|
|
5662
5786
|
return _functions
|
|
5663
5787
|
|
|
5664
|
-
def _model_should_return_structured_output(self):
|
|
5788
|
+
def _model_should_return_structured_output(self, run_context: Optional[RunContext] = None):
|
|
5789
|
+
# Get output_schema from run_context
|
|
5790
|
+
output_schema = run_context.output_schema if run_context else None
|
|
5791
|
+
|
|
5665
5792
|
self.model = cast(Model, self.model)
|
|
5666
5793
|
return bool(
|
|
5667
5794
|
self.model.supports_native_structured_outputs
|
|
5668
|
-
and
|
|
5795
|
+
and output_schema is not None
|
|
5669
5796
|
and (not self.use_json_mode or self.structured_outputs)
|
|
5670
5797
|
)
|
|
5671
5798
|
|
|
5672
|
-
def _get_response_format(
|
|
5799
|
+
def _get_response_format(
|
|
5800
|
+
self, model: Optional[Model] = None, run_context: Optional[RunContext] = None
|
|
5801
|
+
) -> Optional[Union[Dict, Type[BaseModel]]]:
|
|
5802
|
+
# Get output_schema from run_context
|
|
5803
|
+
output_schema = run_context.output_schema if run_context else None
|
|
5804
|
+
|
|
5673
5805
|
model = cast(Model, model or self.model)
|
|
5674
|
-
if
|
|
5806
|
+
if output_schema is None:
|
|
5675
5807
|
return None
|
|
5676
5808
|
else:
|
|
5677
5809
|
json_response_format = {"type": "json_object"}
|
|
@@ -5679,7 +5811,7 @@ class Agent:
|
|
|
5679
5811
|
if model.supports_native_structured_outputs:
|
|
5680
5812
|
if not self.use_json_mode or self.structured_outputs:
|
|
5681
5813
|
log_debug("Setting Model.response_format to Agent.output_schema")
|
|
5682
|
-
return
|
|
5814
|
+
return output_schema
|
|
5683
5815
|
else:
|
|
5684
5816
|
log_debug(
|
|
5685
5817
|
"Model supports native structured outputs but it is not enabled. Using JSON mode instead."
|
|
@@ -5692,8 +5824,8 @@ class Agent:
|
|
|
5692
5824
|
return {
|
|
5693
5825
|
"type": "json_schema",
|
|
5694
5826
|
"json_schema": {
|
|
5695
|
-
"name":
|
|
5696
|
-
"schema":
|
|
5827
|
+
"name": output_schema.__name__,
|
|
5828
|
+
"schema": output_schema.model_json_schema(),
|
|
5697
5829
|
},
|
|
5698
5830
|
}
|
|
5699
5831
|
else:
|
|
@@ -6767,6 +6899,9 @@ class Agent:
|
|
|
6767
6899
|
dependencies = run_context.dependencies or dependencies
|
|
6768
6900
|
metadata = run_context.metadata or metadata
|
|
6769
6901
|
|
|
6902
|
+
# Get output_schema from run_context
|
|
6903
|
+
output_schema = run_context.output_schema if run_context else None
|
|
6904
|
+
|
|
6770
6905
|
# 1. If the system_message is provided, use that.
|
|
6771
6906
|
if self.system_message is not None:
|
|
6772
6907
|
if isinstance(self.system_message, Message):
|
|
@@ -6776,7 +6911,9 @@ class Agent:
|
|
|
6776
6911
|
if isinstance(self.system_message, str):
|
|
6777
6912
|
sys_message_content = self.system_message
|
|
6778
6913
|
elif callable(self.system_message):
|
|
6779
|
-
sys_message_content =
|
|
6914
|
+
sys_message_content = execute_system_message(
|
|
6915
|
+
agent=self, system_message=self.system_message, session_state=session_state, run_context=run_context
|
|
6916
|
+
)
|
|
6780
6917
|
if not isinstance(sys_message_content, str):
|
|
6781
6918
|
raise Exception("system_message must return a string")
|
|
6782
6919
|
|
|
@@ -6805,25 +6942,9 @@ class Agent:
|
|
|
6805
6942
|
if self.instructions is not None:
|
|
6806
6943
|
_instructions = self.instructions
|
|
6807
6944
|
if callable(self.instructions):
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
instruction_args: Dict[str, Any] = {}
|
|
6812
|
-
|
|
6813
|
-
# Check for agent parameter
|
|
6814
|
-
if "agent" in signature.parameters:
|
|
6815
|
-
instruction_args["agent"] = self
|
|
6816
|
-
|
|
6817
|
-
# Check for session_state parameter
|
|
6818
|
-
if "session_state" in signature.parameters:
|
|
6819
|
-
instruction_args["session_state"] = session_state or {}
|
|
6820
|
-
|
|
6821
|
-
# Check for run_context parameter
|
|
6822
|
-
if "run_context" in signature.parameters:
|
|
6823
|
-
instruction_args["run_context"] = run_context or None
|
|
6824
|
-
|
|
6825
|
-
# Run the instructions function
|
|
6826
|
-
_instructions = self.instructions(**instruction_args)
|
|
6945
|
+
_instructions = execute_instructions(
|
|
6946
|
+
agent=self, instructions=self.instructions, session_state=session_state, run_context=run_context
|
|
6947
|
+
)
|
|
6827
6948
|
|
|
6828
6949
|
if isinstance(_instructions, str):
|
|
6829
6950
|
instructions.append(_instructions)
|
|
@@ -6838,7 +6959,7 @@ class Agent:
|
|
|
6838
6959
|
# 3.2 Build a list of additional information for the system message
|
|
6839
6960
|
additional_information: List[str] = []
|
|
6840
6961
|
# 3.2.1 Add instructions for using markdown
|
|
6841
|
-
if self.markdown and
|
|
6962
|
+
if self.markdown and output_schema is None:
|
|
6842
6963
|
additional_information.append("Use markdown to format your answers.")
|
|
6843
6964
|
# 3.2.2 Add the current datetime
|
|
6844
6965
|
if self.add_datetime_to_context:
|
|
@@ -7073,18 +7194,18 @@ class Agent:
|
|
|
7073
7194
|
# 3.3.13 Add the JSON output prompt if output_schema is provided and the model does not support native structured outputs or JSON schema outputs
|
|
7074
7195
|
# or if use_json_mode is True
|
|
7075
7196
|
if (
|
|
7076
|
-
|
|
7197
|
+
output_schema is not None
|
|
7077
7198
|
and self.parser_model is None
|
|
7078
7199
|
and not (
|
|
7079
7200
|
(self.model.supports_native_structured_outputs or self.model.supports_json_schema_outputs)
|
|
7080
7201
|
and (not self.use_json_mode or self.structured_outputs is True)
|
|
7081
7202
|
)
|
|
7082
7203
|
):
|
|
7083
|
-
system_message_content += f"{get_json_output_prompt(
|
|
7204
|
+
system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
7084
7205
|
|
|
7085
7206
|
# 3.3.14 Add the response model format prompt if output_schema is provided
|
|
7086
|
-
if
|
|
7087
|
-
system_message_content += f"{get_response_model_format_prompt(
|
|
7207
|
+
if output_schema is not None and self.parser_model is not None:
|
|
7208
|
+
system_message_content += f"{get_response_model_format_prompt(output_schema)}"
|
|
7088
7209
|
|
|
7089
7210
|
# 3.3.15 Add the session state to the system message
|
|
7090
7211
|
if add_session_state_to_context and session_state is not None:
|
|
@@ -7121,6 +7242,9 @@ class Agent:
|
|
|
7121
7242
|
dependencies = run_context.dependencies or dependencies
|
|
7122
7243
|
metadata = run_context.metadata or metadata
|
|
7123
7244
|
|
|
7245
|
+
# Get output_schema from run_context
|
|
7246
|
+
output_schema = run_context.output_schema if run_context else None
|
|
7247
|
+
|
|
7124
7248
|
# 1. If the system_message is provided, use that.
|
|
7125
7249
|
if self.system_message is not None:
|
|
7126
7250
|
if isinstance(self.system_message, Message):
|
|
@@ -7130,7 +7254,9 @@ class Agent:
|
|
|
7130
7254
|
if isinstance(self.system_message, str):
|
|
7131
7255
|
sys_message_content = self.system_message
|
|
7132
7256
|
elif callable(self.system_message):
|
|
7133
|
-
sys_message_content =
|
|
7257
|
+
sys_message_content = await aexecute_system_message(
|
|
7258
|
+
agent=self, system_message=self.system_message, session_state=session_state, run_context=run_context
|
|
7259
|
+
)
|
|
7134
7260
|
if not isinstance(sys_message_content, str):
|
|
7135
7261
|
raise Exception("system_message must return a string")
|
|
7136
7262
|
|
|
@@ -7160,20 +7286,9 @@ class Agent:
|
|
|
7160
7286
|
if self.instructions is not None:
|
|
7161
7287
|
_instructions = self.instructions
|
|
7162
7288
|
if callable(self.instructions):
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
instruction_args: Dict[str, Any] = {}
|
|
7167
|
-
|
|
7168
|
-
# Check for agent parameter
|
|
7169
|
-
if "agent" in signature.parameters:
|
|
7170
|
-
instruction_args["agent"] = self
|
|
7171
|
-
|
|
7172
|
-
# Check for session_state parameter
|
|
7173
|
-
if "session_state" in signature.parameters:
|
|
7174
|
-
instruction_args["session_state"] = session_state or {}
|
|
7175
|
-
|
|
7176
|
-
_instructions = self.instructions(**instruction_args)
|
|
7289
|
+
_instructions = await aexecute_instructions(
|
|
7290
|
+
agent=self, instructions=self.instructions, session_state=session_state, run_context=run_context
|
|
7291
|
+
)
|
|
7177
7292
|
|
|
7178
7293
|
if isinstance(_instructions, str):
|
|
7179
7294
|
instructions.append(_instructions)
|
|
@@ -7188,7 +7303,7 @@ class Agent:
|
|
|
7188
7303
|
# 3.2 Build a list of additional information for the system message
|
|
7189
7304
|
additional_information: List[str] = []
|
|
7190
7305
|
# 3.2.1 Add instructions for using markdown
|
|
7191
|
-
if self.markdown and
|
|
7306
|
+
if self.markdown and output_schema is None:
|
|
7192
7307
|
additional_information.append("Use markdown to format your answers.")
|
|
7193
7308
|
# 3.2.2 Add the current datetime
|
|
7194
7309
|
if self.add_datetime_to_context:
|
|
@@ -7426,18 +7541,18 @@ class Agent:
|
|
|
7426
7541
|
# 3.3.13 Add the JSON output prompt if output_schema is provided and the model does not support native structured outputs or JSON schema outputs
|
|
7427
7542
|
# or if use_json_mode is True
|
|
7428
7543
|
if (
|
|
7429
|
-
|
|
7544
|
+
output_schema is not None
|
|
7430
7545
|
and self.parser_model is None
|
|
7431
7546
|
and not (
|
|
7432
7547
|
(self.model.supports_native_structured_outputs or self.model.supports_json_schema_outputs)
|
|
7433
7548
|
and (not self.use_json_mode or self.structured_outputs is True)
|
|
7434
7549
|
)
|
|
7435
7550
|
):
|
|
7436
|
-
system_message_content += f"{get_json_output_prompt(
|
|
7551
|
+
system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
7437
7552
|
|
|
7438
7553
|
# 3.3.14 Add the response model format prompt if output_schema is provided
|
|
7439
|
-
if
|
|
7440
|
-
system_message_content += f"{get_response_model_format_prompt(
|
|
7554
|
+
if output_schema is not None and self.parser_model is not None:
|
|
7555
|
+
system_message_content += f"{get_response_model_format_prompt(output_schema)}"
|
|
7441
7556
|
|
|
7442
7557
|
# 3.3.15 Add the session state to the system message
|
|
7443
7558
|
if add_session_state_to_context and session_state is not None:
|
|
@@ -7627,6 +7742,180 @@ class Agent:
|
|
|
7627
7742
|
**kwargs,
|
|
7628
7743
|
)
|
|
7629
7744
|
|
|
7745
|
+
async def _aget_user_message(
|
|
7746
|
+
self,
|
|
7747
|
+
*,
|
|
7748
|
+
run_response: RunOutput,
|
|
7749
|
+
run_context: Optional[RunContext] = None,
|
|
7750
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
7751
|
+
dependencies: Optional[Dict[str, Any]] = None,
|
|
7752
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
7753
|
+
user_id: Optional[str] = None,
|
|
7754
|
+
input: Optional[Union[str, List, Dict, Message, BaseModel, List[Message]]] = None,
|
|
7755
|
+
audio: Optional[Sequence[Audio]] = None,
|
|
7756
|
+
images: Optional[Sequence[Image]] = None,
|
|
7757
|
+
videos: Optional[Sequence[Video]] = None,
|
|
7758
|
+
files: Optional[Sequence[File]] = None,
|
|
7759
|
+
add_dependencies_to_context: Optional[bool] = None,
|
|
7760
|
+
knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
7761
|
+
**kwargs: Any,
|
|
7762
|
+
) -> Optional[Message]:
|
|
7763
|
+
"""Return the user message for the Agent (async version).
|
|
7764
|
+
|
|
7765
|
+
1. If the user_message is provided, use that.
|
|
7766
|
+
2. If build_user_context is False or if the message is a list, return the message as is.
|
|
7767
|
+
3. Build the default user message for the Agent
|
|
7768
|
+
"""
|
|
7769
|
+
# Consider both run_context and session_state, dependencies, metadata, knowledge_filters (deprecated fields)
|
|
7770
|
+
if run_context is not None:
|
|
7771
|
+
session_state = run_context.session_state or session_state
|
|
7772
|
+
dependencies = run_context.dependencies or dependencies
|
|
7773
|
+
metadata = run_context.metadata or metadata
|
|
7774
|
+
knowledge_filters = run_context.knowledge_filters or knowledge_filters
|
|
7775
|
+
# Get references from the knowledge base to use in the user message
|
|
7776
|
+
references = None
|
|
7777
|
+
|
|
7778
|
+
# 1. If build_user_context is False or message is a list, return the message as is.
|
|
7779
|
+
if not self.build_user_context:
|
|
7780
|
+
return Message(
|
|
7781
|
+
role=self.user_message_role or "user",
|
|
7782
|
+
content=input, # type: ignore
|
|
7783
|
+
images=None if not self.send_media_to_model else images,
|
|
7784
|
+
audio=None if not self.send_media_to_model else audio,
|
|
7785
|
+
videos=None if not self.send_media_to_model else videos,
|
|
7786
|
+
files=None if not self.send_media_to_model else files,
|
|
7787
|
+
**kwargs,
|
|
7788
|
+
)
|
|
7789
|
+
# 2. Build the user message for the Agent
|
|
7790
|
+
elif input is None:
|
|
7791
|
+
# If we have any media, return a message with empty content
|
|
7792
|
+
if images is not None or audio is not None or videos is not None or files is not None:
|
|
7793
|
+
return Message(
|
|
7794
|
+
role=self.user_message_role or "user",
|
|
7795
|
+
content="",
|
|
7796
|
+
images=None if not self.send_media_to_model else images,
|
|
7797
|
+
audio=None if not self.send_media_to_model else audio,
|
|
7798
|
+
videos=None if not self.send_media_to_model else videos,
|
|
7799
|
+
files=None if not self.send_media_to_model else files,
|
|
7800
|
+
**kwargs,
|
|
7801
|
+
)
|
|
7802
|
+
else:
|
|
7803
|
+
# If the input is None, return None
|
|
7804
|
+
return None
|
|
7805
|
+
|
|
7806
|
+
else:
|
|
7807
|
+
# Handle list messages by converting to string
|
|
7808
|
+
if isinstance(input, list):
|
|
7809
|
+
# Convert list to string (join with newlines if all elements are strings)
|
|
7810
|
+
if all(isinstance(item, str) for item in input):
|
|
7811
|
+
message_content = "\n".join(input) # type: ignore
|
|
7812
|
+
else:
|
|
7813
|
+
message_content = str(input)
|
|
7814
|
+
|
|
7815
|
+
return Message(
|
|
7816
|
+
role=self.user_message_role,
|
|
7817
|
+
content=message_content,
|
|
7818
|
+
images=None if not self.send_media_to_model else images,
|
|
7819
|
+
audio=None if not self.send_media_to_model else audio,
|
|
7820
|
+
videos=None if not self.send_media_to_model else videos,
|
|
7821
|
+
files=None if not self.send_media_to_model else files,
|
|
7822
|
+
**kwargs,
|
|
7823
|
+
)
|
|
7824
|
+
|
|
7825
|
+
# If message is provided as a Message, use it directly
|
|
7826
|
+
elif isinstance(input, Message):
|
|
7827
|
+
return input
|
|
7828
|
+
# If message is provided as a dict, try to validate it as a Message
|
|
7829
|
+
elif isinstance(input, dict):
|
|
7830
|
+
try:
|
|
7831
|
+
return Message.model_validate(input)
|
|
7832
|
+
except Exception as e:
|
|
7833
|
+
log_warning(f"Failed to validate message: {e}")
|
|
7834
|
+
raise Exception(f"Failed to validate message: {e}")
|
|
7835
|
+
|
|
7836
|
+
# If message is provided as a BaseModel, convert it to a Message
|
|
7837
|
+
elif isinstance(input, BaseModel):
|
|
7838
|
+
try:
|
|
7839
|
+
# Create a user message with the BaseModel content
|
|
7840
|
+
content = input.model_dump_json(indent=2, exclude_none=True)
|
|
7841
|
+
return Message(role=self.user_message_role, content=content)
|
|
7842
|
+
except Exception as e:
|
|
7843
|
+
log_warning(f"Failed to convert BaseModel to message: {e}")
|
|
7844
|
+
raise Exception(f"Failed to convert BaseModel to message: {e}")
|
|
7845
|
+
else:
|
|
7846
|
+
user_msg_content = input
|
|
7847
|
+
if self.add_knowledge_to_context:
|
|
7848
|
+
if isinstance(input, str):
|
|
7849
|
+
user_msg_content = input
|
|
7850
|
+
elif callable(input):
|
|
7851
|
+
user_msg_content = input(agent=self)
|
|
7852
|
+
else:
|
|
7853
|
+
raise Exception("message must be a string or a callable when add_references is True")
|
|
7854
|
+
|
|
7855
|
+
try:
|
|
7856
|
+
retrieval_timer = Timer()
|
|
7857
|
+
retrieval_timer.start()
|
|
7858
|
+
docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
|
|
7859
|
+
query=user_msg_content, filters=knowledge_filters, **kwargs
|
|
7860
|
+
)
|
|
7861
|
+
if docs_from_knowledge is not None:
|
|
7862
|
+
references = MessageReferences(
|
|
7863
|
+
query=user_msg_content,
|
|
7864
|
+
references=docs_from_knowledge,
|
|
7865
|
+
time=round(retrieval_timer.elapsed, 4),
|
|
7866
|
+
)
|
|
7867
|
+
# Add the references to the run_response
|
|
7868
|
+
if run_response.references is None:
|
|
7869
|
+
run_response.references = []
|
|
7870
|
+
run_response.references.append(references)
|
|
7871
|
+
retrieval_timer.stop()
|
|
7872
|
+
log_debug(f"Time to get references: {retrieval_timer.elapsed:.4f}s")
|
|
7873
|
+
except Exception as e:
|
|
7874
|
+
log_warning(f"Failed to get references: {e}")
|
|
7875
|
+
|
|
7876
|
+
if self.resolve_in_context:
|
|
7877
|
+
user_msg_content = self._format_message_with_state_variables(
|
|
7878
|
+
user_msg_content,
|
|
7879
|
+
user_id=user_id,
|
|
7880
|
+
session_state=session_state,
|
|
7881
|
+
dependencies=dependencies,
|
|
7882
|
+
metadata=metadata,
|
|
7883
|
+
)
|
|
7884
|
+
|
|
7885
|
+
# Convert to string for concatenation operations
|
|
7886
|
+
user_msg_content_str = get_text_from_message(user_msg_content) if user_msg_content is not None else ""
|
|
7887
|
+
|
|
7888
|
+
# 4.1 Add knowledge references to user message
|
|
7889
|
+
if (
|
|
7890
|
+
self.add_knowledge_to_context
|
|
7891
|
+
and references is not None
|
|
7892
|
+
and references.references is not None
|
|
7893
|
+
and len(references.references) > 0
|
|
7894
|
+
):
|
|
7895
|
+
user_msg_content_str += "\n\nUse the following references from the knowledge base if it helps:\n"
|
|
7896
|
+
user_msg_content_str += "<references>\n"
|
|
7897
|
+
user_msg_content_str += self._convert_documents_to_string(references.references) + "\n"
|
|
7898
|
+
user_msg_content_str += "</references>"
|
|
7899
|
+
# 4.2 Add context to user message
|
|
7900
|
+
if add_dependencies_to_context and dependencies is not None:
|
|
7901
|
+
user_msg_content_str += "\n\n<additional context>\n"
|
|
7902
|
+
user_msg_content_str += self._convert_dependencies_to_string(dependencies) + "\n"
|
|
7903
|
+
user_msg_content_str += "</additional context>"
|
|
7904
|
+
|
|
7905
|
+
# Use the string version for the final content
|
|
7906
|
+
user_msg_content = user_msg_content_str
|
|
7907
|
+
|
|
7908
|
+
# Return the user message
|
|
7909
|
+
return Message(
|
|
7910
|
+
role=self.user_message_role,
|
|
7911
|
+
content=user_msg_content,
|
|
7912
|
+
audio=None if not self.send_media_to_model else audio,
|
|
7913
|
+
images=None if not self.send_media_to_model else images,
|
|
7914
|
+
videos=None if not self.send_media_to_model else videos,
|
|
7915
|
+
files=None if not self.send_media_to_model else files,
|
|
7916
|
+
**kwargs,
|
|
7917
|
+
)
|
|
7918
|
+
|
|
7630
7919
|
def _get_run_messages(
|
|
7631
7920
|
self,
|
|
7632
7921
|
*,
|
|
@@ -7976,7 +8265,7 @@ class Agent:
|
|
|
7976
8265
|
)
|
|
7977
8266
|
)
|
|
7978
8267
|
):
|
|
7979
|
-
user_message = self.
|
|
8268
|
+
user_message = await self._aget_user_message(
|
|
7980
8269
|
run_response=run_response,
|
|
7981
8270
|
run_context=run_context,
|
|
7982
8271
|
session_state=session_state,
|
|
@@ -8080,16 +8369,20 @@ class Agent:
|
|
|
8080
8369
|
self,
|
|
8081
8370
|
model_response: ModelResponse,
|
|
8082
8371
|
response_format: Optional[Union[Dict, Type[BaseModel]]],
|
|
8372
|
+
run_context: Optional[RunContext] = None,
|
|
8083
8373
|
) -> List[Message]:
|
|
8084
8374
|
"""Get the messages for the parser model."""
|
|
8375
|
+
# Get output_schema from run_context
|
|
8376
|
+
output_schema = run_context.output_schema if run_context else None
|
|
8377
|
+
|
|
8085
8378
|
system_content = (
|
|
8086
8379
|
self.parser_model_prompt
|
|
8087
8380
|
if self.parser_model_prompt is not None
|
|
8088
8381
|
else "You are tasked with creating a structured output from the provided user message."
|
|
8089
8382
|
)
|
|
8090
8383
|
|
|
8091
|
-
if response_format == {"type": "json_object"} and
|
|
8092
|
-
system_content += f"{get_json_output_prompt(
|
|
8384
|
+
if response_format == {"type": "json_object"} and output_schema is not None:
|
|
8385
|
+
system_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
8093
8386
|
|
|
8094
8387
|
return [
|
|
8095
8388
|
Message(role="system", content=system_content),
|
|
@@ -8100,16 +8393,20 @@ class Agent:
|
|
|
8100
8393
|
self,
|
|
8101
8394
|
run_response: RunOutput,
|
|
8102
8395
|
response_format: Optional[Union[Dict, Type[BaseModel]]],
|
|
8396
|
+
run_context: Optional[RunContext] = None,
|
|
8103
8397
|
) -> List[Message]:
|
|
8104
8398
|
"""Get the messages for the parser model."""
|
|
8399
|
+
# Get output_schema from run_context
|
|
8400
|
+
output_schema = run_context.output_schema if run_context else None
|
|
8401
|
+
|
|
8105
8402
|
system_content = (
|
|
8106
8403
|
self.parser_model_prompt
|
|
8107
8404
|
if self.parser_model_prompt is not None
|
|
8108
8405
|
else "You are tasked with creating a structured output from the provided data."
|
|
8109
8406
|
)
|
|
8110
8407
|
|
|
8111
|
-
if response_format == {"type": "json_object"} and
|
|
8112
|
-
system_content += f"{get_json_output_prompt(
|
|
8408
|
+
if response_format == {"type": "json_object"} and output_schema is not None:
|
|
8409
|
+
system_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
|
|
8113
8410
|
|
|
8114
8411
|
return [
|
|
8115
8412
|
Message(role="system", content=system_content),
|
|
@@ -9148,14 +9445,21 @@ class Agent:
|
|
|
9148
9445
|
else:
|
|
9149
9446
|
log_warning("Unable to parse response with parser model")
|
|
9150
9447
|
|
|
9151
|
-
def _parse_response_with_parser_model(
|
|
9448
|
+
def _parse_response_with_parser_model(
|
|
9449
|
+
self, model_response: ModelResponse, run_messages: RunMessages, run_context: Optional[RunContext] = None
|
|
9450
|
+
) -> None:
|
|
9152
9451
|
"""Parse the model response using the parser model."""
|
|
9153
9452
|
if self.parser_model is None:
|
|
9154
9453
|
return
|
|
9155
9454
|
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9455
|
+
# Get output_schema from run_context
|
|
9456
|
+
output_schema = run_context.output_schema if run_context else None
|
|
9457
|
+
|
|
9458
|
+
if output_schema is not None:
|
|
9459
|
+
parser_response_format = self._get_response_format(self.parser_model, run_context=run_context)
|
|
9460
|
+
messages_for_parser_model = self._get_messages_for_parser_model(
|
|
9461
|
+
model_response, parser_response_format, run_context=run_context
|
|
9462
|
+
)
|
|
9159
9463
|
parser_model_response: ModelResponse = self.parser_model.response(
|
|
9160
9464
|
messages=messages_for_parser_model,
|
|
9161
9465
|
response_format=parser_response_format,
|
|
@@ -9170,15 +9474,20 @@ class Agent:
|
|
|
9170
9474
|
log_warning("A response model is required to parse the response with a parser model")
|
|
9171
9475
|
|
|
9172
9476
|
async def _aparse_response_with_parser_model(
|
|
9173
|
-
self, model_response: ModelResponse, run_messages: RunMessages
|
|
9477
|
+
self, model_response: ModelResponse, run_messages: RunMessages, run_context: Optional[RunContext] = None
|
|
9174
9478
|
) -> None:
|
|
9175
9479
|
"""Parse the model response using the parser model."""
|
|
9176
9480
|
if self.parser_model is None:
|
|
9177
9481
|
return
|
|
9178
9482
|
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9483
|
+
# Get output_schema from run_context
|
|
9484
|
+
output_schema = run_context.output_schema if run_context else None
|
|
9485
|
+
|
|
9486
|
+
if output_schema is not None:
|
|
9487
|
+
parser_response_format = self._get_response_format(self.parser_model, run_context=run_context)
|
|
9488
|
+
messages_for_parser_model = self._get_messages_for_parser_model(
|
|
9489
|
+
model_response, parser_response_format, run_context=run_context
|
|
9490
|
+
)
|
|
9182
9491
|
parser_model_response: ModelResponse = await self.parser_model.aresponse(
|
|
9183
9492
|
messages=messages_for_parser_model,
|
|
9184
9493
|
response_format=parser_response_format,
|
|
@@ -9193,11 +9502,18 @@ class Agent:
|
|
|
9193
9502
|
log_warning("A response model is required to parse the response with a parser model")
|
|
9194
9503
|
|
|
9195
9504
|
def _parse_response_with_parser_model_stream(
|
|
9196
|
-
self,
|
|
9505
|
+
self,
|
|
9506
|
+
session: AgentSession,
|
|
9507
|
+
run_response: RunOutput,
|
|
9508
|
+
stream_events: bool = True,
|
|
9509
|
+
run_context: Optional[RunContext] = None,
|
|
9197
9510
|
):
|
|
9198
9511
|
"""Parse the model response using the parser model"""
|
|
9199
9512
|
if self.parser_model is not None:
|
|
9200
|
-
|
|
9513
|
+
# Get output_schema from run_context
|
|
9514
|
+
output_schema = run_context.output_schema if run_context else None
|
|
9515
|
+
|
|
9516
|
+
if output_schema is not None:
|
|
9201
9517
|
if stream_events:
|
|
9202
9518
|
yield handle_event(
|
|
9203
9519
|
create_parser_model_response_started_event(run_response),
|
|
@@ -9207,9 +9523,9 @@ class Agent:
|
|
|
9207
9523
|
)
|
|
9208
9524
|
|
|
9209
9525
|
parser_model_response = ModelResponse(content="")
|
|
9210
|
-
parser_response_format = self._get_response_format(self.parser_model)
|
|
9526
|
+
parser_response_format = self._get_response_format(self.parser_model, run_context=run_context)
|
|
9211
9527
|
messages_for_parser_model = self._get_messages_for_parser_model_stream(
|
|
9212
|
-
run_response, parser_response_format
|
|
9528
|
+
run_response, parser_response_format, run_context=run_context
|
|
9213
9529
|
)
|
|
9214
9530
|
for model_response_event in self.parser_model.response_stream(
|
|
9215
9531
|
messages=messages_for_parser_model,
|
|
@@ -9223,6 +9539,7 @@ class Agent:
|
|
|
9223
9539
|
model_response_event=model_response_event,
|
|
9224
9540
|
parse_structured_output=True,
|
|
9225
9541
|
stream_events=stream_events,
|
|
9542
|
+
run_context=run_context,
|
|
9226
9543
|
)
|
|
9227
9544
|
|
|
9228
9545
|
parser_model_response_message: Optional[Message] = None
|
|
@@ -9248,11 +9565,18 @@ class Agent:
|
|
|
9248
9565
|
log_warning("A response model is required to parse the response with a parser model")
|
|
9249
9566
|
|
|
9250
9567
|
async def _aparse_response_with_parser_model_stream(
|
|
9251
|
-
self,
|
|
9568
|
+
self,
|
|
9569
|
+
session: AgentSession,
|
|
9570
|
+
run_response: RunOutput,
|
|
9571
|
+
stream_events: bool = True,
|
|
9572
|
+
run_context: Optional[RunContext] = None,
|
|
9252
9573
|
):
|
|
9253
9574
|
"""Parse the model response using the parser model stream."""
|
|
9254
9575
|
if self.parser_model is not None:
|
|
9255
|
-
|
|
9576
|
+
# Get output_schema from run_context
|
|
9577
|
+
output_schema = run_context.output_schema if run_context else None
|
|
9578
|
+
|
|
9579
|
+
if output_schema is not None:
|
|
9256
9580
|
if stream_events:
|
|
9257
9581
|
yield handle_event(
|
|
9258
9582
|
create_parser_model_response_started_event(run_response),
|
|
@@ -9262,9 +9586,9 @@ class Agent:
|
|
|
9262
9586
|
)
|
|
9263
9587
|
|
|
9264
9588
|
parser_model_response = ModelResponse(content="")
|
|
9265
|
-
parser_response_format = self._get_response_format(self.parser_model)
|
|
9589
|
+
parser_response_format = self._get_response_format(self.parser_model, run_context=run_context)
|
|
9266
9590
|
messages_for_parser_model = self._get_messages_for_parser_model_stream(
|
|
9267
|
-
run_response, parser_response_format
|
|
9591
|
+
run_response, parser_response_format, run_context=run_context
|
|
9268
9592
|
)
|
|
9269
9593
|
model_response_stream = self.parser_model.aresponse_stream(
|
|
9270
9594
|
messages=messages_for_parser_model,
|
|
@@ -9279,6 +9603,7 @@ class Agent:
|
|
|
9279
9603
|
model_response_event=model_response_event,
|
|
9280
9604
|
parse_structured_output=True,
|
|
9281
9605
|
stream_events=stream_events,
|
|
9606
|
+
run_context=run_context,
|
|
9282
9607
|
):
|
|
9283
9608
|
yield event
|
|
9284
9609
|
|
|
@@ -10351,7 +10676,10 @@ class Agent:
|
|
|
10351
10676
|
for tool in self.tools:
|
|
10352
10677
|
if isawaitable(tool):
|
|
10353
10678
|
raise NotImplementedError("Use `acli_app` to use async tools.")
|
|
10354
|
-
|
|
10679
|
+
# Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
|
|
10680
|
+
if hasattr(type(tool), "__mro__") and any(
|
|
10681
|
+
c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__
|
|
10682
|
+
):
|
|
10355
10683
|
raise NotImplementedError("Use `acli_app` to use MCP tools.")
|
|
10356
10684
|
|
|
10357
10685
|
if input:
|