mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-23 05:34:41 +00:00
fix(proxy): add /openai/responses router routes for Azure SDK Responses API (Fixes #45033)
- Register /openai/responses, /compact, /input_tokens, /{id}, /cancel, and /{id}/input_items endpoints on the proxy router
- Update RESPONSES_API_PATHS, _MANAGED_MODEL_ROUTES, _UNBILLED_ROUTES, and LiteLLMRoutes
- Prevent AzureOpenAI Responses API calls from falling into wildcard pass-through
- Add unit test coverage in tests/proxy_unit_tests/test_openai_responses_routes.py
This commit is contained in:
parent
d8c0e2c715
commit
449b37364b
6 changed files with 74 additions and 1 deletions
|
|
@ -454,18 +454,23 @@ class LiteLLMRoutes(enum.Enum):
|
|||
"/responses",
|
||||
"/v1/responses",
|
||||
"/openai/v1/responses",
|
||||
"/openai/responses",
|
||||
"/responses/{response_id}",
|
||||
"/v1/responses/{response_id}",
|
||||
"/openai/v1/responses/{response_id}",
|
||||
"/openai/responses/{response_id}",
|
||||
"/responses/{response_id}/input_items",
|
||||
"/v1/responses/{response_id}/input_items",
|
||||
"/openai/v1/responses/{response_id}/input_items",
|
||||
"/openai/responses/{response_id}/input_items",
|
||||
"/responses/{response_id}/cancel",
|
||||
"/v1/responses/{response_id}/cancel",
|
||||
"/openai/v1/responses/{response_id}/cancel",
|
||||
"/openai/responses/{response_id}/cancel",
|
||||
"/responses/input_tokens",
|
||||
"/v1/responses/input_tokens",
|
||||
"/openai/v1/responses/input_tokens",
|
||||
"/openai/responses/input_tokens",
|
||||
# vector stores
|
||||
"/vector_stores",
|
||||
"/v1/vector_stores",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ _MANAGED_MODEL_ROUTES: Final = frozenset(
|
|||
) | frozenset(
|
||||
(
|
||||
"/openai/v1/responses",
|
||||
"/openai/responses",
|
||||
"/v2/rerank",
|
||||
"/claude_code_gateway/v1/messages",
|
||||
"/claude_code_gateway/v1/messages/count_tokens",
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class CustomOpenAPISpec:
|
|||
"/openai/deployments/{model}/embeddings",
|
||||
]
|
||||
|
||||
RESPONSES_API_PATHS = ["/v1/responses", "/responses", "/openai/v1/responses"]
|
||||
RESPONSES_API_PATHS = ["/v1/responses", "/responses", "/openai/v1/responses", "/openai/responses"]
|
||||
|
||||
@staticmethod
|
||||
def _as_object(node: JsonValue) -> JsonObject:
|
||||
|
|
|
|||
|
|
@ -213,6 +213,12 @@ async def _resolve_cursor_model_variant_before_auth(request: Request) -> None:
|
|||
tags=["responses"],
|
||||
responses=RESPONSES_API_CREATE_RESPONSE_SCHEMAS,
|
||||
)
|
||||
@router.post(
|
||||
"/openai/responses",
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["responses"],
|
||||
responses=RESPONSES_API_CREATE_RESPONSE_SCHEMAS,
|
||||
)
|
||||
async def responses_api(
|
||||
request: Request,
|
||||
fastapi_response: Response,
|
||||
|
|
@ -696,6 +702,12 @@ async def cursor_chat_completions(
|
|||
tags=["responses"],
|
||||
responses=RESPONSES_API_RESPONSE_SCHEMAS,
|
||||
)
|
||||
@router.get(
|
||||
"/openai/responses/{response_id}",
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["responses"],
|
||||
responses=RESPONSES_API_RESPONSE_SCHEMAS,
|
||||
)
|
||||
async def get_response(
|
||||
response_id: str,
|
||||
request: Request,
|
||||
|
|
@ -812,6 +824,12 @@ async def get_response(
|
|||
tags=["responses"],
|
||||
responses=DELETE_RESPONSE_SCHEMAS,
|
||||
)
|
||||
@router.delete(
|
||||
"/openai/responses/{response_id}",
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["responses"],
|
||||
responses=DELETE_RESPONSE_SCHEMAS,
|
||||
)
|
||||
async def delete_response(
|
||||
response_id: str,
|
||||
request: Request,
|
||||
|
|
@ -921,6 +939,12 @@ async def delete_response(
|
|||
tags=["responses"],
|
||||
responses=RESPONSE_ITEM_LIST_SCHEMAS,
|
||||
)
|
||||
@router.get(
|
||||
"/openai/responses/{response_id}/input_items",
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["responses"],
|
||||
responses=RESPONSE_ITEM_LIST_SCHEMAS,
|
||||
)
|
||||
async def get_response_input_items(
|
||||
response_id: str,
|
||||
request: Request,
|
||||
|
|
@ -989,6 +1013,11 @@ async def get_response_input_items(
|
|||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["responses"],
|
||||
)
|
||||
@router.post(
|
||||
"/openai/responses/compact",
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["responses"],
|
||||
)
|
||||
async def compact_response(
|
||||
request: Request,
|
||||
fastapi_response: Response,
|
||||
|
|
@ -1144,6 +1173,11 @@ def _responses_input_as_token_count_messages(
|
|||
dependencies=(_user_api_key_auth_dep,),
|
||||
tags=_RESPONSES_TAGS,
|
||||
)
|
||||
@router.post(
|
||||
"/openai/responses/input_tokens",
|
||||
dependencies=(_user_api_key_auth_dep,),
|
||||
tags=_RESPONSES_TAGS,
|
||||
)
|
||||
async def responses_input_tokens(
|
||||
request: Request,
|
||||
token_counter: _TokenCounter = _token_counter_dep,
|
||||
|
|
@ -1217,6 +1251,11 @@ async def responses_input_tokens(
|
|||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["responses"],
|
||||
)
|
||||
@router.post(
|
||||
"/openai/responses/{response_id}/cancel",
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
tags=["responses"],
|
||||
)
|
||||
async def cancel_response(
|
||||
response_id: str,
|
||||
request: Request,
|
||||
|
|
|
|||
|
|
@ -236,6 +236,7 @@ _UNBILLED_ROUTES: Final[frozenset[str]] = frozenset(
|
|||
"/responses/input_tokens",
|
||||
"/v1/responses/input_tokens",
|
||||
"/openai/v1/responses/input_tokens",
|
||||
"/openai/responses/input_tokens",
|
||||
}
|
||||
)
|
||||
_TOKEN_COUNTING_SEGMENTS: Final[frozenset[str]] = frozenset({"count_tokens", "count-tokens"})
|
||||
|
|
|
|||
27
tests/proxy_unit_tests/test_openai_responses_routes.py
Normal file
27
tests/proxy_unit_tests/test_openai_responses_routes.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
from litellm.proxy._types import LiteLLMRoutes
|
||||
from litellm.proxy.agent_endpoints.auth.managed_authorization import _MANAGED_MODEL_ROUTES
|
||||
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
|
||||
|
||||
|
||||
def test_openai_responses_routes_registered():
|
||||
"""Verify that Azure-style /openai/responses endpoints are registered on the router."""
|
||||
routes = {getattr(r, "path", None) for r in router.routes}
|
||||
|
||||
expected_routes = [
|
||||
"/openai/responses",
|
||||
"/openai/responses/{response_id}",
|
||||
"/openai/responses/{response_id}/input_items",
|
||||
"/openai/responses/compact",
|
||||
"/openai/responses/input_tokens",
|
||||
"/openai/responses/{response_id}/cancel",
|
||||
]
|
||||
|
||||
for expected in expected_routes:
|
||||
assert expected in routes, f"Route {expected} missing from router.routes!"
|
||||
|
||||
assert "/openai/responses" in CustomOpenAPISpec.RESPONSES_API_PATHS
|
||||
assert "/openai/responses" in _MANAGED_MODEL_ROUTES
|
||||
assert "/openai/responses/input_tokens" in _UNBILLED_ROUTES
|
||||
assert "/openai/responses" in LiteLLMRoutes.openai_routes.value
|
||||
Loading…
Add table
Reference in a new issue