Skip to content

feat(vertex_ai): Vertex AI Gemini Live via unified /realtime endpoint - #22153

Merged
ishaan-jaff merged 7 commits into
mainfrom
worktree-elegant-sauteeing-hanrahan
Feb 26, 2026
Merged

feat(vertex_ai): Vertex AI Gemini Live via unified /realtime endpoint#22153
ishaan-jaff merged 7 commits into
mainfrom
worktree-elegant-sauteeing-hanrahan

Conversation

@ishaan-jaff

Copy link
Copy Markdown
Contributor

Relevant issues

Closes: N/A

What this does

Adds Vertex AI Gemini Live (gemini-2.0-flash-live-001) support through LiteLLM's unified /realtime WebSocket endpoint. Clients use the standard OpenAI Realtime protocol — the proxy handles translation to/from Vertex AI's BidiGenerateContent format.

Tested end-to-end: text in/text out + voice in/voice out (mic → 16 kHz PCM16 → Vertex AI → 24 kHz PCM16 → speaker).

Changes

New: litellm/llms/vertex_ai/realtime/transformation.pyVertexAIRealtimeConfig

  • Builds correct wss://{location}-aiplatform.googleapis.com/.../BidiGenerateContent URL
  • OAuth2 Bearer token auth (not API key)
  • Full model path: projects/{project}/locations/{location}/publishers/google/models/{model}
  • Ignores session.update — Vertex AI only accepts one setup message per connection; sending a second causes a 1007 disconnect
  • Audio MIME type includes sample rate: audio/pcm;rate=16000

realtime_api/main.py — adds vertex_ai branch that resolves the OAuth token via VertexBase._ensure_access_token_async and constructs VertexAIRealtimeConfig

llm_http_handler.py — auto-sends the provider's session setup message before bidirectional_forward() when requires_session_configuration() returns True (needed for Gemini/Vertex AI Live)

gemini/realtime/transformation.py — fixes two crashes:

  • transform_response_done_event raised ValueError when turnComplete arrived before any audio (IDs were None); now generates UUIDs instead
  • Same fix in transform_content_done_event
  • Silently drop response.create (Vertex AI responds automatically)
  • Handle conversation.item.create to extract actual user text

realtime_streaming.py — try/except guard in backend loop so a single malformed message doesn't kill the whole session; also fix pre-existing Pyright type annotation errors on _collect_tool_calls_from_response_done / _collect_user_input_from_backend_event

proxy_server.py — add missing import websockets.exceptions (caused NameError on every WebSocket close)

provider_endpoints_support.json — mark vertex_ai with "realtime": true

docs/my-website/docs/providers/vertex_realtime.md — usage docs with Python, Node.js, and OpenAI SDK examples

Pre-Submission checklist

  • Added tests (tests/test_litellm/llms/vertex_ai/realtime/test_vertex_ai_realtime_transformation.py) — 6 unit tests covering URL construction, auth headers, session config format, and full text-in/text-out round trip with mocked WebSockets
  • make test-unit passes

Type

  • Bug Fix
  • New Feature
  • Refactoring
  • Documentation

Changes

  • litellm/llms/vertex_ai/realtime/transformation.py
  • litellm/llms/vertex_ai/realtime/__init__.py
  • litellm/realtime_api/main.py
  • litellm/llms/custom_httpx/llm_http_handler.py
  • litellm/llms/gemini/realtime/transformation.py
  • litellm/litellm_core_utils/realtime_streaming.py
  • litellm/proxy/proxy_server.py
  • provider_endpoints_support.json
  • docs/my-website/docs/providers/vertex_realtime.md

@vercel

vercel Bot commented Feb 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Feb 26, 2026 6:11am

Request Review

…ime endpoint

Adds VertexAIRealtimeConfig which translates the OpenAI Realtime WebSocket
protocol to Vertex AI BidiGenerateContent. Supports voice in/voice out
(16 kHz mic → 24 kHz speaker) and text in/text out through the proxy's
/realtime endpoint.

Key changes:
- New litellm/llms/vertex_ai/realtime/transformation.py with VertexAIRealtimeConfig
  - Builds correct wss:// URL (regional + global)
  - OAuth2 Bearer token auth (not API key)
  - Full model path (projects/.../publishers/google/models/...)
  - Ignores session.update (Vertex AI only accepts one setup message)
- realtime_api/main.py: vertex_ai branch resolves OAuth token + constructs config
- llm_http_handler.py: auto-sends session setup before bidirectional_forward
- gemini/realtime/transformation.py: fix crashes on empty turnComplete events
- realtime_streaming.py: try/except guard so bad messages don't kill the loop
- proxy_server.py: add missing websockets.exceptions import
@ishaan-jaff
ishaan-jaff force-pushed the worktree-elegant-sauteeing-hanrahan branch from 2f9096d to f6d0775 Compare February 26, 2026 05:36
@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds Vertex AI Gemini Live (gemini-2.0-flash-live-001) support through LiteLLM's unified /realtime WebSocket endpoint. The new VertexAIRealtimeConfig extends GeminiRealtimeConfig with Vertex AI-specific WSS URL construction, OAuth2 Bearer token auth, and fully-qualified model paths. The PR also fixes several crashes in the base Gemini realtime transformer (UUID fallbacks for None IDs, proper model path normalization, graceful handling of unknown event types).

  • New provider: VertexAIRealtimeConfig in litellm/llms/vertex_ai/realtime/transformation.py handles URL construction, auth headers, audio MIME types with sample rate, and session setup
  • Crash fixes: GeminiRealtimeConfig no longer raises ValueError when turnComplete arrives before any audio content — generates UUID fallbacks instead
  • Request translation fixes: response.create and unknown OpenAI event types are now silently dropped instead of being forwarded as raw JSON text to the model
  • Guardrail integration concern: The new _send_to_backend helper routes guardrail messages through transform_realtime_request, but Vertex AI drops both session.update and response.create — meaning realtime guardrail warning messages and create_response=false injection are silently lost, making guardrails ineffective for Vertex AI sessions
  • Tests: 6 mock-only unit tests covering URL construction, auth, session config, and end-to-end text round-trip

Confidence Score: 3/5

  • Core Vertex AI realtime functionality is solid, but guardrail integration has a silent failure mode that should be addressed before merge.
  • The new Vertex AI realtime feature is well-implemented with proper URL construction, auth, and session setup. However, the _send_to_backend refactor introduces a logic issue: guardrail warning messages and the create_response=false safety mechanism are silently dropped for Vertex AI because transform_realtime_request returns [] for session.update and response.create. This means realtime guardrails won't function correctly for Vertex AI sessions — blocked content won't generate a warning to the user. The crash fixes in GeminiRealtimeConfig and the model path normalization are good improvements.
  • litellm/litellm_core_utils/realtime_streaming.py — guardrail messages silently dropped for Vertex AI via _send_to_backend

Important Files Changed

Filename Overview
litellm/llms/vertex_ai/realtime/transformation.py New VertexAIRealtimeConfig extending GeminiRealtimeConfig. Well-structured with proper URL construction, OAuth2 auth, and session setup. Minor: double JSON parse on non-session.update messages, inline imports in session_configuration_request.
litellm/litellm_core_utils/realtime_streaming.py New _send_to_backend method routes guardrail messages through provider transform, but this causes guardrail warning messages and create_response=false injection to be silently dropped for Vertex AI, making realtime guardrails ineffective.
litellm/llms/gemini/realtime/transformation.py Good fixes: UUID fallbacks for None IDs prevent crashes on early turnComplete; proper model path normalization for Vertex AI; response.create and unknown events now handled gracefully instead of forwarding raw JSON as text.
litellm/realtime_api/main.py Vertex AI branch properly resolves OAuth token, project, and location before creating config. Health check branch added. Follows established patterns from other providers (azure, bedrock, xai).
litellm/llms/custom_httpx/llm_http_handler.py Clean addition: auto-sends session setup message for providers that require it, then stores it on the RealTimeStreaming instance. No issues found.
tests/test_litellm/llms/vertex_ai/realtime/test_vertex_ai_realtime_transformation.py 6 well-structured unit tests covering URL construction, auth headers, session config format, and full text-in/text-out round trip with mocked WebSockets. All mock-based, no real network calls.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Proxy as LiteLLM Proxy
    participant Transform as VertexAIRealtimeConfig
    participant VertexAI as Vertex AI

    Client->>Proxy: WebSocket /realtime
    Proxy->>Transform: get_complete_url + validate_environment
    Proxy->>VertexAI: WSS connect
    Transform->>VertexAI: setup message with model path and VAD
    VertexAI-->>Transform: setupComplete
    Transform-->>Client: session.created

    Client->>Transform: conversation.item.create
    Transform->>VertexAI: realtime_input.text
    VertexAI-->>Transform: serverContent.modelTurn text
    Transform-->>Client: response.text.delta
    VertexAI-->>Transform: serverContent.generationComplete
    Transform-->>Client: response.text.done
    VertexAI-->>Transform: serverContent.turnComplete
    Transform-->>Client: response.done

    Client->>Transform: input_audio_buffer.append
    Transform->>VertexAI: realtime_input.audio PCM16
    VertexAI-->>Transform: serverContent.modelTurn inlineData
    Transform-->>Client: response.audio.delta

    Client->>Transform: session.update
    Note right of Transform: Silently dropped
Loading

Last reviewed commit: 58109ac

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +17 to +20
from litellm.llms.gemini.realtime.transformation import GeminiRealtimeConfig
from litellm.llms.vertex_ai.vertex_llm_base import VertexBase

vertex_llm_base = VertexBase()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import and module-level instantiation

VertexBase is imported and instantiated as vertex_llm_base at module level but is never referenced anywhere in this file. The actual VertexBase usage happens in realtime_api/main.py which has its own instance. This creates an unnecessary object at import time.

Suggested change
from litellm.llms.gemini.realtime.transformation import GeminiRealtimeConfig
from litellm.llms.vertex_ai.vertex_llm_base import VertexBase
vertex_llm_base = VertexBase()
from litellm.llms.gemini.realtime.transformation import GeminiRealtimeConfig

Comment on lines +111 to +112
from litellm.types.llms.gemini import BidiGenerateContentSetup
from litellm.types.llms.vertex_ai import GeminiResponseModalities

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline imports in method body

Per the CLAUDE.md style guide: "Avoid imports within methods — place all imports at the top of the file (module-level)." The imports of BidiGenerateContentSetup and GeminiResponseModalities should be moved to the top of the file.

# At the top of the file, add:
from litellm.types.llms.gemini import BidiGenerateContentSetup
from litellm.types.llms.vertex_ai import GeminiResponseModalities

Context Used: Context from dashboard - CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines 265 to 267
else:
# Pass through any other message types as raw text
realtime_input_dict["text"] = message

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrecognized message types sent as raw text to Gemini

The else branch sends the entire raw JSON string (e.g. {"type":"input_audio_buffer.commit","event_id":"..."}) as a text input to the model. For OpenAI Realtime events that have no Gemini equivalent (like input_audio_buffer.commit, input_audio_buffer.clear), this will cause the model to receive the raw JSON as a user text message, which is likely not the intended behavior.

Consider either explicitly handling known no-op events (returning []) or at minimum logging a warning, similar to how response.create and session.update are handled:

Suggested change
else:
# Pass through any other message types as raw text
realtime_input_dict["text"] = message
else:
# Drop unrecognized OpenAI event types that have no Gemini equivalent
return []

Comment on lines +442 to +448
try:
await self._handle_provider_config_message(raw_response)
except Exception as e:
verbose_logger.exception(
f"Error processing backend message, skipping: {e}"
)
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broad exception catch silently swallows all backend errors

While adding a try/except here prevents a single malformed message from killing the session (good), catching all Exception types means that serious errors (e.g. auth failures, protocol violations, or bugs in transformation code) will also be silently swallowed, with the loop continue-ing past them. Consider narrowing this to catch only expected transformation errors (like ValueError, KeyError, json.JSONDecodeError) so that unexpected failures still propagate and are visible.

Comment on lines +221 to +266
elif _custom_llm_provider == "vertex_ai":
vertex_credentials = (
kwargs.get("vertex_credentials")
or kwargs.get("vertex_ai_credentials")
or get_secret_str("VERTEXAI_CREDENTIALS")
)
vertex_project = (
kwargs.get("vertex_project")
or kwargs.get("vertex_ai_project")
or litellm.vertex_project
or get_secret_str("VERTEXAI_PROJECT")
)
vertex_location = (
kwargs.get("vertex_location")
or kwargs.get("vertex_ai_location")
or litellm.vertex_location
or get_secret_str("VERTEXAI_LOCATION")
)

resolved_location = vertex_llm_base.get_vertex_region(
vertex_region=vertex_location, model=model
)

access_token, resolved_project = await vertex_llm_base._ensure_access_token_async(
credentials=vertex_credentials,
project_id=vertex_project,
custom_llm_provider="vertex_ai",
)

vertex_realtime_config = VertexAIRealtimeConfig(
access_token=access_token,
project=resolved_project,
location=resolved_location,
)

await base_llm_http_handler.async_realtime(
model=model,
websocket=websocket,
logging_obj=litellm_logging_obj,
provider_config=vertex_realtime_config,
api_base=dynamic_api_base or litellm_params.api_base,
api_key=None,
client=client,
timeout=timeout,
headers=headers,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_realtime_health_check not updated for vertex_ai

The _realtime_health_check function (line 271) does not have a vertex_ai branch and will raise ValueError("Unsupported model: ...") if called for a Vertex AI realtime model. If health checks are run for deployed realtime models, this will fail at runtime. Consider adding a vertex_ai case or at least a graceful skip.

@ishaan-jaff

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (1)

litellm/llms/gemini/realtime/transformation.py, line 778
Generated UUIDs not propagated to next call

When generationComplete arrives before any content deltas, current_output_item_id and current_response_id are None. The fix in transform_content_done_event (line 480-483) generates UUIDs locally, but handle_openai_modality_event passes the original None values to return_additional_content_done_events (line 772-774), which still raises ValueError at line 517-520.

The UUIDs generated inside transform_content_done_event are local to that method and don't propagate back. Either:

  • Apply the same UUID-generation fallback in return_additional_content_done_events (consistent with what was done for transform_content_done_event and transform_response_done_event)
  • Or extract the generated IDs from transformed_content_done_event and pass them through:
            transformed_content_done_event = self.transform_content_done_event(
                current_output_item_id=current_output_item_id,
                current_response_id=current_response_id,
                delta_chunks=current_delta_chunks,
                delta_type=delta_type,
            )
            returned_message = [transformed_content_done_event]

            # Use the IDs from the done event (which may have been generated
            # as UUID fallbacks inside transform_content_done_event).
            resolved_item_id = transformed_content_done_event.get("item_id") or current_output_item_id
            resolved_response_id = transformed_content_done_event.get("response_id") or current_response_id

            additional_items = self.return_additional_content_done_events(
                current_output_item_id=resolved_item_id,
                current_response_id=resolved_response_id,
                delta_done_event=transformed_content_done_event,
                delta_type=delta_type,
            )
            returned_message.extend(additional_items)

@ishaan-jaff

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (2)

litellm/litellm_core_utils/realtime_streaming.py, line 350
Raw OpenAI-format messages sent to non-OpenAI backends

When guardrails are active, this block sends an OpenAI-format session.update JSON directly to self.backend_ws (lines 338–350), and later sends response.create (line 367–369). For Vertex AI (and Gemini), the backend expects its own protocol — not OpenAI Realtime events. These raw messages bypass the provider_config.transform_realtime_request() layer entirely.

For Vertex AI specifically, sending a raw {"type": "session.update", ...} to the WebSocket will either be silently ignored or cause a protocol error. Similarly, response.create is not a valid Vertex AI message type (Vertex AI responds automatically after each turn).

Consider routing these guardrail messages through self.provider_config.transform_realtime_request() before sending, or adding a provider-aware conditional that skips these sends for providers that don't support mid-session reconfiguration.


litellm/llms/gemini/realtime/transformation.py, line 317
str.strip() strips characters, not substrings — may corrupt Vertex AI model paths

_model.strip("models/") strips any of the individual characters m, o, d, e, l, s, / from both ends of the string. For Vertex AI, _model is now a fully-qualified path like "projects/my-proj/locations/us-central1/publishers/google/models/gemini-2.0-flash-live-001". It currently works only because the path starts with p and ends with 1 (neither in the strip set), but a model name like "gemini-2.0-flash-live-demo" would have its trailing o stripped.

Consider using removeprefix instead:

            session["model"] = _model.removeprefix(
                "models/"
            )  # keep it consistent with how openai returns the model name

@ishaan-jaff

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

…ardrail block sends through _send_to_backend
@ishaan-jaff

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (2)

litellm/llms/gemini/realtime/transformation.py, line 321
Vertex AI model path leaks into session.created

For Vertex AI, _model will be the fully-qualified resource path (e.g., projects/my-proj/locations/us-central1/publishers/google/models/gemini-2.0-flash-live-001). This doesn't start with "models/", so the startswith("models/") check on line 319 won't strip it. The full Vertex AI resource path will be returned as session["model"] in the session.created event sent to the client, which breaks the OpenAI-compatible contract.

Consider also stripping the Vertex AI resource prefix, e.g.:

        if _model is not None and isinstance(_model, str):
            # Strip "models/" or Vertex AI resource prefix if present
            # to match OpenAI model name format.
            if _model.startswith("models/"):
                _model = _model[len("models/"):]
            elif "/publishers/google/models/" in _model:
                _model = _model.split("/publishers/google/models/")[-1]
            session["model"] = _model

litellm/litellm_core_utils/realtime_streaming.py, line 313
Guardrail sends raw OpenAI events to Vertex AI backend

run_realtime_guardrails sends response.cancel (line 298) and response.create (line 300-313) directly via self.backend_ws.send() rather than the new self._send_to_backend() helper. For Vertex AI, these raw OpenAI-format messages will be sent untransformed to the Vertex AI WebSocket, which doesn't understand them. While both event types would be silently dropped by the Gemini transform (returning []), this means the guardrail block+warning flow effectively becomes a no-op for Vertex AI — the model won't speak the warning and won't be cancelled.

Consider using self._send_to_backend() here for consistency, and/or adding a Vertex AI-specific mechanism to handle guardrail-triggered responses.

@ishaan-jaff

Copy link
Copy Markdown
Contributor Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (2)

litellm/llms/vertex_ai/realtime/transformation.py, line 162
Double JSON parse, first without error handling

json.loads(message) on line 155 will raise a raw json.JSONDecodeError if the message is malformed, bypassing the parent class's error handling that wraps it in a ValueError with a helpful message. Additionally, the message is parsed a second time inside super().transform_realtime_request() (line 221 of gemini/realtime/transformation.py), which is wasteful.

Consider guarding the parse or checking the type after the parent has parsed:

        try:
            json_message = json.loads(message)
        except json.JSONDecodeError:
            # Fall through to parent which has proper error handling
            return super().transform_realtime_request(
                message, model, session_configuration_request
            )
        if json_message.get("type") == "session.update":
            # Do not forward as a second setup — Vertex AI rejects it.
            return []

        return super().transform_realtime_request(
            message, model, session_configuration_request
        )

litellm/realtime_api/main.py, line 248
access_token may be None at runtime

_ensure_access_token_async can return (None, project_id) if credential resolution fails (e.g. no credentials file, no metadata server). VertexAIRealtimeConfig.__init__ declares access_token: str, so passing None would violate the type contract and result in Authorization: Bearer None being sent to Vertex AI — which will fail with a confusing auth error at WebSocket connect time rather than a clear error here.

Consider adding a guard:

if not access_token:
    raise ValueError(
        "Could not resolve Vertex AI access token. "
        "Set GOOGLE_APPLICATION_CREDENTIALS or run inside a GCP environment."
    )

@ishaan-jaff
ishaan-jaff merged commit f1c9cb7 into main Feb 26, 2026
24 of 34 checks passed
@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (3)

litellm/litellm_core_utils/realtime_streaming.py, line 313
Guardrail warning messages silently dropped for Vertex AI

When a guardrail blocks content, _send_to_backend sends both response.cancel and response.create through transform_realtime_request. For Vertex AI (via VertexAIRealtimeConfigGeminiRealtimeConfig), response.create explicitly returns [] (line 244 of gemini/realtime/transformation.py) and response.cancel falls through to the else branch which also returns []. This means the warning speech message ("Say exactly and only: ...") will be silently dropped — the user will never hear or see the guardrail's block message.

The guardrail correctly returns True (blocked), so the original content won't be forwarded, but the replacement warning is lost. Consider either:

  1. Having VertexAIRealtimeConfig.transform_realtime_request translate response.create into an equivalent Gemini realtime_input.text message, or
  2. Sending the guardrail warning directly to the client as a synthesized response.text.delta / response.done event without going through the backend at all.

litellm/litellm_core_utils/realtime_streaming.py, line 367
Guardrail create_response=false injection has no effect for Vertex AI

When realtime guardrails are registered, this code sends session.update with create_response: false to disable auto-response. However, for Vertex AI, _send_to_backend routes this through VertexAIRealtimeConfig.transform_realtime_request, which returns [] for all session.update messages — so this safety mechanism is silently dropped.

Since Vertex AI has server-side VAD enabled and auto-responds after each turn, guardrails cannot actually prevent the LLM from responding before the guardrail check completes. This makes realtime guardrails ineffective for Vertex AI sessions. Consider documenting this limitation or implementing an alternative mechanism (e.g., client-side buffering).


litellm/llms/vertex_ai/realtime/transformation.py, line 159
Double JSON parse on every session.update message

transform_realtime_request parses message with json.loads(message) at line 152, and if the type is NOT session.update, it calls super().transform_realtime_request(message, ...) which parses the same JSON string again at GeminiRealtimeConfig line 221. For session.update this is fine (returns early), but for all other message types the raw JSON is parsed twice.

Consider passing the already-parsed json_message dict into the parent class, or restructuring so parsing happens once. This is a minor performance concern for high-frequency audio messages.

    def transform_realtime_request(
        self,
        message: str,
        model: str,
        session_configuration_request: Optional[str] = None,
    ) -> List[str]:
        """
        Translate OpenAI realtime client messages to Vertex AI format.

        ``session.update`` is intentionally ignored (returns []) because
        Vertex AI only accepts a single ``setup`` message at the start of
        the connection — sending a second one causes a 1007 close error.
        The initial setup (sent automatically before bidirectional_forward)
        already includes AUDIO modality and server VAD, so there is nothing
        more to configure.
        """
        try:
            json_message = json.loads(message)
        except json.JSONDecodeError:
            return super().transform_realtime_request(
                message, model, session_configuration_request
            )
        if json_message.get("type") == "session.update":
            # Do not forward as a second setup — Vertex AI rejects it.
            return []

        return super().transform_realtime_request(
            message, model, session_configuration_request
        )

Sameerlite pushed a commit that referenced this pull request Mar 3, 2026
…#22153)

* feat(vertex_ai): add Vertex AI Gemini Live support via unified /realtime endpoint

Adds VertexAIRealtimeConfig which translates the OpenAI Realtime WebSocket
protocol to Vertex AI BidiGenerateContent. Supports voice in/voice out
(16 kHz mic → 24 kHz speaker) and text in/text out through the proxy's
/realtime endpoint.

Key changes:
- New litellm/llms/vertex_ai/realtime/transformation.py with VertexAIRealtimeConfig
  - Builds correct wss:// URL (regional + global)
  - OAuth2 Bearer token auth (not API key)
  - Full model path (projects/.../publishers/google/models/...)
  - Ignores session.update (Vertex AI only accepts one setup message)
- realtime_api/main.py: vertex_ai branch resolves OAuth token + constructs config
- llm_http_handler.py: auto-sends session setup before bidirectional_forward
- gemini/realtime/transformation.py: fix crashes on empty turnComplete events
- realtime_streaming.py: try/except guard so bad messages don't kill the loop
- proxy_server.py: add missing websockets.exceptions import

* docs: add vertex_realtime to sidebars

* fix: drop unknown event types in Gemini transform; add vertex_ai health check

* fix: propagate UUID fallback IDs from transform_content_done_event to return_additional_content_done_events

* fix: route guardrail backend sends through provider transform; fix str.strip misuse for model prefix

* fix: handle Vertex AI full resource path in session.created; route guardrail block sends through _send_to_backend

* fix: remove unused VertexBase in transformation.py; apply UUID fallback in return_additional_content_done_events
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…BerriAI#22153)

* feat(vertex_ai): add Vertex AI Gemini Live support via unified /realtime endpoint

Adds VertexAIRealtimeConfig which translates the OpenAI Realtime WebSocket
protocol to Vertex AI BidiGenerateContent. Supports voice in/voice out
(16 kHz mic → 24 kHz speaker) and text in/text out through the proxy's
/realtime endpoint.

Key changes:
- New litellm/llms/vertex_ai/realtime/transformation.py with VertexAIRealtimeConfig
  - Builds correct wss:// URL (regional + global)
  - OAuth2 Bearer token auth (not API key)
  - Full model path (projects/.../publishers/google/models/...)
  - Ignores session.update (Vertex AI only accepts one setup message)
- realtime_api/main.py: vertex_ai branch resolves OAuth token + constructs config
- llm_http_handler.py: auto-sends session setup before bidirectional_forward
- gemini/realtime/transformation.py: fix crashes on empty turnComplete events
- realtime_streaming.py: try/except guard so bad messages don't kill the loop
- proxy_server.py: add missing websockets.exceptions import

* docs: add vertex_realtime to sidebars

* fix: drop unknown event types in Gemini transform; add vertex_ai health check

* fix: propagate UUID fallback IDs from transform_content_done_event to return_additional_content_done_events

* fix: route guardrail backend sends through provider transform; fix str.strip misuse for model prefix

* fix: handle Vertex AI full resource path in session.created; route guardrail block sends through _send_to_backend

* fix: remove unused VertexBase in transformation.py; apply UUID fallback in return_additional_content_done_events
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant