fix: Add a fallback user message to LiteLLM requests if the last user message is empty

Related to #3255
Close #2560

PiperOrigin-RevId: 825143315
This commit is contained in:
George Weale
2025-10-28 11:56:47 -07:00
committed by Copybara-Service
parent 240ef5beea
commit 74d8361a7e
2 changed files with 83 additions and 0 deletions
+48
View File
@@ -155,6 +155,53 @@ def _safe_json_serialize(obj) -> str:
return str(obj)
def _part_has_payload(part: types.Part) -> bool:
"""Checks whether a Part contains usable payload for the model."""
if part.text:
return True
if part.inline_data and part.inline_data.data:
return True
if part.file_data and (part.file_data.file_uri or part.file_data.data):
return True
return False
def _append_fallback_user_content_if_missing(
llm_request: LlmRequest,
) -> None:
"""Ensures there is a user message with content for LiteLLM backends.
Args:
llm_request: The request that may need a fallback user message.
"""
for content in reversed(llm_request.contents):
if content.role == "user":
parts = content.parts or []
if any(_part_has_payload(part) for part in parts):
return
if not parts:
content.parts = []
content.parts.append(
types.Part.from_text(
text="Handle the requests as specified in the System Instruction."
)
)
return
llm_request.contents.append(
types.Content(
role="user",
parts=[
types.Part.from_text(
text=(
"Handle the requests as specified in the System"
" Instruction."
)
),
],
)
)
def _content_to_message_param(
content: types.Content,
) -> Union[Message, list[Message]]:
@@ -818,6 +865,7 @@ class LiteLlm(BaseLlm):
"""
self._maybe_append_user_content(llm_request)
_append_fallback_user_content_if_missing(llm_request)
logger.debug(_build_request_log(llm_request))
messages, tools, response_format, generation_params = (
+35
View File
@@ -548,6 +548,41 @@ async def test_generate_content_async(mock_acompletion, lite_llm_instance):
)
@pytest.mark.asyncio
async def test_generate_content_async_adds_fallback_user_message(
mock_acompletion, lite_llm_instance
):
llm_request = LlmRequest(
contents=[
types.Content(
role="user",
parts=[],
)
]
)
async for _ in lite_llm_instance.generate_content_async(llm_request):
pass
mock_acompletion.assert_called_once()
_, kwargs = mock_acompletion.call_args
user_messages = [
message for message in kwargs["messages"] if message["role"] == "user"
]
assert any(
message.get("content")
== "Handle the requests as specified in the System Instruction."
for message in user_messages
)
assert (
sum(1 for content in llm_request.contents if content.role == "user") == 1
)
assert llm_request.contents[-1].parts[0].text == (
"Handle the requests as specified in the System Instruction."
)
litellm_append_user_content_test_cases = [
pytest.param(
LlmRequest(