feat(realtime): guardrails support for /v1/realtime WebSocket endpoint - #22152
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR adds guardrail support to the
Confidence Score: 3/5
|
| 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
Last reviewed commit: 7af19bf
| 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 |
There was a problem hiding this comment.
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:
| 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 |
#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
|
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? |
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
Relevant issues
Pre-Submission checklist
tests/litellm/directory, Adding at least 1 test is a hard requirement - see detailsmake test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewCI (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/realtimeWebSocket endpoint so guardrails apply to realtime sessions, not just chat completions.Backend (
proxy_server.py)guardrailsquery param (comma-separated list) torealtime_websocket_endpointimport websockets/import websockets.exceptionsat module level (was missing, caused aNameErrorin the existingexcept websockets.exceptions.InvalidStatusCodeclause which silently swallowed errors){"type": "error", "error": {"type": "guardrail_error", "message": "..."}}back to the client before closing with code 1011UI (
RealtimePlayground.tsx,ChatUI.tsx)selectedGuardrailsfrom the playground sidebar now flows through to the WebSocket URL as?guardrails=name1,name2RealtimePlaygroundDocs (
docs/my-website/docs/realtime.md)## Guardrailssection showing how to pass guardrails dynamically via query param (JS + Python examples) and links to key/team-level guardrail setupE2E tested locally:
?guardrails=block-emails: receives the error event then closes with code 1011