Skip to content

feat(realtime): guardrails support for /v1/realtime WebSocket endpoint - #22152

Merged
ishaan-jaff merged 3 commits into
mainfrom
worktree-precious-toasting-stroustrup
Feb 26, 2026
Merged

feat(realtime): guardrails support for /v1/realtime WebSocket endpoint#22152
ishaan-jaff merged 3 commits into
mainfrom
worktree-precious-toasting-stroustrup

Conversation

@ishaan-jaff

Copy link
Copy Markdown
Contributor

Relevant issues

Pre-Submission checklist

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

CI (LiteLLM team)

  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature

Changes

Adds guardrail support to the /v1/realtime WebSocket endpoint so guardrails apply to realtime sessions, not just chat completions.

Backend (proxy_server.py)

  • Added guardrails query param (comma-separated list) to realtime_websocket_endpoint
  • Added import websockets / import websockets.exceptions at module level (was missing, caused a NameError in the existing except websockets.exceptions.InvalidStatusCode clause which silently swallowed errors)
  • Split the single try/except into two phases:
    • Phase 1 (pre-call): if a guardrail blocks the request, sends {"type": "error", "error": {"type": "guardrail_error", "message": "..."}} back to the client before closing with code 1011
    • Phase 2 (upstream routing): upstream errors close with 1011 without a misleading guardrail label

UI (RealtimePlayground.tsx, ChatUI.tsx)

  • selectedGuardrails from the playground sidebar now flows through to the WebSocket URL as ?guardrails=name1,name2
  • Previously the prop was never passed down to RealtimePlayground

Docs (docs/my-website/docs/realtime.md)

  • Added ## Guardrails section showing how to pass guardrails dynamically via query param (JS + Python examples) and links to key/team-level guardrail setup

E2E tested locally:

  • Without guardrails: connects normally, close_code=None
  • With ?guardrails=block-emails: receives the error event then closes with code 1011

…endpoint

- Add 'guardrails' query param (comma-separated) to realtime_websocket_endpoint
- Import websockets and websockets.exceptions at module level (fixes NameError in except clause)
- Split try/except into Phase 1 (pre-call) and Phase 2 (routing) so guardrail
  errors send back a typed error event before closing, while upstream errors
  close silently with 1011
@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 Building Building Preview, Comment Feb 26, 2026 5:31am

Request Review

@ishaan-jaff
ishaan-jaff merged commit 82cd14e into main Feb 26, 2026
27 of 35 checks passed
@greptile-apps

greptile-apps Bot commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds guardrail support to the /v1/realtime WebSocket endpoint. Guardrails can be passed as a comma-separated query parameter (?guardrails=name1,name2) which is parsed into a list and fed into the existing common_processing_pre_call_logic pipeline. The UI playground now also threads selectedGuardrails from the sidebar down to the WebSocket URL. A useful side-fix adds the missing import websockets at module level — previously websockets.exceptions.InvalidStatusCode was referenced without an import, which would have caused a NameError at runtime.

  • Backend: New guardrails query param on the realtime endpoint, with error handling split into two phases (pre-call vs upstream routing)
  • Bug: The Phase 1 catch-all labels every pre-call error as "guardrail_error", even for auth, rate-limit, or parsing failures — this will mislead clients
  • UI: selectedGuardrails prop plumbed from ChatUIRealtimePlayground and appended to the WebSocket URL
  • Docs: New Guardrails section with JS/Python examples and error format documentation
  • Missing tests: No tests were added for this feature (the PR checklist also shows tests unchecked)

Confidence Score: 3/5

  • This PR is safe to merge with minor risk — the core guardrails plumbing is correct, but the catch-all error handler mislabels non-guardrail errors.
  • The guardrails integration into the realtime endpoint follows existing patterns (using data["guardrails"] and common_processing_pre_call_logic). The UI changes are clean. However, the error type mislabeling could confuse clients, and no tests were added for this new feature, which makes it harder to verify correctness.
  • Pay attention to litellm/proxy/proxy_server.py — the Phase 1 exception handler (lines 7434–7451) needs to distinguish guardrail errors from other pre-call errors.

Important Files Changed

Filename Overview
litellm/proxy/proxy_server.py Adds guardrails query param to realtime WebSocket endpoint, splits error handling into two phases, and fixes a missing websockets import. However, the Phase 1 catch-all mislabels all pre-call errors as "guardrail_error".
ui/litellm-dashboard/src/components/playground/chat_ui/RealtimePlayground.tsx Adds selectedGuardrails prop and appends it to WebSocket URL as a query param. Correctly updates the useCallback dependency array. Clean implementation.
ui/litellm-dashboard/src/components/playground/chat_ui/ChatUI.tsx Passes selectedGuardrails prop to RealtimePlayground component when guardrails are selected. Simple, correct change.
docs/my-website/docs/realtime.md Adds new Guardrails section with JS and Python examples for passing guardrails via query param. Good documentation with clear error format examples.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Proxy as LiteLLM Proxy<br/>/v1/realtime
    participant PreCall as Pre-Call Pipeline<br/>(auth, guardrails, rate limits)
    participant LLM as Upstream LLM

    Client->>Proxy: WebSocket connect<br/>?model=X&guardrails=name1,name2
    Proxy->>Proxy: Accept WebSocket, parse guardrails query param
    Proxy->>PreCall: common_processing_pre_call_logic(data)

    alt Guardrail blocks request (Phase 1)
        PreCall-->>Proxy: raise Exception
        Proxy->>Client: {"type":"error","error":{"type":"guardrail_error",...}}
        Proxy->>Client: close(1011)
    else Pre-call succeeds
        PreCall-->>Proxy: (data, logging_obj)
        Proxy->>LLM: route_request (Phase 2)
        LLM-->>Proxy: WebSocket stream
        Proxy-->>Client: Relay messages
    end
Loading

Last reviewed commit: 7af19bf

@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.

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +7434 to +7451
except Exception as e:
verbose_proxy_logger.exception("Realtime pre-call error")
try:
await websocket.send_text(
json.dumps(
{
"type": "error",
"error": {
"type": "guardrail_error",
"message": str(e),
},
}
)
)
except Exception:
pass
await websocket.close(code=1011, reason="Pre-call error")
return

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.

All pre-call errors mislabelled as guardrail_error

The except Exception block catches every exception from common_processing_pre_call_logic and labels it as "type": "guardrail_error". However, that method also handles auth validation, rate limiting, model alias resolution, and request parsing — not just guardrails. If, for example, a budget limit is exceeded or request parsing fails, the client will receive a misleading guardrail_error type.

Consider inspecting the exception type to provide a more accurate error label:

Suggested change
except Exception as e:
verbose_proxy_logger.exception("Realtime pre-call error")
try:
await websocket.send_text(
json.dumps(
{
"type": "error",
"error": {
"type": "guardrail_error",
"message": str(e),
},
}
)
)
except Exception:
pass
await websocket.close(code=1011, reason="Pre-call error")
return
except Exception as e:
verbose_proxy_logger.exception("Realtime pre-call error")
error_type = "guardrail_error" if "guardrail" in str(e).lower() else "pre_call_error"
try:
await websocket.send_text(
json.dumps(
{
"type": "error",
"error": {
"type": error_type,
"message": str(e),
},
}
)
)
except Exception:
pass
await websocket.close(code=1011, reason="Pre-call error")
return

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

* feat(realtime): add guardrails query param to /v1/realtime WebSocket endpoint

- Add 'guardrails' query param (comma-separated) to realtime_websocket_endpoint
- Import websockets and websockets.exceptions at module level (fixes NameError in except clause)
- Split try/except into Phase 1 (pre-call) and Phase 2 (routing) so guardrail
  errors send back a typed error event before closing, while upstream errors
  close silently with 1011

* feat(ui): pass selectedGuardrails from sidebar to RealtimePlayground WebSocket URL

* docs(realtime): add guardrails section with dynamic passing examples
@javimp2003uma

Copy link
Copy Markdown

hi @ishaan-jaff , does LiteLLM support realtime speech to speech models guardrails? I mean, from what i saw here https://docs.litellm.ai/docs/proxy/guardrails/realtime_guardrails, LiteLLM guardrails apply to cascade workflows STT->LLM->TTS, also to Speech to Speech native models?

fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
BerriAI#22152)

* feat(realtime): add guardrails query param to /v1/realtime WebSocket endpoint

- Add 'guardrails' query param (comma-separated) to realtime_websocket_endpoint
- Import websockets and websockets.exceptions at module level (fixes NameError in except clause)
- Split try/except into Phase 1 (pre-call) and Phase 2 (routing) so guardrail
  errors send back a typed error event before closing, while upstream errors
  close silently with 1011

* feat(ui): pass selectedGuardrails from sidebar to RealtimePlayground WebSocket URL

* docs(realtime): add guardrails section with dynamic passing examples
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.

2 participants