From 4a486dc669867df037658e57616f86fe923c0647 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 13 Nov 2025 17:03:14 -0800 Subject: [PATCH] [Bug fix] Fixes SambaNova API rejecting requests when message content is passed as a list format (#16612) * add runwayml_models * test_call_with_end_user_over_budget * TestSambanovaContentListHandling * add _transform_messages for sambanova --- litellm/llms/sambanova/chat.py | 33 ++++- .../test_sambanova_chat_transformation.py | 127 ++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 tests/llm_translation/test_sambanova_chat_transformation.py diff --git a/litellm/llms/sambanova/chat.py b/litellm/llms/sambanova/chat.py index 57a39ec8bbc..b0534347c9a 100644 --- a/litellm/llms/sambanova/chat.py +++ b/litellm/llms/sambanova/chat.py @@ -4,9 +4,13 @@ Sambanova Chat Completions API this is OpenAI compatible - no translation needed / occurs """ -from typing import Optional, Union +from typing import Any, Coroutine, List, Literal, Optional, Union, overload +from litellm.litellm_core_utils.prompt_templates.common_utils import ( + handle_messages_with_content_list_to_str_conversion, +) from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig +from litellm.types.llms.openai import AllMessageValues class SambanovaConfig(OpenAIGPTConfig): @@ -92,3 +96,30 @@ class SambanovaConfig(OpenAIGPTConfig): elif param in supported_openai_params: optional_params[param] = value return optional_params + + @overload + def _transform_messages( + self, messages: List[AllMessageValues], model: str, is_async: Literal[True] + ) -> Coroutine[Any, Any, List[AllMessageValues]]: + ... + + @overload + def _transform_messages( + self, + messages: List[AllMessageValues], + model: str, + is_async: Literal[False] = False, + ) -> List[AllMessageValues]: + ... + + def _transform_messages( + self, messages: List[AllMessageValues], model: str, is_async: bool = False + ) -> Union[List[AllMessageValues], Coroutine[Any, Any, List[AllMessageValues]]]: + """ + 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. + """ + messages = handle_messages_with_content_list_to_str_conversion(messages) + return messages diff --git a/tests/llm_translation/test_sambanova_chat_transformation.py b/tests/llm_translation/test_sambanova_chat_transformation.py new file mode 100644 index 00000000000..368c09931db --- /dev/null +++ b/tests/llm_translation/test_sambanova_chat_transformation.py @@ -0,0 +1,127 @@ +""" +Unit tests for SambaNova chat message transformation +""" +import pytest +from litellm.llms.sambanova.chat import SambanovaConfig + + +class TestSambanovaContentListHandling: + """ + Test that SambaNova properly transforms content lists to strings + """ + + def test_content_list_to_string_transformation(self): + """ + Test content list with text objects is converted to string. + + SambaNova API doesn't support content as a list - only string content. + """ + config = SambanovaConfig() + + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "Hello, how are you?"} + ] + } + ] + + transformed_messages = config._transform_messages( + messages=messages, + model="sambanova/gpt-oss-120b", + is_async=False + ) + + assert len(transformed_messages) == 1 + assert transformed_messages[0]["role"] == "user" + assert isinstance(transformed_messages[0]["content"], str) + assert transformed_messages[0]["content"] == "Hello, how are you?" + + def test_content_list_multiple_text_blocks(self): + """ + Test content list with multiple text blocks is converted to concatenated string. + """ + config = SambanovaConfig() + + messages = [ + { + "role": "user", + "content": [ + {"type": "text", "text": "Hello, "}, + {"type": "text", "text": "how are you?"} + ] + } + ] + + transformed_messages = config._transform_messages( + messages=messages, + model="sambanova/gpt-oss-120b", + is_async=False + ) + + assert transformed_messages[0]["content"] == "Hello, how are you?" + + def test_string_content_unchanged(self): + """ + Test that string content is passed through unchanged. + """ + config = SambanovaConfig() + + messages = [ + { + "role": "user", + "content": "Hello, how are you?" + } + ] + + transformed_messages = config._transform_messages( + messages=messages, + model="sambanova/gpt-oss-120b", + is_async=False + ) + + assert transformed_messages[0]["content"] == "Hello, how are you?" + + def test_multiple_messages_transformation(self): + """ + Test transformation of multiple messages with mixed content types. + """ + config = SambanovaConfig() + + messages = [ + { + "role": "system", + "content": "You are a helpful assistant." + }, + { + "role": "user", + "content": [ + {"type": "text", "text": "What is the weather?"} + ] + }, + { + "role": "assistant", + "content": "I need your location." + }, + { + "role": "user", + "content": [ + {"type": "text", "text": "I'm in "}, + {"type": "text", "text": "San Francisco"} + ] + } + ] + + transformed_messages = config._transform_messages( + messages=messages, + model="sambanova/gpt-oss-120b", + is_async=False + ) + + assert len(transformed_messages) == 4 + assert transformed_messages[0]["content"] == "You are a helpful assistant." + 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" +