mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix: merge developer role into leading system message
This commit is contained in:
parent
3583ac1159
commit
079d6e40fd
2 changed files with 127 additions and 6 deletions
|
|
@ -5,7 +5,7 @@ Utility functions for base LLM classes.
|
|||
import copy
|
||||
import json
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, List, Optional, Type, Union
|
||||
from typing import Any, Dict, List, Optional, Type, Union, cast
|
||||
|
||||
from openai.lib import _parsing, _pydantic
|
||||
from pydantic import BaseModel
|
||||
|
|
@ -215,13 +215,55 @@ def map_developer_role_to_system_role(
|
|||
"""
|
||||
Translate `developer` role to `system` role for non-OpenAI providers.
|
||||
"""
|
||||
new_messages: List[AllMessageValues] = []
|
||||
if not any(m["role"] == "developer" for m in messages):
|
||||
return messages
|
||||
|
||||
system_message: Optional[Dict[str, Any]] = None
|
||||
system_contents: List[Any] = []
|
||||
non_system_messages: List[AllMessageValues] = []
|
||||
for m in messages:
|
||||
if m["role"] in {"developer", "system"}:
|
||||
if system_message is None:
|
||||
system_message = dict(m)
|
||||
system_message["role"] = "system"
|
||||
system_contents.append(m["content"])
|
||||
else:
|
||||
non_system_messages.append(m)
|
||||
|
||||
if m["role"] == "developer":
|
||||
verbose_logger.debug(
|
||||
"Translating developer role to system role for non-OpenAI providers."
|
||||
) # ensure user knows what's happening with their input.
|
||||
new_messages.append({"role": "system", "content": m["content"]})
|
||||
else:
|
||||
new_messages.append(m)
|
||||
return new_messages
|
||||
|
||||
if system_message is None:
|
||||
return non_system_messages
|
||||
|
||||
system_message["content"] = _merge_system_message_contents(system_contents)
|
||||
return [cast(AllMessageValues, system_message), *non_system_messages]
|
||||
|
||||
|
||||
def _merge_system_message_contents(contents: List[Any]) -> Union[str, List[Any]]:
|
||||
"""
|
||||
Merge system-equivalent content into one leading system message.
|
||||
"""
|
||||
if all(isinstance(content, str) or content is None for content in contents):
|
||||
return "\n\n".join(content for content in contents if content)
|
||||
|
||||
merged_blocks: List[Any] = []
|
||||
for content in contents:
|
||||
content_blocks = _system_message_content_to_blocks(content)
|
||||
if not content_blocks:
|
||||
continue
|
||||
|
||||
if merged_blocks:
|
||||
merged_blocks.append({"type": "text", "text": "\n\n"})
|
||||
merged_blocks.extend(content_blocks)
|
||||
return merged_blocks
|
||||
|
||||
|
||||
def _system_message_content_to_blocks(content: Any) -> List[Any]:
|
||||
if content is None or content == "":
|
||||
return []
|
||||
if isinstance(content, list):
|
||||
return list(content)
|
||||
return [{"type": "text", "text": str(content)}]
|
||||
|
|
|
|||
79
tests/llm_translation/test_base_llm_base_utils.py
Normal file
79
tests/llm_translation/test_base_llm_base_utils.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
from litellm.llms.base_llm.base_utils import map_developer_role_to_system_role
|
||||
from litellm.responses.litellm_completion_transformation.transformation import (
|
||||
LiteLLMCompletionResponsesConfig,
|
||||
)
|
||||
|
||||
|
||||
def test_map_developer_role_leaves_messages_without_developer_role_unchanged():
|
||||
messages = [
|
||||
{"role": "system", "content": "Follow the product policy."},
|
||||
{"role": "user", "content": "Hello!"},
|
||||
]
|
||||
|
||||
assert map_developer_role_to_system_role(messages=messages) is messages
|
||||
|
||||
|
||||
def test_map_developer_role_merges_system_equivalent_messages():
|
||||
messages = [
|
||||
{"role": "system", "content": "Follow the product policy."},
|
||||
{"role": "developer", "content": "Prefer concise answers."},
|
||||
{"role": "user", "content": "Hello!"},
|
||||
{"role": "system", "content": "Use markdown only when helpful."},
|
||||
]
|
||||
|
||||
result = map_developer_role_to_system_role(messages=messages)
|
||||
|
||||
assert result == [
|
||||
{
|
||||
"role": "system",
|
||||
"content": (
|
||||
"Follow the product policy.\n\n"
|
||||
"Prefer concise answers.\n\n"
|
||||
"Use markdown only when helpful."
|
||||
),
|
||||
},
|
||||
{"role": "user", "content": "Hello!"},
|
||||
]
|
||||
|
||||
|
||||
def test_map_developer_role_preserves_structured_system_content():
|
||||
messages = [
|
||||
{"role": "system", "content": [{"type": "text", "text": "System rules."}]},
|
||||
{"role": "developer", "content": "Developer rules."},
|
||||
{"role": "user", "content": "Hello!"},
|
||||
]
|
||||
|
||||
result = map_developer_role_to_system_role(messages=messages)
|
||||
|
||||
assert result == [
|
||||
{
|
||||
"role": "system",
|
||||
"content": [
|
||||
{"type": "text", "text": "System rules."},
|
||||
{"type": "text", "text": "\n\n"},
|
||||
{"type": "text", "text": "Developer rules."},
|
||||
],
|
||||
},
|
||||
{"role": "user", "content": "Hello!"},
|
||||
]
|
||||
|
||||
|
||||
def test_responses_instructions_and_developer_input_become_single_system_message():
|
||||
request = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
|
||||
model="anthropic/claude-sonnet-4-5",
|
||||
input=[
|
||||
{"role": "developer", "content": "Prefer concise answers."},
|
||||
{"role": "user", "content": "Hello!"},
|
||||
],
|
||||
responses_api_request={"instructions": "Follow the product policy."},
|
||||
)
|
||||
|
||||
result = map_developer_role_to_system_role(messages=request["messages"])
|
||||
|
||||
assert result == [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "Follow the product policy.\n\nPrefer concise answers.",
|
||||
},
|
||||
{"role": "user", "content": "Hello!"},
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue