fix(mistral): forward reasoning_effort on native hybrid reasoning models

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Devin AI 2026-08-10 15:10:33 +00:00
parent f6b9518ddb
commit 7e4d59e2d5
2 changed files with 65 additions and 7 deletions

View file

@ -24,7 +24,7 @@ from litellm.secret_managers.main import get_secret_str
from litellm.types.llms.mistral import MistralThinkingBlock, MistralToolCallMessage
from litellm.types.llms.openai import AllMessageValues
from litellm.types.utils import ModelResponse, ModelResponseStream
from litellm.utils import convert_to_model_response_object
from litellm.utils import convert_to_model_response_object, supports_reasoning
class MistralConfig(OpenAIGPTConfig):
@ -98,12 +98,26 @@ class MistralConfig(OpenAIGPTConfig):
"parallel_tool_calls",
]
# Add reasoning support for magistral models
if "magistral" in model.lower():
if self._is_magistral_model(model):
supported_params.extend(["thinking", "reasoning_effort"])
elif self._supports_native_reasoning_effort(model):
supported_params.append("reasoning_effort")
return supported_params
@staticmethod
def _is_magistral_model(model: str) -> bool:
return "magistral" in model.lower()
@staticmethod
def _supports_native_reasoning_effort(model: str) -> bool:
"""
Mistral's hybrid reasoning models (mistral-medium-3-5 and newer) take ``reasoning_effort``
directly on /chat/completions, so it is forwarded as-is instead of being emulated with the
magistral system prompt: https://docs.mistral.ai/capabilities/reasoning
"""
return supports_reasoning(model=model, custom_llm_provider="mistral")
def _map_tool_choice(self, tool_choice: str) -> str:
if tool_choice == "auto" or tool_choice == "none":
return tool_choice
@ -168,10 +182,12 @@ class MistralConfig(OpenAIGPTConfig):
optional_params["extra_body"] = {"random_seed": value}
if param == "response_format":
optional_params["response_format"] = value
if param == "reasoning_effort" and "magistral" in model.lower():
# Flag that we need to add reasoning system prompt
optional_params["_add_reasoning_prompt"] = True
if param == "thinking" and "magistral" in model.lower():
if param == "reasoning_effort" and value is not None:
if self._is_magistral_model(model):
optional_params["_add_reasoning_prompt"] = True
elif self._supports_native_reasoning_effort(model):
optional_params["reasoning_effort"] = value
if param == "thinking" and self._is_magistral_model(model):
# Flag that we need to add reasoning system prompt
optional_params["_add_reasoning_prompt"] = True
if param == "parallel_tool_calls":

View file

@ -311,6 +311,48 @@ class TestMistralReasoningSupport:
assert len(result["messages"]) == 1
assert result["messages"][0]["role"] == "user"
@pytest.mark.parametrize(
"model",
["mistral/mistral-medium-3-5", "mistral-medium-3-5", "mistral/mistral-medium-latest"],
)
def test_native_reasoning_model_supports_reasoning_effort(self, model):
"""Hybrid reasoning models take reasoning_effort natively, see https://github.com/BerriAI/litellm/issues/36407"""
supported_params = MistralConfig().get_supported_openai_params(model)
assert "reasoning_effort" in supported_params
assert "thinking" not in supported_params
def test_native_reasoning_effort_forwarded_to_request_body(self):
mistral_config = MistralConfig()
optional_params = mistral_config.map_openai_params(
non_default_params={"reasoning_effort": "high"},
optional_params={},
model="mistral/mistral-medium-3-5",
drop_params=False,
)
assert optional_params == {"reasoning_effort": "high"}
request = mistral_config.transform_request(
model="mistral/mistral-medium-3-5",
messages=[{"role": "user", "content": "What is 15 * 7?"}],
optional_params=optional_params,
litellm_params={},
headers={},
)
assert request["reasoning_effort"] == "high"
assert len(request["messages"]) == 1
def test_magistral_reasoning_effort_stays_prompt_based(self):
optional_params = MistralConfig().map_openai_params(
non_default_params={"reasoning_effort": "high"},
optional_params={},
model="mistral/magistral-medium-2506",
drop_params=False,
)
assert optional_params == {"_add_reasoning_prompt": True}
def test_case_insensitive_magistral_detection(self):
"""Test that magistral model detection is case-insensitive."""
mistral_config = MistralConfig()