diff --git a/src/agents/realtime/session.py b/src/agents/realtime/session.py index f424b5b9d5..ae9acb63d7 100644 --- a/src/agents/realtime/session.py +++ b/src/agents/realtime/session.py @@ -694,11 +694,21 @@ async def _handle_tool_call( tool_arguments=event.arguments, agent=agent, ) - result = await invoke_function_tool( - function_tool=func_tool, - context=tool_context, - arguments=event.arguments, - ) + try: + result = await invoke_function_tool( + function_tool=func_tool, + context=tool_context, + arguments=event.arguments, + ) + except Exception: + await self._model.send_event( + RealtimeModelSendToolOutput( + tool_call=event, + output=f"Tool '{event.name}' failed", + start_response=True, + ) + ) + raise await self._model.send_event( RealtimeModelSendToolOutput( @@ -729,7 +739,17 @@ async def _handle_tool_call( ) # Execute the handoff to get the new agent - result = await handoff.on_invoke_handoff(self._context_wrapper, event.arguments) + try: + result = await handoff.on_invoke_handoff(self._context_wrapper, event.arguments) + except Exception: + await self._model.send_event( + RealtimeModelSendToolOutput( + tool_call=event, + output=f"Handoff '{event.name}' failed", + start_response=True, + ) + ) + raise if not isinstance(result, RealtimeAgent): raise UserError( f"Handoff {handoff.tool_name} returned invalid result: {type(result)}" diff --git a/tests/realtime/test_session.py b/tests/realtime/test_session.py index 67cf717aa5..03b3ff867e 100644 --- a/tests/realtime/test_session.py +++ b/tests/realtime/test_session.py @@ -1109,7 +1109,7 @@ async def invoke_slow_tool(_ctx: ToolContext[Any], _arguments: str) -> str: with pytest.raises(ToolTimeoutError, match="timed out"): await session._handle_tool_call(tool_call_event) - assert len(mock_model.sent_tool_outputs) == 0 + assert len(mock_model.sent_tool_outputs) == 1 assert session._event_queue.qsize() == 1 tool_start_event = await session._event_queue.get() @@ -1196,7 +1196,7 @@ async def invoke_slow_tool(_ctx: ToolContext[Any], _arguments: str) -> str: assert isinstance(session._stored_exception, ToolTimeoutError) assert session._stored_exception.tool_name == "slow_tool" - assert len(mock_model.sent_tool_outputs) == 0 + assert len(mock_model.sent_tool_outputs) == 1 events = [] while True: @@ -1626,8 +1626,8 @@ async def test_function_tool_exception_handling( assert isinstance(tool_start_event, RealtimeToolStart) assert tool_start_event.arguments == "{}" - # But no tool output should have been sent and no end event queued - assert len(mock_model.sent_tool_outputs) == 0 + # An error tool output should be sent to the model before re-raising + assert len(mock_model.sent_tool_outputs) == 1 @pytest.mark.asyncio async def test_tool_call_with_complex_arguments(