From d56d875ea63ff5dd8b8ce64d45d2f5458dc047fa Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 22 May 2026 22:04:19 +0000 Subject: [PATCH] fix(vertex realtime): warn when dropping guardrail turn-detection update In non-deferred mode the auto-setup is sent on connect, so the audio-transcription guardrail's subsequent session.update carrying turn_detection.create_response=False cannot be forwarded as a second setup (Vertex Live closes the WebSocket with 1007). Surface a warning when this specific drop happens so operators know the model will auto-respond before the guardrail can gate it, instead of failing silently at debug level. --- .../llms/vertex_ai/realtime/transformation.py | 24 +++++++- .../test_vertex_ai_realtime_transformation.py | 58 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/litellm/llms/vertex_ai/realtime/transformation.py b/litellm/llms/vertex_ai/realtime/transformation.py index d2d89a52c8d..ea4dbccc8c8 100644 --- a/litellm/llms/vertex_ai/realtime/transformation.py +++ b/litellm/llms/vertex_ai/realtime/transformation.py @@ -226,9 +226,29 @@ class VertexAIRealtimeConfig(GeminiRealtimeConfig): ) return [gemini_setup_msg] - verbose_logger.debug( - "Vertex AI Realtime: Ignoring session.update (setup already sent)" + # A follow-up session.update can't be forwarded as a second setup + # (Vertex Live closes the WebSocket with 1007). If this drop is + # silencing the audio-transcription guardrail's create_response + # disable, surface a warning so operators know the model will + # auto-respond before the guardrail can gate it on Vertex AI. + client_turn_detection = GeminiRealtimeConfig._extract_turn_detection( + json_message.get("session") or {} ) + if ( + isinstance(client_turn_detection, dict) + and client_turn_detection.get("create_response") is False + ): + verbose_logger.warning( + "Vertex AI Realtime: Dropping subsequent session.update " + "(turn_detection.create_response=False) — Vertex Live " + "rejects a second setup message. Audio-transcription " + "guardrails cannot suppress the model's auto-response on " + "Vertex AI in non-deferred mode." + ) + else: + verbose_logger.debug( + "Vertex AI Realtime: Ignoring session.update (setup already sent)" + ) return [] return super().transform_realtime_request( diff --git a/tests/test_litellm/llms/vertex_ai/realtime/test_vertex_ai_realtime_transformation.py b/tests/test_litellm/llms/vertex_ai/realtime/test_vertex_ai_realtime_transformation.py index 8917d460b84..0ad614099de 100644 --- a/tests/test_litellm/llms/vertex_ai/realtime/test_vertex_ai_realtime_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/realtime/test_vertex_ai_realtime_transformation.py @@ -288,3 +288,61 @@ async def test_vertex_realtime_text_in_text_out(): # response.done should have been forwarded done_msgs = [m for m in sent_to_client if '"response.done"' in m] assert done_msgs, "Expected response.done to be sent to client" + + +def test_vertex_warns_when_dropping_guardrail_turn_detection_update(caplog): + """A subsequent session.update carrying the guardrail's + ``create_response: False`` cannot be forwarded as a follow-up setup on + Vertex AI (1007). Surface a warning so operators know the auto-response + suppression is being silently dropped.""" + import logging + + cfg = VertexAIRealtimeConfig( + access_token="tok", project="my-proj", location="us-central1" + ) + + session_update = { + "type": "session.update", + "session": {"turn_detection": {"create_response": False}}, + } + + with caplog.at_level(logging.WARNING, logger="LiteLLM"): + result = cfg.transform_realtime_request( + json.dumps(session_update), + "gemini-live-2.5-flash-native-audio", + session_configuration_request=json.dumps({"setup": {"model": "x"}}), + ) + + assert result == [] + assert any( + "Vertex AI Realtime" in record.message + and "create_response=False" in record.message + for record in caplog.records + ) + + +def test_vertex_does_not_warn_when_dropping_non_guardrail_session_update(caplog): + """A subsequent session.update without ``create_response: False`` is a + routine drop and should stay at debug level (no warning).""" + import logging + + cfg = VertexAIRealtimeConfig( + access_token="tok", project="my-proj", location="us-central1" + ) + + session_update = { + "type": "session.update", + "session": {"instructions": "Be concise."}, + } + + with caplog.at_level(logging.WARNING, logger="LiteLLM"): + cfg.transform_realtime_request( + json.dumps(session_update), + "gemini-live-2.5-flash-native-audio", + session_configuration_request=json.dumps({"setup": {"model": "x"}}), + ) + + assert not any( + "Vertex AI Realtime" in record.message and "session.update" in record.message + for record in caplog.records + )