fix(realtime): register /openai/v1/realtime as websocket route

This commit is contained in:
Michael Riad Zaky
2026-05-06 13:13:58 -07:00
parent 169c436684
commit 5d7b7e7e37
4 changed files with 38 additions and 0 deletions
+2
View File
@@ -353,8 +353,10 @@ class LiteLLMRoutes(enum.Enum):
# realtime
"/realtime",
"/v1/realtime",
"/openai/v1/realtime",
"/realtime?{model}",
"/v1/realtime?{model}",
"/openai/v1/realtime?{model}",
# responses API
"/responses",
"/v1/responses",
+1
View File
@@ -8805,6 +8805,7 @@ def _realtime_query_params_template(
return tuple(params)
@app.websocket("/openai/v1/realtime")
@app.websocket("/v1/realtime")
@app.websocket("/realtime")
async def realtime_websocket_endpoint(
+1
View File
@@ -825,6 +825,7 @@ API_ROUTE_TO_CALL_TYPES = {
# Realtime API
"/realtime": [CallTypes.arealtime],
"/v1/realtime": [CallTypes.arealtime],
"/openai/v1/realtime": [CallTypes.arealtime],
# Provider-specific routes
"/anthropic/v1/messages": [CallTypes.anthropic_messages],
# Google GenAI routes
@@ -6513,3 +6513,37 @@ async def test_get_current_spend_redis_error_falls_back_to_in_memory():
finally:
ps.spend_counter_cache = orig_counter
ps.prisma_client = orig_prisma
def test_realtime_websocket_route_aliases_registered():
"""Realtime sessions reach the proxy via three path aliases stacked on
`realtime_websocket_endpoint`. Dropping any of them silently 405s
WebSocket upgrades because the catch-all `/openai/{endpoint:path}`
HTTP passthrough only declares HTTP methods. The aliases must also be
in `LiteLLMRoutes.openai_routes` (so non-admin / team / key-scoped
auth allows them) and in `API_ROUTE_TO_CALL_TYPES` (so call-type-aware
logic such as guardrails can resolve the realtime call type)."""
from starlette.routing import WebSocketRoute
from litellm.proxy._types import LiteLLMRoutes
from litellm.proxy.proxy_server import app
from litellm.types.utils import API_ROUTE_TO_CALL_TYPES, CallTypes
websocket_paths = {
route.path for route in app.routes if isinstance(route, WebSocketRoute)
}
openai_routes = LiteLLMRoutes.openai_routes.value
for expected in ("/openai/v1/realtime", "/v1/realtime", "/realtime"):
assert expected in websocket_paths, (
f"{expected!r} missing from registered WebSocket routes; the "
f"realtime endpoint will 405 for clients hitting this path."
)
assert expected in openai_routes, (
f"{expected!r} missing from LiteLLMRoutes.openai_routes; "
f"non-admin / team / key-scoped users will get 403 on this path."
)
assert API_ROUTE_TO_CALL_TYPES.get(expected) == [CallTypes.arealtime], (
f"{expected!r} missing from API_ROUTE_TO_CALL_TYPES; call-type "
f"resolution will return None and break call-type-aware features."
)