From 573df855d359c237d8d976d4708783e742cc8de4 Mon Sep 17 00:00:00 2001 From: Cesar Garcia <128240629+Chesars@users.noreply.github.com> Date: Mon, 12 Jan 2026 08:38:32 -0300 Subject: [PATCH] fix(oci): handle OpenAI-style image_url object in multimodal messages (#18272) The OCI adapter now accepts both string and object formats for image_url: - String: "image_url": "https://example.com/image.png" - Object: "image_url": {"url": "https://example.com/image.png"} This fixes compatibility with OpenAI Vision API format. --- litellm/llms/oci/chat/transformation.py | 5 +- .../oci/chat/test_oci_chat_transformation.py | 87 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/litellm/llms/oci/chat/transformation.py b/litellm/llms/oci/chat/transformation.py index 038895a39e..7af7be2094 100644 --- a/litellm/llms/oci/chat/transformation.py +++ b/litellm/llms/oci/chat/transformation.py @@ -1124,8 +1124,11 @@ def adapt_messages_to_generic_oci_standard_content_message( elif type == "image_url": image_url = content_item.get("image_url") + # Handle both OpenAI format (object with url) and string format + if isinstance(image_url, dict): + image_url = image_url.get("url") if not isinstance(image_url, str): - raise Exception("Prop `image_url` is not a string") + raise Exception("Prop `image_url` must be a string or an object with a `url` property") new_content.append(OCIImageContentPart(imageUrl=image_url)) return OCIMessage( diff --git a/tests/litellm/llms/oci/chat/test_oci_chat_transformation.py b/tests/litellm/llms/oci/chat/test_oci_chat_transformation.py index c23f4501a2..f706a025a0 100644 --- a/tests/litellm/llms/oci/chat/test_oci_chat_transformation.py +++ b/tests/litellm/llms/oci/chat/test_oci_chat_transformation.py @@ -197,3 +197,90 @@ class TestOCIGetCompleteUrl: assert "eu-frankfurt-1" in url assert "inference.generativeai" in url + + +class TestOCIImageUrlTransformation: + """Tests for OCI image_url format handling in multimodal messages. + + Fixes: https://github.com/BerriAI/litellm/issues/18270 + """ + + def test_image_url_as_string(self): + """Test that image_url as a plain string works.""" + from litellm.llms.oci.chat.transformation import adapt_messages_to_generic_oci_standard + + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "What is in this image?"}, + {"type": "image_url", "image_url": "https://example.com/image.png"}, + ], + } + ] + + result = adapt_messages_to_generic_oci_standard(messages) + + assert len(result) == 1 + assert result[0].role == "USER" + assert len(result[0].content) == 2 + assert result[0].content[1].imageUrl == "https://example.com/image.png" + + def test_image_url_as_openai_object(self): + """Test that image_url as OpenAI-style object {"url": "..."} works.""" + from litellm.llms.oci.chat.transformation import adapt_messages_to_generic_oci_standard + + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "What is in this image?"}, + {"type": "image_url", "image_url": {"url": "https://example.com/image.png"}}, + ], + } + ] + + result = adapt_messages_to_generic_oci_standard(messages) + + assert len(result) == 1 + assert result[0].role == "USER" + assert len(result[0].content) == 2 + assert result[0].content[1].imageUrl == "https://example.com/image.png" + + def test_image_url_invalid_type_raises_error(self): + """Test that invalid image_url type raises an error.""" + from litellm.llms.oci.chat.transformation import adapt_messages_to_generic_oci_standard + + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "What is in this image?"}, + {"type": "image_url", "image_url": 12345}, # Invalid type + ], + } + ] + + with pytest.raises(Exception) as exc_info: + adapt_messages_to_generic_oci_standard(messages) + + assert "image_url" in str(exc_info.value) + + def test_image_url_object_missing_url_raises_error(self): + """Test that object without 'url' property raises an error.""" + from litellm.llms.oci.chat.transformation import adapt_messages_to_generic_oci_standard + + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "What is in this image?"}, + {"type": "image_url", "image_url": {"detail": "high"}}, # Missing 'url' + ], + } + ] + + with pytest.raises(Exception) as exc_info: + adapt_messages_to_generic_oci_standard(messages) + + assert "image_url" in str(exc_info.value)