fix(responses): keep bare strings in a system content part list

Filtering a content list down to dicts dropped plain strings, which
upstream normalization leaves in place, so part of the system prompt
went missing from the merged message.
This commit is contained in:
Animesh Kumar 2026-09-11 22:39:50 +05:30
parent e327aefe3b
commit 18de9e6eff
2 changed files with 29 additions and 4 deletions

View file

@ -468,13 +468,24 @@ class LiteLLMCompletionResponsesConfig:
the backends that reject a trailing system message are the same ones that expect one.
"""
texts: Final = tuple(
content
if isinstance(content, str)
else "\n\n".join(part.get("text", "") for part in content if isinstance(part, dict))
for content in contents
text for content in contents for text in LiteLLMCompletionResponsesConfig._system_content_texts(content)
)
return ChatCompletionSystemMessage(role="system", content="\n\n".join(text for text in texts if text))
@staticmethod
def _system_content_texts(content: object) -> tuple[str, ...]:
"""Every text a system content field carries, as a plain string or as a part list
that may mix bare strings with text parts."""
if isinstance(content, str):
return (content,)
if not isinstance(content, (list, tuple)):
return ()
return tuple(
part if isinstance(part, str) else str(part.get("text", ""))
for part in content
if isinstance(part, (str, dict))
)
@staticmethod
async def async_responses_api_session_handler(
previous_response_id: str,

View file

@ -73,3 +73,17 @@ def test_a_conversation_without_a_system_message_is_unchanged():
roles, _ = _roles([{"role": "user", "content": "q"}], {})
assert roles == ["user"]
def test_bare_strings_in_a_part_list_survive_the_merge():
"""Upstream normalization leaves plain strings in a content list, so filtering the
list down to dicts silently dropped part of the prompt."""
_, messages = _roles(
[
{"role": "user", "content": "q"},
{"role": "system", "content": ["B", {"type": "text", "text": "C"}]},
],
{"instructions": "A"},
)
assert messages[0]["content"] == "A\n\nB\n\nC"