fix(anthropic-messages): map tool_choice.disable_parallel_tool_use to parallel_tool_calls=false on the chat and responses adapters

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
mateo 2026-10-31 22:37:02 +00:00
parent f6d2f2098c
commit 2b77493e32
4 changed files with 51 additions and 6 deletions

View file

@ -1101,9 +1101,10 @@ class LiteLLMAnthropicMessagesAdapter:
tool_choice: Final = anthropic_message_request["tool_choice"]
if not tool_choice:
return
new_kwargs["tool_choice"] = self.translate_anthropic_tool_choice_to_openai(
tool_choice=cast(AnthropicMessagesToolChoice, tool_choice)
)
anthropic_tool_choice: Final = cast(AnthropicMessagesToolChoice, tool_choice)
new_kwargs["tool_choice"] = self.translate_anthropic_tool_choice_to_openai(tool_choice=anthropic_tool_choice)
if anthropic_tool_choice.get("disable_parallel_tool_use") is True:
new_kwargs["parallel_tool_calls"] = False # rebind-ok: out-param store like tool_choice above
def _translate_stop_sequences_to_openai(
self,

View file

@ -656,9 +656,10 @@ class LiteLLMAnthropicToResponsesAPIAdapter:
# tool_choice
tool_choice: Final = anthropic_request.get("tool_choice")
if tool_choice:
responses_kwargs["tool_choice"] = self.translate_tool_choice_to_responses_api(
cast(AnthropicMessagesToolChoice, tool_choice)
)
anthropic_tool_choice: Final = cast(AnthropicMessagesToolChoice, tool_choice)
responses_kwargs["tool_choice"] = self.translate_tool_choice_to_responses_api(anthropic_tool_choice)
if anthropic_tool_choice.get("disable_parallel_tool_use") is True:
responses_kwargs["parallel_tool_calls"] = False
# thinking -> reasoning
thinking: Final = anthropic_request.get("thinking")

View file

@ -5527,3 +5527,30 @@ async def test_chat_bridge_tool_result_text_block_without_text_reaches_the_model
],
}
]
@pytest.mark.parametrize(
("tool_choice", "expected_parallel_tool_calls"),
[
({"type": "auto", "disable_parallel_tool_use": True}, False),
({"type": "any", "disable_parallel_tool_use": True}, False),
({"type": "auto", "disable_parallel_tool_use": False}, None),
({"type": "auto"}, None),
],
)
def test_translate_anthropic_to_openai_disable_parallel_tool_use_sets_parallel_tool_calls(
tool_choice: dict[str, object], expected_parallel_tool_calls: bool | None
) -> None:
from litellm.types.llms.anthropic import AnthropicMessagesRequest
request: Final = AnthropicMessagesRequest(
model="gpt-5.4",
max_tokens=64,
messages=[{"role": "user", "content": "What is the weather in Paris?"}],
tools=[{"name": "lookup_weather", "input_schema": {"type": "object", "properties": {}}}],
tool_choice=tool_choice,
)
translated, _ = LiteLLMAnthropicMessagesAdapter().translate_anthropic_to_openai(request)
assert translated.get("parallel_tool_calls") == expected_parallel_tool_calls

View file

@ -1178,6 +1178,22 @@ class TestTranslateRequestBroaderCoverage:
kwargs = _ADAPTER.translate_request(req)
assert kwargs["tool_choice"] == {"type": "function", "name": "do_thing"}
@pytest.mark.parametrize(
("tool_choice", "expected_parallel_tool_calls"),
[
({"type": "auto", "disable_parallel_tool_use": True}, False),
({"type": "any", "disable_parallel_tool_use": True}, False),
({"type": "auto", "disable_parallel_tool_use": False}, None),
({"type": "auto"}, None),
],
)
def test_disable_parallel_tool_use_sets_parallel_tool_calls(
self, tool_choice: dict[str, object], expected_parallel_tool_calls: bool | None
):
req = _make_request(tools=[{"name": "do_thing"}], tool_choice=tool_choice)
kwargs = _ADAPTER.translate_request(req)
assert kwargs.get("parallel_tool_calls") == expected_parallel_tool_calls
def test_thinking_translated_to_reasoning(self):
req = _make_request(thinking={"type": "enabled", "budget_tokens": 12000})
kwargs = _ADAPTER.translate_request(req)