From 4fff05f1cce716876315ea0cf262f4e225abdcb9 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 27 Aug 2025 12:12:47 -0700 Subject: [PATCH] [Feature]: Support Gemini requests with only system prompt (#14010) * _default_user_message_when_system_message_passed * test_system_prompt_only_adds_blank_user_message * test_system_message_with_no_user_message --- .../llms/vertex_ai/gemini/transformation.py | 15 ++++++++ tests/llm_translation/base_llm_unit_tests.py | 22 ++++++++++++ .../llms/vertex_ai/test_vertex.py | 34 +++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/litellm/llms/vertex_ai/gemini/transformation.py b/litellm/llms/vertex_ai/gemini/transformation.py index 85e3f15364..8ab212e255 100644 --- a/litellm/llms/vertex_ai/gemini/transformation.py +++ b/litellm/llms/vertex_ai/gemini/transformation.py @@ -35,6 +35,7 @@ from litellm.types.llms.openai import ( ChatCompletionFileObject, ChatCompletionImageObject, ChatCompletionTextObject, + ChatCompletionUserMessage, ) from litellm.types.llms.vertex_ai import * from litellm.types.llms.vertex_ai import ( @@ -475,6 +476,13 @@ async def async_transform_request_body( optional_params=optional_params, ) +def _default_user_message_when_system_message_passed() -> ChatCompletionUserMessage: + """ + Returns a default user message when a "system" message is passed in gemini fails. + + This adds a blank user message to the messages list, to ensure that gemini doesn't fail the request. + """ + return ChatCompletionUserMessage(content=".", role="user") def _transform_system_message( supports_system_message: bool, messages: List[AllMessageValues] @@ -510,6 +518,13 @@ def _transform_system_message( messages.pop(idx) if len(system_content_blocks) > 0: + ######################################################### + # If no messages are passed in, add a blank user message + # Relevant Issue - https://github.com/BerriAI/litellm/issues/13769 + ######################################################### + if len(messages) == 0: + messages.append(_default_user_message_when_system_message_passed()) + ######################################################### return SystemInstructions(parts=system_content_blocks), messages return None, messages diff --git a/tests/llm_translation/base_llm_unit_tests.py b/tests/llm_translation/base_llm_unit_tests.py index 4c24d1fcd5..9e2af87178 100644 --- a/tests/llm_translation/base_llm_unit_tests.py +++ b/tests/llm_translation/base_llm_unit_tests.py @@ -119,6 +119,28 @@ class BaseLLMChatTest(ABC): pytest.skip("Model is overloaded") assert response.choices[0].message.content is not None + + def test_system_message_with_no_user_message(self): + """ + Test that the system message is translated correctly for non-OpenAI providers. + """ + base_completion_call_args = self.get_base_completion_call_args() + messages = [ + { + "role": "system", + "content": "Be a good bot!", + }, + ] + try: + response = self.completion_function( + **base_completion_call_args, + messages=messages, + ) + assert response is not None + except litellm.InternalServerError: + pytest.skip("Model is overloaded") + + assert response.choices[0].message.content is not None def test_content_list_handling(self): """Check if content list is supported by LLM API""" diff --git a/tests/test_litellm/llms/vertex_ai/test_vertex.py b/tests/test_litellm/llms/vertex_ai/test_vertex.py index 07b0cbc623..7e683d1f54 100644 --- a/tests/test_litellm/llms/vertex_ai/test_vertex.py +++ b/tests/test_litellm/llms/vertex_ai/test_vertex.py @@ -1469,3 +1469,37 @@ def test_vertex_parallel_tool_calls_false_single_tool(): parallel_tool_calls=False, ) assert "tools" in optional_params + + +from litellm.llms.vertex_ai.gemini.transformation import _transform_request_body + + +def test_system_prompt_only_adds_blank_user_message(): + """ + Test that the system prompt only adds a blank user message when a system message is passed in. + + Relevant Issue - https://github.com/BerriAI/litellm/issues/13769 + """ + SYSTEM_INSTRUCTION = "System instructions for the model" + data = _transform_request_body( + messages=[{"role": "system", "content": SYSTEM_INSTRUCTION}], + model="gemini-2.5-flash", + optional_params={}, + custom_llm_provider="vertex_ai", + litellm_params={}, + cached_content=None, + ) + print("Final data: ", data) + + # validate that a blank user message is added when a system message is passed in + assert len(data["contents"]) == 1 + first_content = data["contents"][0] + assert first_content["role"] == "user" + assert len(first_content["parts"]) == 1 + + + ######################################################### + # system message was passed in + ######################################################### + assert len(data["system_instruction"]) == 1 + assert data["system_instruction"]["parts"][0]["text"] == SYSTEM_INSTRUCTION