mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
fix(base_utils): keep a closing developer message in place only after an assistant turn
A developer message that closes the conversation after a user or tool message hoists like any other, so the request shape the ticket reports keeps working on system-first templates; only a closing run that follows an assistant turn stays in place
This commit is contained in:
parent
f500578ab1
commit
9820d2f52c
2 changed files with 48 additions and 7 deletions
|
|
@ -312,14 +312,17 @@ def _leading_system_block_length(messages: Sequence[AllMessageValues]) -> int:
|
|||
|
||||
|
||||
def _closing_instruction_block_start(messages: Sequence[AllMessageValues], leading_length: int) -> int:
|
||||
return next(
|
||||
last_conversation_index: Final = next(
|
||||
(
|
||||
index + 1
|
||||
index
|
||||
for index in range(len(messages) - 1, leading_length - 1, -1)
|
||||
if messages[index]["role"] not in _INSTRUCTION_ROLES
|
||||
),
|
||||
leading_length,
|
||||
None,
|
||||
)
|
||||
if last_conversation_index is None or messages[last_conversation_index]["role"] != "assistant":
|
||||
return len(messages)
|
||||
return last_conversation_index + 1
|
||||
|
||||
|
||||
def _move_later_developer_messages_up(messages: Sequence[AllMessageValues]) -> tuple[AllMessageValues, ...]:
|
||||
|
|
@ -347,9 +350,10 @@ def hoist_developer_messages_into_leading_system_message(
|
|||
Translate `developer` role to `system` role for OpenAI-compatible backends whose
|
||||
chat template allows a single system message and only at the start: developer
|
||||
messages that arrive after the first user turn move into the leading system
|
||||
block, except a developer message that closes the conversation, which stays in
|
||||
place so the request does not end on the assistant's turn. Each run of
|
||||
consecutive system messages is then folded into one message in a single pass.
|
||||
block, except a developer message that closes the conversation right after an
|
||||
assistant turn, which stays in place so the request does not end on the
|
||||
assistant's turn. Each run of consecutive system messages is then folded into
|
||||
one message in a single pass.
|
||||
"""
|
||||
translated: Final = tuple(map(_as_system_message, _move_later_developer_messages_up(messages)))
|
||||
return tuple(_merged_system_runs(translated))
|
||||
|
|
|
|||
|
|
@ -275,12 +275,15 @@ class TestHoistDeveloperMessagesIntoLeadingSystemMessage:
|
|||
{"role": "system", "content": "Reply with the single word PONG and nothing else."},
|
||||
]
|
||||
|
||||
def test_only_the_closing_developer_run_stays_in_place_and_is_folded_into_one_message(self):
|
||||
def test_only_the_closing_developer_run_after_an_assistant_turn_stays_in_place_and_is_folded_into_one_message(
|
||||
self,
|
||||
):
|
||||
messages = [
|
||||
{"role": "system", "content": "Base"},
|
||||
{"role": "user", "content": "Turn 1"},
|
||||
{"role": "developer", "content": "Update A"},
|
||||
{"role": "user", "content": "Turn 2"},
|
||||
{"role": "assistant", "content": "Reply 2"},
|
||||
{"role": "developer", "content": "Closing B"},
|
||||
{"role": "developer", "content": "Closing C"},
|
||||
]
|
||||
|
|
@ -288,9 +291,43 @@ class TestHoistDeveloperMessagesIntoLeadingSystemMessage:
|
|||
{"role": "system", "content": "Base\n\nUpdate A"},
|
||||
{"role": "user", "content": "Turn 1"},
|
||||
{"role": "user", "content": "Turn 2"},
|
||||
{"role": "assistant", "content": "Reply 2"},
|
||||
{"role": "system", "content": "Closing B\n\nClosing C"},
|
||||
]
|
||||
|
||||
def test_developer_message_that_closes_the_conversation_after_a_user_turn_is_hoisted(self):
|
||||
messages = [
|
||||
{"role": "user", "content": "Hi there"},
|
||||
{"role": "developer", "content": "Reply with exactly one word: the capital of France"},
|
||||
]
|
||||
assert list(hoist_developer_messages_into_leading_system_message(messages)) == [
|
||||
{"role": "system", "content": "Reply with exactly one word: the capital of France"},
|
||||
{"role": "user", "content": "Hi there"},
|
||||
]
|
||||
|
||||
def test_developer_message_that_closes_the_conversation_after_a_tool_result_is_hoisted(self):
|
||||
messages = [
|
||||
{"role": "system", "content": "Base"},
|
||||
{"role": "user", "content": "Look it up"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [{"id": "call_1", "type": "function", "function": {"name": "lookup", "arguments": "{}"}}],
|
||||
},
|
||||
{"role": "tool", "tool_call_id": "call_1", "content": "Paris"},
|
||||
{"role": "developer", "content": "Answer with exactly one word."},
|
||||
]
|
||||
assert list(hoist_developer_messages_into_leading_system_message(messages)) == [
|
||||
{"role": "system", "content": "Base\n\nAnswer with exactly one word."},
|
||||
{"role": "user", "content": "Look it up"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [{"id": "call_1", "type": "function", "function": {"name": "lookup", "arguments": "{}"}}],
|
||||
},
|
||||
{"role": "tool", "tool_call_id": "call_1", "content": "Paris"},
|
||||
]
|
||||
|
||||
def test_hoisted_block_content_developer_message_merges_as_blocks_after_string_instructions(self):
|
||||
messages = [
|
||||
{"role": "system", "content": "You are Codex"},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue