This commit is contained in:
Temo 2026-10-30 22:26:18 +04:00 • committed by GitHub
commit 8dc2607084
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 69 additions and 1 deletions

View file

@ -696,7 +696,7 @@ class RejectedRequestError(BadRequestError):
def __init__(
self,
message,
model,
model: str,
llm_provider,
request_data: dict,
litellm_debug_info: str | None = None,

View file

@ -12933,6 +12933,7 @@ async def chat_completion(
request_data=_data,
)
_chat_response = litellm.ModelResponse()
_chat_response.model = e.model
_chat_response.choices[0].message.content = e.message
if data.get("stream", None) is not None and data["stream"] is True:
@ -13118,6 +13119,7 @@ async def completion(
else:
_response = litellm.TextCompletionResponse()
_response.choices[0].text = e.message
_response.model = e.model
return _response
except Exception as e:
await proxy_logging_obj.post_call_failure_hook(

View file

@ -0,0 +1,66 @@
"""Regression test for the non-streaming RejectedRequestError response.
A pre-call hook that returns a string raises ``RejectedRequestError``, and the
route answers with that string instead of calling the LLM. The non-streaming
response used to be a bare ``ModelResponse()`` with ``model=None``, which the
OpenAI SDK rejects (``model: Input should be a valid string``).
"""
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from fastapi import Request, Response
from openai.types.chat import ChatCompletion
from litellm.exceptions import RejectedRequestError
from litellm.proxy._types import UserAPIKeyAuth
from litellm.proxy.proxy_server import chat_completion, completion
async def _run_rejected_request(route, body):
exception = RejectedRequestError(
message="cached answer",
model=body["model"],
llm_provider="",
request_data=dict(body),
)
with (
patch(
"litellm.proxy.proxy_server.read_request_body",
new_callable=AsyncMock,
return_value=dict(body),
),
patch(
"litellm.proxy.proxy_server.ProxyBaseLLMRequestProcessing.base_process_llm_request",
new_callable=AsyncMock,
side_effect=exception,
),
patch("litellm.proxy.proxy_server.proxy_logging_obj") as mock_proxy_logging,
):
mock_proxy_logging.post_call_failure_hook = AsyncMock()
return await route(
request=MagicMock(spec=Request),
fastapi_response=MagicMock(spec=Response),
user_api_key_dict=UserAPIKeyAuth(),
)
@pytest.mark.asyncio
async def test_rejected_chat_completion_response_has_model():
response = await _run_rejected_request(
chat_completion,
{"model": "my-model", "messages": [{"role": "user", "content": "hi"}]},
)
assert response.model == "my-model"
ChatCompletion.model_validate(response.model_dump())
@pytest.mark.asyncio
async def test_rejected_text_completion_response_has_model():
response = await _run_rejected_request(completion, {"model": "my-model", "prompt": "hi"})
# finish_reason is still None here, so this is not validated against
# openai.types.Completion the way the chat response is.
assert response.model == "my-model"