fix(proxy): add /openai/responses to API_ROUTE_TO_CALL_TYPES and Prometheus route normalization

- Map /openai/responses routes in API_ROUTE_TO_CALL_TYPES for tool allowlist and guardrail validation
- Add regex normalization patterns for /openai/responses/{response_id} in normalize_request_route
- Expand test suite in tests/proxy_unit_tests/test_openai_responses_routes.py
This commit is contained in:
Chau Vu / CPF-FAMILY 2026-10-08 21:47:06 +07:00
parent 449b37364b
commit 4064e59aa1
3 changed files with 22 additions and 0 deletions

View file

@ -857,6 +857,9 @@ def normalize_request_route(route: str) -> str:
(r"^(/(?:openai/)?v1/responses)/([^/]+)(/input_items)$", r"\1/{response_id}\3"),
(r"^(/(?:openai/)?v1/responses)/([^/]+)(/cancel)$", r"\1/{response_id}\3"),
(r"^(/(?:openai/)?v1/responses)/([^/]+)$", r"\1/{response_id}"),
(r"^(/openai/responses)/([^/]+)(/input_items)$", r"\1/{response_id}\3"),
(r"^(/openai/responses)/([^/]+)(/cancel)$", r"\1/{response_id}\3"),
(r"^(/openai/responses)/([^/]+)$", r"\1/{response_id}"),
(r"^(/responses)/([^/]+)(/input_items)$", r"\1/{response_id}\3"),
(r"^(/responses)/([^/]+)(/cancel)$", r"\1/{response_id}\3"),
(r"^(/responses)/([^/]+)$", r"\1/{response_id}"),

View file

@ -1038,12 +1038,15 @@ API_ROUTE_TO_CALL_TYPES: Final[Mapping[str, Sequence[CallTypes]]] = {
"/responses": (CallTypes.aresponses, CallTypes.responses),
"/v1/responses": (CallTypes.aresponses, CallTypes.responses),
"/openai/v1/responses": (CallTypes.aresponses, CallTypes.responses),
"/openai/responses": (CallTypes.aresponses, CallTypes.responses),
"/responses/{response_id}": (CallTypes.aresponses, CallTypes.responses),
"/v1/responses/{response_id}": (CallTypes.aresponses, CallTypes.responses),
"/openai/v1/responses/{response_id}": (CallTypes.aresponses, CallTypes.responses),
"/openai/responses/{response_id}": (CallTypes.aresponses, CallTypes.responses),
"/responses/{response_id}/input_items": (CallTypes.alist_input_items,),
"/v1/responses/{response_id}/input_items": (CallTypes.alist_input_items,),
"/openai/v1/responses/{response_id}/input_items": (CallTypes.alist_input_items,),
"/openai/responses/{response_id}/input_items": (CallTypes.alist_input_items,),
# Realtime API
"/realtime": [CallTypes.arealtime],
"/v1/realtime": [CallTypes.arealtime],

View file

@ -1,8 +1,10 @@
from litellm.proxy._types import LiteLLMRoutes
from litellm.proxy.agent_endpoints.auth.managed_authorization import _MANAGED_MODEL_ROUTES
from litellm.proxy.auth.auth_utils import normalize_request_route
from litellm.proxy.common_utils.custom_openapi_spec import CustomOpenAPISpec
from litellm.proxy.response_api_endpoints.endpoints import router
from litellm.proxy.spend_tracking.budget_reservation import _UNBILLED_ROUTES
from litellm.types.utils import API_ROUTE_TO_CALL_TYPES, CallTypes
def test_openai_responses_routes_registered():
@ -25,3 +27,17 @@ def test_openai_responses_routes_registered():
assert "/openai/responses" in _MANAGED_MODEL_ROUTES
assert "/openai/responses/input_tokens" in _UNBILLED_ROUTES
assert "/openai/responses" in LiteLLMRoutes.openai_routes.value
def test_openai_responses_api_route_to_call_types():
"""Verify that /openai/responses mappings exist in API_ROUTE_TO_CALL_TYPES for guardrails/tools."""
assert API_ROUTE_TO_CALL_TYPES.get("/openai/responses") == (CallTypes.aresponses, CallTypes.responses)
assert API_ROUTE_TO_CALL_TYPES.get("/openai/responses/{response_id}") == (CallTypes.aresponses, CallTypes.responses)
assert API_ROUTE_TO_CALL_TYPES.get("/openai/responses/{response_id}/input_items") == (CallTypes.alist_input_items,)
def test_openai_responses_metrics_normalization():
"""Verify that dynamic response IDs are normalized to prevent Prometheus high-cardinality metrics."""
assert normalize_request_route("/openai/responses/resp-123456") == "/openai/responses/{response_id}"
assert normalize_request_route("/openai/responses/resp-123456/cancel") == "/openai/responses/{response_id}/cancel"
assert normalize_request_route("/openai/responses/resp-123456/input_items") == "/openai/responses/{response_id}/input_items"