fix: download and base64-encode remote image URLs for ollama_chat

Ollama does not accept image URLs, so extract_images_from_message must send pure
base64. Remote http(s) image_url values were forwarded verbatim, making Ollama
fail with "illegal base64 data at input byte 5" (the ":" in "https:"). Download
and base64-encode them, mirroring the data:-URL handling fixed in #18465.
This commit is contained in:
Eric (GabiDevFamily) 2026-06-13 17:45:24 +02:00
parent ec9353cb69
commit dcf5a2433f
2 changed files with 43 additions and 11 deletions

View file

@ -1555,13 +1555,29 @@ def _extract_base64_data(image_url: str) -> str:
return image_url
def _extract_ollama_image_data(image_url: str) -> str:
"""
Return pure base64 image data for providers like Ollama that do not accept URLs.
Remote http(s) URLs are downloaded and base64-encoded; data URLs have their base64
payload extracted; anything else is returned unchanged.
"""
if image_url.startswith(("http://", "https://")):
from litellm.litellm_core_utils.prompt_templates.image_handling import (
convert_url_to_base64,
)
image_url = convert_url_to_base64(image_url)
return _extract_base64_data(image_url)
def extract_images_from_message(message: AllMessageValues) -> List[str]:
"""
Extract images from a message.
For data URLs (e.g., "data:image/png;base64,iVBOR..."), only the base64
data portion is extracted. This is required for providers like Ollama
that expect pure base64 data rather than full data URLs.
For data URLs (e.g., "data:image/png;base64,iVBOR..."), only the base64 data portion is
extracted, and remote http(s) URLs are downloaded and base64-encoded. This is required for
providers like Ollama that expect pure base64 data rather than URLs or full data URLs.
"""
images = []
message_content = message.get("content")
@ -1570,9 +1586,9 @@ def extract_images_from_message(message: AllMessageValues) -> List[str]:
image_url = m.get("image_url")
if image_url:
if isinstance(image_url, str):
images.append(_extract_base64_data(image_url))
images.append(_extract_ollama_image_data(image_url))
elif isinstance(image_url, dict) and "url" in image_url:
images.append(_extract_base64_data(image_url["url"]))
images.append(_extract_ollama_image_data(image_url["url"]))
return images

View file

@ -7,6 +7,8 @@ which fixes the Ollama error "illegal base64 data at input byte 4".
Related issue: https://github.com/BerriAI/litellm/issues/18338
"""
from unittest.mock import patch
import pytest
from litellm.litellm_core_utils.prompt_templates.common_utils import (
@ -91,8 +93,12 @@ class TestExtractImagesFromMessage:
result = extract_images_from_message(message)
assert result == ["iVBORw0KGgo"]
def test_extract_from_message_with_regular_url(self):
"""Test that regular URLs are preserved"""
def test_extract_from_message_with_remote_url_downloads_and_encodes(self):
"""Remote http(s) URLs must be downloaded and base64-encoded (issue #30313).
Ollama does not accept URLs, so forwarding one verbatim made it fail with
"illegal base64 data at input byte 5".
"""
message = {
"role": "user",
"content": [
@ -102,8 +108,14 @@ class TestExtractImagesFromMessage:
}
],
}
result = extract_images_from_message(message)
assert result == ["https://example.com/image.png"]
with patch(
"litellm.litellm_core_utils.prompt_templates.image_handling.convert_url_to_base64",
return_value="data:image/png;base64,ZmV0Y2hlZA==",
) as mock_convert:
result = extract_images_from_message(message)
mock_convert.assert_called_once_with("https://example.com/image.png")
assert result == ["ZmV0Y2hlZA=="]
def test_extract_multiple_images(self):
"""Test extracting multiple images from a single message"""
@ -124,11 +136,15 @@ class TestExtractImagesFromMessage:
},
],
}
result = extract_images_from_message(message)
with patch(
"litellm.litellm_core_utils.prompt_templates.image_handling.convert_url_to_base64",
return_value="data:image/png;base64,image3base64",
):
result = extract_images_from_message(message)
assert result == [
"image1base64",
"image2base64",
"https://example.com/image3.png",
"image3base64",
]
def test_empty_content(self):