diff --git a/litellm/litellm_core_utils/realtime_streaming.py b/litellm/litellm_core_utils/realtime_streaming.py index 3d7871eac41..de08bf5a24f 100644 --- a/litellm/litellm_core_utils/realtime_streaming.py +++ b/litellm/litellm_core_utils/realtime_streaming.py @@ -899,6 +899,30 @@ class RealTimeStreaming: output_text ) if blocked: + # Forward a sanitized function_call_output so + # providers that pair every toolCall with a + # toolResponse (e.g. Gemini/Vertex Live) exit + # their pending-tool-call state. Dropping the + # blocked item outright would leave such + # providers waiting indefinitely while the + # subsequent guardrail clientContent is + # ignored. The sanitized payload carries no + # blocked content — only a generic policy + # marker. + sanitized_msg = json.dumps( + { + **msg_obj, + "item": { + **item, + "output": json.dumps( + { + "error": "Tool output blocked by content policy", + } + ), + }, + } + ) + await self._send_to_backend(sanitized_msg) self._pending_guardrail_message = output_text continue diff --git a/tests/test_litellm/litellm_core_utils/test_realtime_streaming.py b/tests/test_litellm/litellm_core_utils/test_realtime_streaming.py index 641d3da3ba3..92cc128b9c3 100644 --- a/tests/test_litellm/litellm_core_utils/test_realtime_streaming.py +++ b/tests/test_litellm/litellm_core_utils/test_realtime_streaming.py @@ -951,10 +951,17 @@ async def test_realtime_function_call_output_guardrail_blocks_and_returns_error( and json.loads(m).get("type") == "conversation.item.create" and json.loads(m).get("item", {}).get("type") == "function_call_output" ] - assert len(forwarded_tool_outputs) == 0, ( - f"Blocked function_call_output should not be forwarded, got: " + # A sanitized placeholder must reach the backend so providers that pair + # every toolCall with a toolResponse (Gemini/Vertex Live) exit their + # pending-tool-call state instead of stalling. The placeholder must NOT + # contain any of the blocked content. + assert len(forwarded_tool_outputs) == 1, ( + f"Sanitized function_call_output should be forwarded, got: " f"{forwarded_tool_outputs}" ) + sanitized_item = forwarded_tool_outputs[0]["item"] + assert sanitized_item["call_id"] == "call_123" + assert "test@example.com" not in sanitized_item["output"] litellm.callbacks = [] # cleanup