mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
[Bug fix] Misclassified 500 error on invalid image_url in /chat/completions request (#14149)
* add ImageFetchError * docs ImageFetchError * fix ImageFetchError * test_completion_with_invalid_image_url
This commit is contained in:
parent
2985915357
commit
58b713d19d
5 changed files with 74 additions and 5 deletions
|
|
@ -12,6 +12,7 @@ All exceptions can be imported from `litellm` - e.g. `from litellm import BadReq
|
|||
| 400 | UnsupportedParamsError | litellm.BadRequestError | Raised when unsupported params are passed |
|
||||
| 400 | ContextWindowExceededError| litellm.BadRequestError | Special error type for context window exceeded error messages - enables context window fallbacks |
|
||||
| 400 | ContentPolicyViolationError| litellm.BadRequestError | Special error type for content policy violation error messages - enables content policy fallbacks |
|
||||
| 400 | ImageFetchError | litellm.BadRequestError | Raised when there are errors fetching or processing images |
|
||||
| 400 | InvalidRequestError | openai.BadRequestError | Deprecated error, use BadRequestError instead |
|
||||
| 401 | AuthenticationError | openai.AuthenticationError |
|
||||
| 403 | PermissionDeniedError | openai.PermissionDeniedError |
|
||||
|
|
|
|||
|
|
@ -1261,6 +1261,7 @@ from .exceptions import (
|
|||
AuthenticationError,
|
||||
InvalidRequestError,
|
||||
BadRequestError,
|
||||
ImageFetchError,
|
||||
NotFoundError,
|
||||
RateLimitError,
|
||||
ServiceUnavailableError,
|
||||
|
|
|
|||
|
|
@ -153,6 +153,29 @@ class BadRequestError(openai.BadRequestError): # type: ignore
|
|||
_message += f", LiteLLM Max Retries: {self.max_retries}"
|
||||
return _message
|
||||
|
||||
class ImageFetchError(BadRequestError):
|
||||
def __init__(
|
||||
self,
|
||||
message,
|
||||
model=None,
|
||||
llm_provider=None,
|
||||
response: Optional[httpx.Response] = None,
|
||||
litellm_debug_info: Optional[str] = None,
|
||||
max_retries: Optional[int] = None,
|
||||
num_retries: Optional[int] = None,
|
||||
body: Optional[dict] = None,
|
||||
):
|
||||
super().__init__(
|
||||
message=message,
|
||||
model=model,
|
||||
llm_provider=llm_provider,
|
||||
response=response,
|
||||
litellm_debug_info=litellm_debug_info,
|
||||
max_retries=max_retries,
|
||||
num_retries=num_retries,
|
||||
body=body,
|
||||
)
|
||||
|
||||
|
||||
class UnprocessableEntityError(openai.UnprocessableEntityError): # type: ignore
|
||||
def __init__(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ in_memory_cache = InMemoryCache(max_size_in_memory=MAX_IMGS_IN_MEMORY)
|
|||
|
||||
def _process_image_response(response: Response, url: str) -> str:
|
||||
if response.status_code != 200:
|
||||
raise Exception(
|
||||
raise litellm.ImageFetchError(
|
||||
f"Error: Unable to fetch image from URL. Status code: {response.status_code}, url={url}"
|
||||
)
|
||||
|
||||
|
|
@ -57,9 +57,11 @@ async def async_convert_url_to_base64(url: str) -> str:
|
|||
try:
|
||||
response = await client.get(url, follow_redirects=True)
|
||||
return _process_image_response(response, url)
|
||||
except litellm.ImageFetchError:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
raise Exception(
|
||||
raise litellm.ImageFetchError(
|
||||
f"Error: Unable to fetch image from URL after 3 attempts. url={url}"
|
||||
)
|
||||
|
||||
|
|
@ -74,10 +76,11 @@ def convert_url_to_base64(url: str) -> str:
|
|||
try:
|
||||
response = client.get(url, follow_redirects=True)
|
||||
return _process_image_response(response, url)
|
||||
except litellm.ImageFetchError:
|
||||
raise
|
||||
except Exception as e:
|
||||
verbose_logger.exception(e)
|
||||
# print(e)
|
||||
pass
|
||||
raise Exception(
|
||||
f"Error: Unable to fetch image from URL after 3 attempts. url={url}"
|
||||
raise litellm.ImageFetchError(
|
||||
f"Error: Unable to fetch image from URL after 3 attempts. url={url}",
|
||||
)
|
||||
|
|
|
|||
41
tests/test_litellm/litellm_core_utils/test_image_handling.py
Normal file
41
tests/test_litellm/litellm_core_utils/test_image_handling.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import pytest
|
||||
from httpx import Request, Response
|
||||
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.prompt_templates.image_handling import (
|
||||
convert_url_to_base64,
|
||||
)
|
||||
|
||||
|
||||
class DummyClient:
|
||||
def get(self, url, follow_redirects=True):
|
||||
return Response(status_code=404, request=Request("GET", url))
|
||||
|
||||
|
||||
def test_invalid_image_url_raises_bad_request(monkeypatch):
|
||||
monkeypatch.setattr(litellm, "module_level_client", DummyClient())
|
||||
with pytest.raises(litellm.ImageFetchError) as excinfo:
|
||||
convert_url_to_base64("https://invalid.example/image.png")
|
||||
assert "Unable to fetch image" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_completion_with_invalid_image_url(monkeypatch):
|
||||
monkeypatch.setattr(litellm, "module_level_client", DummyClient())
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "hi"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": "https://invalid.example/image.png"},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
with pytest.raises(litellm.ImageFetchError) as excinfo:
|
||||
litellm.completion(
|
||||
model="gemini/gemini-pro", messages=messages, api_key="test"
|
||||
)
|
||||
assert excinfo.value.status_code == 400
|
||||
assert "Unable to fetch image" in str(excinfo.value)
|
||||
Loading…
Add table
Reference in a new issue