From bd89cbf3acb9fcdb9c35d79f4226ef65711ec099 Mon Sep 17 00:00:00 2001 From: whoismonesh Date: Thu, 26 Mar 2026 13:34:49 +0530 Subject: [PATCH] fix(ollama): preserve image_url blocks in ollama_chat multimodal requests Fixes https://github.com/BerriAI/litellm/issues/24615 Four bugs fixed across two files: 1. ollama/chat/transformation.py: extract images BEFORE flattening content to prevent silent regression if text-processing path mutates content 2. ollama/chat/transformation.py: `if images is not None` -> `if images:` Empty list [] is falsy, so the key is now omitted for text-only messages 3. ollama/chat/transformation.py: `if content_str is not None` -> `if content_str:` Empty string "" is falsy, so the key is now omitted for image-only messages 4. ollama/completion/transformation.py: fix /api/show URL construction Strip /api/chat suffix before appending /api/show to prevent 404 errors Updated 2 existing tests to expect correct behavior (empty keys not set) --- litellm/llms/ollama/chat/transformation.py | 6 +++--- litellm/llms/ollama/completion/transformation.py | 3 +++ .../llms/ollama/test_ollama_chat_transformation.py | 9 ++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/litellm/llms/ollama/chat/transformation.py b/litellm/llms/ollama/chat/transformation.py index 3d9618dfed0..58e89e9a011 100644 --- a/litellm/llms/ollama/chat/transformation.py +++ b/litellm/llms/ollama/chat/transformation.py @@ -283,17 +283,17 @@ class OllamaChatConfig(BaseConfig): reasoning_content, parsed_content = _extract_reasoning_content( cast(dict, m) ) - content_str = convert_content_list_to_str(cast(AllMessageValues, m)) images = extract_images_from_message(cast(AllMessageValues, m)) + content_str = convert_content_list_to_str(cast(AllMessageValues, m)) ollama_message = OllamaChatCompletionMessage( role=cast(str, m.get("role")), ) if reasoning_content is not None: ollama_message["thinking"] = reasoning_content - if content_str is not None: + if content_str: ollama_message["content"] = content_str - if images is not None: + if images: ollama_message["images"] = images new_messages.append(ollama_message) diff --git a/litellm/llms/ollama/completion/transformation.py b/litellm/llms/ollama/completion/transformation.py index 6a03325e6c7..960ecc2596a 100644 --- a/litellm/llms/ollama/completion/transformation.py +++ b/litellm/llms/ollama/completion/transformation.py @@ -236,6 +236,9 @@ class OllamaConfig(BaseConfig): api_base = ( api_base or get_secret_str("OLLAMA_API_BASE") or "http://localhost:11434" ) + # Strip /api/chat suffix if present (same logic as get_complete_url in chat/transformation.py) + if api_base.endswith("/api/chat"): + api_base = api_base[:-10] api_key = self.get_api_key() headers = {"Authorization": f"Bearer {api_key}"} if api_key else {} diff --git a/tests/test_litellm/llms/ollama/test_ollama_chat_transformation.py b/tests/test_litellm/llms/ollama/test_ollama_chat_transformation.py index 02495106a84..6f51188f33e 100644 --- a/tests/test_litellm/llms/ollama/test_ollama_chat_transformation.py +++ b/tests/test_litellm/llms/ollama/test_ollama_chat_transformation.py @@ -173,9 +173,9 @@ class TestOllamaChatConfigResponseFormat: headers={}, ) - # Verify empty content becomes empty string + # Verify empty content is not set (empty string is falsy, so key is omitted) assert len(result["messages"]) == 1 - assert result["messages"][0]["content"] == "" + assert "content" not in result["messages"][0] assert result["messages"][0]["role"] == "user" def test_transform_request_image_extraction(self): @@ -321,9 +321,8 @@ class TestOllamaChatConfigResponseFormat: # Verify no images key when no images present assert result["messages"][0]["content"] == "Just text here" # Since extract_images_from_message returns empty list [] when no images found, - # and the code checks "if images is not None", an empty list will still be set - assert "images" in result["messages"][0] - assert result["messages"][0]["images"] == [] + # and the code now checks "if images:" (truthy), empty list is not set + assert "images" not in result["messages"][0] class TestOllamaToolCalling: