fix(mistral): leave tool_choice to the no-tools removal when no tools are sent

An unusable tool_choice on a request with no tools answered 400, while the
merge base and every other OpenAI-compatible route strip tool_choice from
such a request and answer 200. Map tool_choice only when tools are present.
This commit is contained in:
mateo-berri 2026-11-04 21:01:57 -08:00
parent 6de3708969
commit 3c3081a502
2 changed files with 16 additions and 3 deletions

View file

@ -282,7 +282,7 @@ class MistralConfig(OpenAIGPTConfig):
optional_params["tools"] = self._clean_tool_schema_for_mistral(value)
if param == "stream" and value is True:
optional_params["stream"] = value
if param == "tool_choice":
if param == "tool_choice" and "tools" in non_default_params and non_default_params["tools"]:
match self._tool_choice_to_send(
model,
cast(object, value), # cast-ok: untyped request params, the match narrows it

View file

@ -2084,14 +2084,17 @@ class TestMistralToolChoice:
)
def _request_body(
self, tool_choice: str | Mapping[str, object], drop_params: bool = False
self,
tool_choice: str | Mapping[str, object],
drop_params: bool = False,
tools: Sequence[ChatCompletionToolParam] | None = TOOLS,
) -> Mapping[str, object]:
import litellm
optional_params: Final = litellm.get_optional_params(
model="mistral-large-latest",
custom_llm_provider="mistral",
tools=list(self.TOOLS),
tools=None if tools is None else list(tools),
tool_choice=tool_choice,
drop_params=drop_params,
)
@ -2153,6 +2156,16 @@ class TestMistralToolChoice:
with pytest.raises(litellm.UnsupportedParamsError, match="tool_choice"):
self._request_body(tool_choice)
@pytest.mark.parametrize("tools", [None, ()])
@pytest.mark.parametrize("tool_choice", UNUSABLE_TOOL_CHOICES)
def test_unusable_tool_choice_without_tools_is_not_sent(
self,
local_model_cost_map: None,
tool_choice: str | Mapping[str, object],
tools: Sequence[ChatCompletionToolParam] | None,
) -> None:
assert "tool_choice" not in self._request_body(tool_choice, tools=tools)
@pytest.mark.parametrize("tool_choice", UNUSABLE_TOOL_CHOICES)
def test_unusable_tool_choice_is_dropped_with_a_warning_under_drop_params(
self, local_model_cost_map: None, tool_choice: str | Mapping[str, object], caplog: pytest.LogCaptureFixture