mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
Fix order-dependent collision in _build_anthropic_tool_name_maps
Use a two-pass approach: first pre-register all already-valid tool names in the 'used' set, then sanitize/disambiguate names that need rewriting. This ensures valid names always have priority regardless of input order, preventing duplicate tool names on the wire when e.g. 'foo/bar' appears before 'foo_bar' in the tool list. Add regression test for the reversed ordering case.
This commit is contained in:
parent
767cadd007
commit
a4d290ddf1
2 changed files with 29 additions and 2 deletions
|
|
@ -175,14 +175,22 @@ def _build_anthropic_tool_name_maps(
|
|||
"""
|
||||
forward: Dict[str, str] = {}
|
||||
used: set = set()
|
||||
|
||||
# First pass: reserve slots for names that are already valid so they
|
||||
# always have priority regardless of input order.
|
||||
for original in original_names:
|
||||
if not isinstance(original, str) or not original:
|
||||
continue
|
||||
candidate = _basic_sanitize_anthropic_tool_name(original)
|
||||
if candidate == original:
|
||||
# Already valid. Reserve the slot but don't put it in `forward`
|
||||
# -- we want untouched names to skip translation entirely.
|
||||
used.add(candidate)
|
||||
|
||||
# Second pass: sanitize/disambiguate names that need rewriting.
|
||||
for original in original_names:
|
||||
if not isinstance(original, str) or not original:
|
||||
continue
|
||||
candidate = _basic_sanitize_anthropic_tool_name(original)
|
||||
if candidate == original:
|
||||
continue
|
||||
# Disambiguate against names already chosen this request.
|
||||
unique = candidate
|
||||
|
|
@ -197,6 +205,8 @@ def _build_anthropic_tool_name_maps(
|
|||
used.add(unique)
|
||||
reverse = {v: k for k, v in forward.items()}
|
||||
return forward, reverse
|
||||
|
||||
|
||||
REASONING_EFFORT_TO_OUTPUT_CONFIG_EFFORT: Dict[str, str] = {
|
||||
"low": "low",
|
||||
"minimal": "low",
|
||||
|
|
|
|||
|
|
@ -4026,6 +4026,23 @@ def test_build_anthropic_tool_name_maps_three_way_collision():
|
|||
assert reverse == {"foo_bar_2": "foo/bar", "foo_bar_3": "foo.bar"}
|
||||
|
||||
|
||||
def test_build_anthropic_tool_name_maps_reverse_order_collision():
|
||||
"""REGRESSION: when the invalid name appears *before* the valid name that
|
||||
its sanitized form collides with, both must still end up with distinct
|
||||
names on the wire."""
|
||||
from litellm.llms.anthropic.chat.transformation import (
|
||||
_build_anthropic_tool_name_maps,
|
||||
)
|
||||
|
||||
forward, reverse = _build_anthropic_tool_name_maps(["foo/bar", "foo_bar"])
|
||||
# The valid name keeps its slot untouched.
|
||||
assert "foo_bar" not in forward
|
||||
# The rewritten one gets a disambiguating suffix.
|
||||
assert forward["foo/bar"] == "foo_bar_2"
|
||||
assert reverse == {"foo_bar_2": "foo/bar"}
|
||||
assert "foo_bar" not in reverse
|
||||
|
||||
|
||||
def test_map_openai_params_does_not_pollute_optional_params_with_internal_keys():
|
||||
"""REGRESSION: ``optional_params`` is what becomes the JSON body sent to
|
||||
Anthropic (``data = {**optional_params}``). It MUST NOT carry LiteLLM-
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue