This commit is contained in:
ump45nose 2026-08-27 15:22:25 +00:00 • committed by GitHub
commit 8075edd401
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 117 additions and 2 deletions

View file

@ -455,6 +455,14 @@ class LiteLLMAnthropicMessagesAdapter:
)
self._add_cache_control_if_applicable(content, tool_result, model)
tool_message_list.append(tool_result)
elif c.get("type") == "tool_reference":
tool_result = ChatCompletionToolMessage(
role="tool",
tool_call_id=content.get("tool_use_id", ""),
content=f"[Loaded tool: {c.get('tool_name', '')}]",
)
self._add_cache_control_if_applicable(content, tool_result, model)
tool_message_list.append(tool_result)
elif c.get("type") in ("image", "document"):
image_part = self._tool_result_image_part(c.get("source"))
tool_result = ChatCompletionToolMessage(
@ -483,6 +491,13 @@ class LiteLLMAnthropicMessagesAdapter:
text=c.get("text", ""),
)
)
elif c.get("type") == "tool_reference":
combined_content_parts.append(
ChatCompletionTextObject(
type="text",
text=f"[Loaded tool: {c.get('tool_name', '')}]",
)
)
elif c.get("type") in ("image", "document"):
image_part = self._tool_result_image_part(c.get("source"))
if image_part:

View file

@ -294,7 +294,11 @@ class LiteLLMAnthropicToResponsesAPIAdapter:
output_text = inner
elif isinstance(inner, list):
parts = [
c.get("text", "") for c in inner if isinstance(c, dict) and c.get("type") == "text"
c.get("text", "")
if c.get("type") == "text"
else f"[Loaded tool: {c.get('tool_name', '')}]"
for c in inner
if isinstance(c, dict) and c.get("type") in {"text", "tool_reference"}
]
output_text = "\n".join(parts)
image_candidates = tuple(

View file

@ -324,7 +324,12 @@ class AnthropicMessagesToolResultParam(TypedDict, total=False):
is_error: bool
content: (
str
| Iterable[AnthropicMessagesToolResultContent | AnthropicMessagesImageParam | AnthropicMessagesDocumentParam]
| Iterable[
AnthropicMessagesToolResultContent
| AnthropicMessagesImageParam
| AnthropicMessagesDocumentParam
| ToolReference
]
)
cache_control: dict | ChatCompletionCachedContent | None

View file

@ -1610,6 +1610,70 @@ def test_translate_anthropic_messages_to_openai_tool_result_single_item_backward
assert tool_message["content"] == "72°F and sunny"
@pytest.mark.parametrize(
("tool_references", "expected_content"),
[
(
[{"type": "tool_reference", "tool_name": "get_weather"}],
"[Loaded tool: get_weather]",
),
(
[
{"type": "tool_reference", "tool_name": "get_weather"},
{"type": "tool_reference", "tool_name": "search_docs"},
],
[
{"type": "text", "text": "[Loaded tool: get_weather]"},
{"type": "text", "text": "[Loaded tool: search_docs]"},
],
),
],
)
def test_translate_anthropic_messages_to_openai_tool_reference_results(
tool_references: list[dict[str, str]], expected_content: Any
):
anthropic_messages = [
AnthopicMessagesAssistantMessageParam(
role="assistant",
content=[
{
"type": "tool_use",
"id": "toolu_tool_search",
"name": "ToolSearch",
"input": {"query": "weather"},
}
],
),
AnthropicMessagesUserMessageParam(
role="user",
content=[
{
"type": "tool_result",
"tool_use_id": "toolu_tool_search",
"content": tool_references,
}
],
),
]
result = LiteLLMAnthropicMessagesAdapter().translate_anthropic_messages_to_openai(
messages=anthropic_messages
)
tool_messages = [
message
for message in result
if isinstance(message, dict) and message.get("role") == "tool"
]
assert tool_messages == [
{
"role": "tool",
"tool_call_id": "toolu_tool_search",
"content": expected_content,
}
]
def test_streaming_chunk_with_both_text_and_tool_calls_issue_18238():
"""
When a streaming choice contains both text content and tool_calls,

View file

@ -482,6 +482,33 @@ class TestTranslateMessagesToResponsesInput:
result = _translate_messages(messages)
assert result[0]["output"] == "Line 1\nLine 2"
def test_user_tool_result_list_content_preserves_tool_references(self):
messages = [
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "call_search",
"content": [
{"type": "text", "text": "Available:"},
{"type": "tool_reference", "tool_name": "get_weather"},
],
}
],
}
]
result = _translate_messages(messages)
assert result == [
{
"type": "function_call_output",
"call_id": "call_search",
"output": "Available:\n[Loaded tool: get_weather]",
}
]
def test_user_tool_result_null_content(self):
"""tool_result with null content becomes empty string output."""
messages = [