mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix(openai): preserve embedded provider error status
This commit is contained in:
parent
948a43cd64
commit
2e99d5d5fb
4 changed files with 46 additions and 5 deletions
|
|
@ -786,6 +786,7 @@ class APIError(openai.APIError): # type: ignore
|
|||
litellm_debug_info: Optional[str] = None,
|
||||
max_retries: Optional[int] = None,
|
||||
num_retries: Optional[int] = None,
|
||||
body: dict | None = None,
|
||||
):
|
||||
self.status_code = status_code
|
||||
self.message = "litellm.APIError: {}".format(message)
|
||||
|
|
@ -796,7 +797,7 @@ class APIError(openai.APIError): # type: ignore
|
|||
self.num_retries = num_retries
|
||||
if request is None:
|
||||
request = httpx.Request(method="POST", url="https://api.openai.com/v1")
|
||||
super().__init__(self.message, request=request, body=None) # type: ignore
|
||||
super().__init__(self.message, request=request, body=body) # type: ignore
|
||||
|
||||
def __str__(self):
|
||||
_message = self.message
|
||||
|
|
|
|||
|
|
@ -397,6 +397,7 @@ def _map_openai_exception(
|
|||
model=model,
|
||||
response=getattr(original_exception, "response", None),
|
||||
litellm_debug_info=extra_information,
|
||||
body=getattr(original_exception, "body", None),
|
||||
)
|
||||
elif original_exception.status_code == 401:
|
||||
raise AuthenticationError(
|
||||
|
|
|
|||
|
|
@ -631,14 +631,29 @@ def convert_to_model_response_object(
|
|||
if not response_object.get("choices") or not isinstance(response_object["choices"], Iterable):
|
||||
from litellm.exceptions import APIError
|
||||
|
||||
raise APIError(
|
||||
status_code=500,
|
||||
message=(
|
||||
response_status = response_object.get("status")
|
||||
error_status = (
|
||||
response_status
|
||||
if isinstance(response_status, int)
|
||||
and not isinstance(response_status, bool)
|
||||
and 400 <= response_status <= 599
|
||||
else 500
|
||||
)
|
||||
response_message = response_object.get("response")
|
||||
error_message = (
|
||||
response_message
|
||||
if isinstance(response_message, str) and response_message
|
||||
else (
|
||||
"LiteLLM: provider returned a response with no 'choices'. "
|
||||
f"Raw keys: {list(response_object.keys())}"
|
||||
),
|
||||
)
|
||||
)
|
||||
raise APIError(
|
||||
status_code=error_status,
|
||||
message=error_message,
|
||||
llm_provider="",
|
||||
model="",
|
||||
body=response_object,
|
||||
)
|
||||
|
||||
for idx, choice in enumerate(response_object["choices"]):
|
||||
|
|
|
|||
|
|
@ -7,9 +7,12 @@ import sys
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import Response
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../../../.."))
|
||||
|
||||
import litellm
|
||||
from litellm.llms.openai.openai import OpenAIChatCompletion
|
||||
from litellm.llms.openai.common_utils import OpenAIError
|
||||
|
||||
|
|
@ -126,3 +129,24 @@ class TestEmptyResponseHandling:
|
|||
|
||||
assert response == mock_stream
|
||||
assert headers == {"x-request-id": "123"}
|
||||
|
||||
@respx.mock
|
||||
def test_nonstandard_error_response_preserves_embedded_status_and_body(self):
|
||||
response_body = {"response": "Token is invalid [2]", "status": 400}
|
||||
respx.post("https://gateway.example.com/v1/chat/completions").mock(
|
||||
return_value=Response(200, json=response_body)
|
||||
)
|
||||
|
||||
with pytest.raises(litellm.BadRequestError) as exc_info:
|
||||
litellm.completion(
|
||||
model="openai/test-model",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
api_base="https://gateway.example.com/v1",
|
||||
api_key="test-key",
|
||||
max_retries=0,
|
||||
)
|
||||
|
||||
assert exc_info.value.status_code == 400
|
||||
assert exc_info.value.body["response"] == response_body["response"]
|
||||
assert exc_info.value.body["status"] == response_body["status"]
|
||||
assert "Token is invalid [2]" in exc_info.value.message
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue