mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
Merge 9cc71b877a into 5085022580
This commit is contained in:
commit
d8a8aef5c3
3 changed files with 61 additions and 3 deletions
|
|
@ -25,7 +25,7 @@ from litellm.litellm_core_utils.prompt_templates.reasoning_utils import (
|
|||
from litellm.litellm_core_utils.reasoning_effort_utils import (
|
||||
reasoning_effort_from_thinking,
|
||||
)
|
||||
from litellm.llms.anthropic.messages.convertible_blocks import tool_result_text_parts
|
||||
from litellm.llms.anthropic.messages.convertible_blocks import tool_result_text_part
|
||||
from litellm.llms.anthropic.messages.native.utils import (
|
||||
refusal_stop_details,
|
||||
responses_output_refusal_text,
|
||||
|
|
@ -60,6 +60,22 @@ REASONING_SUMMARY_PART_SEPARATOR: Final = "\n\n"
|
|||
RESPONSES_INCLUDE_ENCRYPTED_REASONING: Final = "reasoning.encrypted_content"
|
||||
|
||||
|
||||
def _responses_tool_result_part(block: object) -> str | None:
|
||||
match block:
|
||||
case {"type": "tool_reference", "tool_name": str(tool_name)}:
|
||||
return f"[Loaded tool: {tool_name}]"
|
||||
case {"type": "tool_reference"}:
|
||||
return "[Loaded tool: ]"
|
||||
case _:
|
||||
return tool_result_text_part(block)
|
||||
|
||||
|
||||
def _responses_tool_result_text_parts(tool_result: Mapping[str, object]) -> tuple[str, ...]:
|
||||
content: Final = tool_result.get("content")
|
||||
parts: Final = map(_responses_tool_result_part, content if isinstance(content, list) else ())
|
||||
return tuple(part for part in parts if part is not None)
|
||||
|
||||
|
||||
class LiteLLMAnthropicToResponsesAPIAdapter:
|
||||
"""
|
||||
Converts Anthropic /v1/messages requests to OpenAI Responses API format and
|
||||
|
|
@ -387,7 +403,7 @@ class LiteLLMAnthropicToResponsesAPIAdapter:
|
|||
elif isinstance(inner, str):
|
||||
output_text = inner
|
||||
elif isinstance(inner, list):
|
||||
output_text = "\n".join(tool_result_text_parts(tool_result_block))
|
||||
output_text = "\n".join(_responses_tool_result_text_parts(tool_result_block))
|
||||
if covered_image_parts or later_image_parts:
|
||||
output_text = (
|
||||
f"{output_text}\n{TOOL_RESULT_IMAGE_PLACEHOLDER}"
|
||||
|
|
|
|||
|
|
@ -490,6 +490,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 = [
|
||||
|
|
|
|||
|
|
@ -136,7 +136,6 @@ _DROPPED_INSIDE_TOOL_RESULT: Final[tuple[tuple[Mapping[str, object], str, str],
|
|||
"text-notes",
|
||||
"document with a text source inside a tool_result",
|
||||
),
|
||||
({"type": "tool_reference", "tool_name": "find_docs"}, "find_docs", "tool_reference inside a tool_result"),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -158,6 +157,22 @@ def test_tool_result_content_both_counters_drop_is_named(inner: Mapping[str, obj
|
|||
assert unconvertible_anthropic_blocks(_tool_turn(inner)) == (label,)
|
||||
|
||||
|
||||
def test_tool_reference_count_guard_rejects_when_only_responses_preserves_it() -> None:
|
||||
inner: Final = {"type": "tool_reference", "tool_name": "find_docs"}
|
||||
gemini, openai = _sent_by_each_counter(inner)
|
||||
|
||||
assert "find_docs" not in gemini
|
||||
assert _JSON_OBJECTS.validate_json(openai) == [
|
||||
{"type": "function_call", "call_id": "toolu_01", "name": "lookup", "arguments": "{}"},
|
||||
{"type": "function_call_output", "call_id": "toolu_01", "output": "[Loaded tool: find_docs]"},
|
||||
]
|
||||
assert unconvertible_anthropic_blocks(_tool_turn(inner)) == ("tool_reference inside a tool_result",)
|
||||
assert unconvertible_blocks_error(_tool_turn(inner), "anthropic") == (
|
||||
"count_tokens can't convert these content blocks for this model, so the count would leave them out: "
|
||||
"tool_reference inside a tool_result"
|
||||
)
|
||||
|
||||
|
||||
def _replayed_web_search(encrypted_content: str) -> tuple[Mapping[str, object], ...]:
|
||||
return (
|
||||
{"role": "user", "content": "What is new?"},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue