mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-16 02:12:01 +00:00
fix(realtime): register /openai/v1/realtime as websocket route
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user