Skip to content

Commit 6f2bcb9

Browse files
authored
Expose failure_error_function in Agent.as_tool (#2179)
1 parent 714ff33 commit 6f2bcb9

File tree

2 files changed

+107
-1
lines changed

2 files changed

+107
-1
lines changed

src/agents/agent.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@
2424
from .models.interface import Model
2525
from .prompts import DynamicPromptFunction, Prompt, PromptUtil
2626
from .run_context import RunContextWrapper, TContext
27-
from .tool import FunctionTool, FunctionToolResult, Tool, function_tool
27+
from .tool import (
28+
FunctionTool,
29+
FunctionToolResult,
30+
Tool,
31+
ToolErrorFunction,
32+
default_tool_error_function,
33+
function_tool,
34+
)
2835
from .tool_context import ToolContext
2936
from .util import _transforms
3037
from .util._types import MaybeAwaitable
@@ -411,6 +418,7 @@ def as_tool(
411418
previous_response_id: str | None = None,
412419
conversation_id: str | None = None,
413420
session: Session | None = None,
421+
failure_error_function: ToolErrorFunction | None = default_tool_error_function,
414422
) -> Tool:
415423
"""Transform this agent into a tool, callable by other agents.
416424
@@ -433,12 +441,15 @@ def as_tool(
433441
agent run. The callback receives an `AgentToolStreamEvent` containing the nested
434442
agent, the originating tool call (when available), and each stream event. When
435443
provided, the nested agent is executed in streaming mode.
444+
failure_error_function: If provided, generate an error message when the tool (agent) run
445+
fails. The message is sent to the LLM. If None, the exception is raised instead.
436446
"""
437447

438448
@function_tool(
439449
name_override=tool_name or _transforms.transform_string_function_style(self.name),
440450
description_override=tool_description or "",
441451
is_enabled=is_enabled,
452+
failure_error_function=failure_error_function,
442453
)
443454
async def run_agent(context: ToolContext, input: str) -> Any:
444455
from .run import DEFAULT_MAX_TURNS, Runner

tests/test_agent_as_tool.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,3 +947,98 @@ async def on_stream(event: AgentToolStreamEvent) -> None:
947947

948948
assert output == "ok"
949949
assert captured[0]["tool_call"] is tool_call
950+
951+
952+
@pytest.mark.asyncio
953+
async def test_agent_as_tool_failure_error_function_none_reraises(
954+
monkeypatch: pytest.MonkeyPatch,
955+
) -> None:
956+
"""If failure_error_function=None, exceptions should propagate to the caller."""
957+
agent = Agent(name="failing_agent")
958+
959+
async def fake_run(
960+
cls,
961+
starting_agent,
962+
input,
963+
*,
964+
context,
965+
max_turns,
966+
hooks,
967+
run_config,
968+
previous_response_id,
969+
conversation_id,
970+
session,
971+
):
972+
assert starting_agent is agent
973+
assert input == "hello"
974+
raise RuntimeError("test failure")
975+
976+
monkeypatch.setattr(Runner, "run", classmethod(fake_run))
977+
978+
tool = agent.as_tool(
979+
tool_name="failing_agent_tool",
980+
tool_description="Agent tool that raises",
981+
is_enabled=True,
982+
failure_error_function=None,
983+
)
984+
985+
assert isinstance(tool, FunctionTool)
986+
987+
tool_context = ToolContext(
988+
context=None,
989+
tool_name="failing_agent_tool",
990+
tool_call_id="call_1",
991+
tool_arguments='{"input": "hello"}',
992+
)
993+
994+
with pytest.raises(RuntimeError, match="test failure"):
995+
await tool.on_invoke_tool(tool_context, '{"input": "hello"}')
996+
997+
998+
@pytest.mark.asyncio
999+
async def test_agent_as_tool_failure_error_function_custom_handler(
1000+
monkeypatch: pytest.MonkeyPatch,
1001+
) -> None:
1002+
"""Custom failure_error_function should be used to convert exceptions into tool output."""
1003+
agent = Agent(name="failing_agent")
1004+
1005+
async def fake_run(
1006+
cls,
1007+
starting_agent,
1008+
input,
1009+
*,
1010+
context,
1011+
max_turns,
1012+
hooks,
1013+
run_config,
1014+
previous_response_id,
1015+
conversation_id,
1016+
session,
1017+
):
1018+
assert starting_agent is agent
1019+
assert input == "hello"
1020+
raise ValueError("test failure")
1021+
1022+
monkeypatch.setattr(Runner, "run", classmethod(fake_run))
1023+
1024+
def custom_failure_handler(ctx: RunContextWrapper[Any], error: Exception) -> str:
1025+
return f"handled:{type(error).__name__}:{error}"
1026+
1027+
tool = agent.as_tool(
1028+
tool_name="failing_agent_tool",
1029+
tool_description="Agent tool that raises",
1030+
is_enabled=True,
1031+
failure_error_function=custom_failure_handler,
1032+
)
1033+
1034+
assert isinstance(tool, FunctionTool)
1035+
1036+
tool_context = ToolContext(
1037+
context=None,
1038+
tool_name="failing_agent_tool",
1039+
tool_call_id="call_1",
1040+
tool_arguments='{"input": "hello"}',
1041+
)
1042+
1043+
result = await tool.on_invoke_tool(tool_context, '{"input": "hello"}')
1044+
assert result == "handled:ValueError:test failure"

0 commit comments

Comments
 (0)