mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
fix: merge responses developer messages into system prompt
This commit is contained in:
parent
d3891e6eae
commit
7356b87757
2 changed files with 167 additions and 1 deletions
|
|
@ -282,7 +282,81 @@ class LiteLLMCompletionResponsesConfig:
|
|||
)
|
||||
)
|
||||
|
||||
return messages
|
||||
return LiteLLMCompletionResponsesConfig._merge_responses_system_messages(
|
||||
messages=messages
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_text_from_message_content(content: Any) -> str:
|
||||
if content is None:
|
||||
return ""
|
||||
if isinstance(content, str):
|
||||
return content
|
||||
if isinstance(content, list):
|
||||
text_parts: List[str] = []
|
||||
for content_part in content:
|
||||
if isinstance(content_part, str):
|
||||
text_parts.append(content_part)
|
||||
elif isinstance(content_part, dict):
|
||||
content_type = content_part.get("type")
|
||||
text = content_part.get("text")
|
||||
if content_type in {"text", "input_text"} and isinstance(text, str):
|
||||
text_parts.append(text)
|
||||
return "\n\n".join(text_parts)
|
||||
return str(content)
|
||||
|
||||
@staticmethod
|
||||
def _merge_responses_system_messages(
|
||||
messages: List[
|
||||
Union[
|
||||
AllMessageValues,
|
||||
GenericChatCompletionMessage,
|
||||
ChatCompletionMessageToolCall,
|
||||
ChatCompletionResponseMessage,
|
||||
Message,
|
||||
]
|
||||
],
|
||||
) -> List[
|
||||
Union[
|
||||
AllMessageValues,
|
||||
GenericChatCompletionMessage,
|
||||
ChatCompletionMessageToolCall,
|
||||
ChatCompletionResponseMessage,
|
||||
Message,
|
||||
]
|
||||
]:
|
||||
system_parts: List[str] = []
|
||||
non_system_messages: List[
|
||||
Union[
|
||||
AllMessageValues,
|
||||
GenericChatCompletionMessage,
|
||||
ChatCompletionMessageToolCall,
|
||||
ChatCompletionResponseMessage,
|
||||
Message,
|
||||
]
|
||||
] = []
|
||||
|
||||
for message in messages:
|
||||
role = message.get("role") if isinstance(message, dict) else None
|
||||
if role in {"system", "developer"}:
|
||||
content = message.get("content") if isinstance(message, dict) else None
|
||||
text_content = (
|
||||
LiteLLMCompletionResponsesConfig._get_text_from_message_content(
|
||||
content=content
|
||||
)
|
||||
)
|
||||
if text_content:
|
||||
system_parts.append(text_content)
|
||||
continue
|
||||
non_system_messages.append(message)
|
||||
|
||||
if not system_parts:
|
||||
return messages
|
||||
|
||||
system_message = ChatCompletionSystemMessage(
|
||||
role="system", content="\n\n".join(system_parts)
|
||||
)
|
||||
return [system_message, *non_system_messages]
|
||||
|
||||
@staticmethod
|
||||
async def async_responses_api_session_handler(
|
||||
|
|
|
|||
|
|
@ -808,6 +808,98 @@ class TestFunctionCallTransformation:
|
|||
|
||||
assert result["extra_headers"] == {"X-Test-Header": "test-value"}
|
||||
|
||||
def test_request_transformation_merges_instructions_and_developer_messages(self):
|
||||
"""Responses instructions and developer messages should become one system message."""
|
||||
test_input = [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "developer",
|
||||
"content": "Use concise responses.",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"role": "developer",
|
||||
"content": [{"type": "input_text", "text": "Never expose secrets."}],
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": "Hello",
|
||||
},
|
||||
]
|
||||
responses_api_request = {"instructions": "You are helpful."}
|
||||
|
||||
result = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
|
||||
model="custom_openai/qwen",
|
||||
input=test_input,
|
||||
responses_api_request=responses_api_request,
|
||||
)
|
||||
|
||||
messages = result["messages"]
|
||||
assert messages == [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are helpful.\n\nUse concise responses.\n\nNever expose secrets.",
|
||||
},
|
||||
{"role": "user", "content": "Hello"},
|
||||
]
|
||||
|
||||
def test_request_transformation_preserves_messages_without_system_content(self):
|
||||
"""Requests without instructions/developer content should not be rewritten."""
|
||||
test_input = [
|
||||
{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": "Hello",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": "Hi there",
|
||||
},
|
||||
]
|
||||
|
||||
result = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
|
||||
model="custom_openai/qwen",
|
||||
input=test_input,
|
||||
responses_api_request={},
|
||||
)
|
||||
|
||||
assert result["messages"] == [
|
||||
{"role": "user", "content": "Hello"},
|
||||
{"role": "assistant", "content": "Hi there"},
|
||||
]
|
||||
|
||||
def test_merge_responses_system_messages_extracts_supported_text_content(self):
|
||||
"""System/developer content blocks should merge only supported text parts."""
|
||||
messages = [
|
||||
{"role": "system", "content": None},
|
||||
{
|
||||
"role": "developer",
|
||||
"content": [
|
||||
"Plain text",
|
||||
{"type": "text", "text": "Text block"},
|
||||
{"type": "input_text", "text": "Input text block"},
|
||||
{"type": "image_url", "text": "Ignored image text"},
|
||||
{"type": "text", "text": 123},
|
||||
],
|
||||
},
|
||||
{"role": "developer", "content": 123},
|
||||
{"role": "user", "content": "Hello"},
|
||||
]
|
||||
|
||||
result = LiteLLMCompletionResponsesConfig._merge_responses_system_messages(
|
||||
messages=messages
|
||||
)
|
||||
|
||||
assert result == [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "Plain text\n\nText block\n\nInput text block\n\n123",
|
||||
},
|
||||
{"role": "user", "content": "Hello"},
|
||||
]
|
||||
|
||||
def test_function_call_without_call_id_fallback_to_id(self):
|
||||
"""Test that function_call items can use 'id' field when 'call_id' is missing"""
|
||||
function_call_item = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue