mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
fix(anthropic): preserve assistant text order around tool_use in Responses adapter
Fixes #45470. translate_messages_to_responses_input previously deferred all assistant text into one trailing message after function_call items, replaying text that appeared before the tool. Emit text blocks in group order and stop collecting them at the end.
This commit is contained in:
parent
98acc01e0d
commit
c511e3ea06
2 changed files with 55 additions and 13 deletions
|
|
@ -294,6 +294,16 @@ class LiteLLMAnthropicToResponsesAPIAdapter:
|
|||
"arguments": json.dumps(first.get("input", {})),
|
||||
},
|
||||
)
|
||||
if btype == "text":
|
||||
# Emit text in block order so text before/after tool_use is not
|
||||
# replayed as one trailing assistant message (#45470).
|
||||
return (
|
||||
{
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [{"type": "output_text", "text": first.get("text", "")}],
|
||||
},
|
||||
)
|
||||
return ()
|
||||
|
||||
def translate_messages_to_responses_input(
|
||||
|
|
@ -447,19 +457,6 @@ class LiteLLMAnthropicToResponsesAPIAdapter:
|
|||
for _, group in groupby(enumerate(blocks), key=self._assistant_block_group_key)
|
||||
for item in self._assistant_group_to_input_items(tuple(block for _, block in group))
|
||||
)
|
||||
asst_parts: list[dict[str, object]] = [ # mutable-ok: API message payload
|
||||
{"type": "output_text", "text": block.get("text", "")}
|
||||
for block in blocks
|
||||
if block.get("type") == "text"
|
||||
]
|
||||
if asst_parts:
|
||||
input_items.append(
|
||||
{
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": asst_parts,
|
||||
}
|
||||
)
|
||||
|
||||
return input_items
|
||||
|
||||
|
|
|
|||
|
|
@ -696,6 +696,51 @@ class TestTranslateMessagesToResponsesInput:
|
|||
assert result[0]["summary"] == [{"type": "summary_text", "text": "Before the call."}]
|
||||
assert result[2]["summary"] == [{"type": "summary_text", "text": "After the call."}]
|
||||
|
||||
def test_assistant_text_before_and_after_tool_use_preserves_order(self):
|
||||
"""Text around tool_use stays in block order, not replayed after (#45470)."""
|
||||
messages = [
|
||||
{"role": "user", "content": "weather in Paris?"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{"type": "text", "text": "Let me check."},
|
||||
{
|
||||
"type": "tool_use",
|
||||
"id": "toolu_1",
|
||||
"name": "get_weather",
|
||||
"input": {"city": "Paris"},
|
||||
},
|
||||
{"type": "text", "text": "(after)"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "tool_result", "tool_use_id": "toolu_1", "content": "sunny"}
|
||||
],
|
||||
},
|
||||
]
|
||||
result = _translate_messages(messages)
|
||||
assert [item["type"] for item in result] == [
|
||||
"message",
|
||||
"message",
|
||||
"function_call",
|
||||
"message",
|
||||
"function_call_output",
|
||||
]
|
||||
assert result[1] == {
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [{"type": "output_text", "text": "Let me check."}],
|
||||
}
|
||||
assert result[2]["type"] == "function_call"
|
||||
assert result[2]["call_id"] == "toolu_1"
|
||||
assert result[3] == {
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [{"type": "output_text", "text": "(after)"}],
|
||||
}
|
||||
|
||||
def test_thinking_and_text_stay_separate(self):
|
||||
"""The visible answer stays the only thing in the assistant message."""
|
||||
messages = [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue