From aa05a5e84f6e2f53a6dd3e322b920f68f1e14379 Mon Sep 17 00:00:00 2001 From: Bisma Nawaz Date: Sat, 22 Aug 2026 00:09:28 +0500 Subject: [PATCH 1/2] fix: keep image content lists intact for sambanova vision requests --- litellm/llms/sambanova/chat.py | 34 +++++++++++++++---- .../test_sambanova_chat_transformation.py | 31 +++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/litellm/llms/sambanova/chat.py b/litellm/llms/sambanova/chat.py index 0e7dadf7062..4fe41973dc3 100644 --- a/litellm/llms/sambanova/chat.py +++ b/litellm/llms/sambanova/chat.py @@ -8,12 +8,33 @@ from collections.abc import Coroutine from typing import Any, Final, Literal, overload from litellm.litellm_core_utils.prompt_templates.common_utils import ( - handle_messages_with_content_list_to_str_conversion, + convert_content_list_to_str, ) from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig from litellm.types.llms.openai import AllMessageValues +def _flatten_text_only_messages( + messages: list[AllMessageValues], +) -> list[AllMessageValues]: + """ + Flatten a message's content list to a plain string only when it contains + text alone. A content list that carries any non-text part (e.g. image_url) + is left as a list so that part still reaches the SambaNova API, which + accepts content lists for vision-capable models. Flattening those would + drop the image and silently return a text-only answer. + """ + for message in messages: + content = message.get("content") + if isinstance(content, list) and all( + isinstance(part, dict) and part.get("type") == "text" for part in content + ): + texts = convert_content_list_to_str(message=message) + if texts: + message["content"] = texts + return messages + + class SambanovaConfig(OpenAIGPTConfig): """ Reference: https://docs.sambanova.ai/cloud/api-reference/ @@ -117,14 +138,15 @@ class SambanovaConfig(OpenAIGPTConfig): """ Transform messages to handle content list conversion. - SambaNova API doesn't support content as a list - only string content. - This converts content lists like [{"type": "text", "text": "..."}] to strings. + Text-only content lists like [{"type": "text", "text": "..."}] are + flattened to a plain string. Content lists that carry non-text parts + (e.g. image_url) are left intact so those parts still reach the + SambaNova API, which accepts content lists for vision-capable models. """ async def _async_transform(): - return handle_messages_with_content_list_to_str_conversion(messages) + return _flatten_text_only_messages(messages) if is_async: return _async_transform() - messages = handle_messages_with_content_list_to_str_conversion(messages) - return messages + return _flatten_text_only_messages(messages) diff --git a/tests/llm_translation/test_sambanova_chat_transformation.py b/tests/llm_translation/test_sambanova_chat_transformation.py index c2938d530c5..be13b0986fe 100644 --- a/tests/llm_translation/test_sambanova_chat_transformation.py +++ b/tests/llm_translation/test_sambanova_chat_transformation.py @@ -102,3 +102,34 @@ class TestSambanovaContentListHandling: assert transformed_messages[1]["content"] == "What is the weather?" assert transformed_messages[2]["content"] == "I need your location." assert transformed_messages[3]["content"] == "I'm in San Francisco" + + def test_content_list_with_image_url_is_preserved(self): + """ + A content list carrying a non-text part (image_url) must stay a list so + the image reaches the SambaNova API, instead of being flattened to a + text-only string that silently drops the image. + """ + config = SambanovaConfig() + + image_part = { + "type": "image_url", + "image_url": {"url": "data:image/png;base64,aGVsbG8="}, + } + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "What colors are in this image?"}, + image_part, + ], + } + ] + + transformed_messages = config._transform_messages( + messages=messages, model="sambanova/gpt-oss-120b", is_async=False + ) + + content = transformed_messages[0]["content"] + assert isinstance(content, list) + assert image_part in content + assert any(part.get("type") == "image_url" for part in content) From 1f6b62f535c56c5360d91d98a986ecf0bd8b8aa3 Mon Sep 17 00:00:00 2001 From: Bisma Nawaz Date: Sat, 22 Aug 2026 01:32:20 +0500 Subject: [PATCH 2/2] chore: satisfy ruff format and lint --- litellm/llms/sambanova/chat.py | 4 +--- tests/llm_translation/test_sambanova_chat_transformation.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/litellm/llms/sambanova/chat.py b/litellm/llms/sambanova/chat.py index 4fe41973dc3..c849d52e744 100644 --- a/litellm/llms/sambanova/chat.py +++ b/litellm/llms/sambanova/chat.py @@ -26,9 +26,7 @@ def _flatten_text_only_messages( """ for message in messages: content = message.get("content") - if isinstance(content, list) and all( - isinstance(part, dict) and part.get("type") == "text" for part in content - ): + if isinstance(content, list) and all(isinstance(part, dict) and part.get("type") == "text" for part in content): texts = convert_content_list_to_str(message=message) if texts: message["content"] = texts diff --git a/tests/llm_translation/test_sambanova_chat_transformation.py b/tests/llm_translation/test_sambanova_chat_transformation.py index be13b0986fe..8c58ec6d0e8 100644 --- a/tests/llm_translation/test_sambanova_chat_transformation.py +++ b/tests/llm_translation/test_sambanova_chat_transformation.py @@ -2,7 +2,6 @@ Unit tests for SambaNova chat message transformation """ -import pytest from litellm.llms.sambanova.chat import SambanovaConfig