fix(azure_ai): forward reasoning_effort for reasoning-capable models

This commit is contained in:
David-Wu1119 2026-07-24 10:17:47 +08:00
parent 43e7b96b83
commit aee3c1fe58
2 changed files with 90 additions and 12 deletions

View file

@ -21,7 +21,7 @@ from litellm.secret_managers.main import get_secret_str
from litellm.types.llms.openai import AllMessageValues
from litellm.types.router import GenericLiteLLMParams
from litellm.types.utils import ModelResponse, ProviderField
from litellm.utils import _add_path_to_api_base, supports_tool_choice
from litellm.utils import _add_path_to_api_base, supports_reasoning, supports_tool_choice
class AzureFoundryErrorStrings(str, enum.Enum):
@ -45,8 +45,33 @@ class AzureAIStudioConfig(OpenAIConfig):
if not self._supports_stop_reason(model):
supported_params = [param for param in supported_params if param != "stop"]
if supports_reasoning(model=f"azure_ai/{model}") and "reasoning_effort" not in supported_params:
supported_params.append("reasoning_effort")
return supported_params
def map_openai_params(
self,
non_default_params: dict,
optional_params: dict,
model: str,
drop_params: bool,
) -> dict:
optional_params = super().map_openai_params(
non_default_params=non_default_params,
optional_params=optional_params,
model=model,
drop_params=drop_params,
)
reasoning_effort = non_default_params.get("reasoning_effort")
if (
reasoning_effort is not None
and "reasoning_effort" not in optional_params
and supports_reasoning(model=f"azure_ai/{model}")
):
optional_params["reasoning_effort"] = reasoning_effort
return optional_params
def _supports_stop_reason(self, model: str) -> bool:
"""
Check if the model supports stop tokens.

View file

@ -5,13 +5,13 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
sys.path.insert(
0, os.path.abspath("../../../../..")
) # Adds the parent directory to the system path
sys.path.insert(0, os.path.abspath("../../../../..")) # Adds the parent directory to the system path
from litellm import UnsupportedParamsError
from litellm.llms.azure_ai.azure_model_router.transformation import (
AzureModelRouterConfig,
)
from litellm.llms.azure_ai.chat.transformation import AzureAIStudioConfig
from litellm.utils import get_optional_params
@pytest.mark.asyncio
@ -197,8 +197,7 @@ def test_azure_model_router_response_shows_actual_model():
# Verify that the response contains the actual model used, not the router model
assert result.model == "azure_ai/gpt-5-nano-2025-08-07", (
f"Expected model to be 'azure_ai/gpt-5-nano-2025-08-07' (actual model used), "
f"but got '{result.model}'"
f"Expected model to be 'azure_ai/gpt-5-nano-2025-08-07' (actual model used), but got '{result.model}'"
)
@ -222,14 +221,10 @@ def test_drop_tool_level_extra_fields_strips_copilot_mcp_server_name():
mock_response.text = error_text
mock_response.json.return_value = json.loads(error_text)
mock_response.status_code = 400
e = httpx.HTTPStatusError(
message="400", request=MagicMock(), response=mock_response
)
e = httpx.HTTPStatusError(message="400", request=MagicMock(), response=mock_response)
assert config._error_has_tool_level_extra_fields(error_text) is True
assert (
config.should_retry_llm_api_inside_llm_translation_on_http_error(e, {}) is True
)
assert config.should_retry_llm_api_inside_llm_translation_on_http_error(e, {}) is True
request_data = {
"model": "FW-Kimi-K2.6",
@ -262,3 +257,61 @@ def test_drop_tool_level_extra_fields_strips_copilot_mcp_server_name():
assert "copilot_mcp_server_name" not in tool
assert result["tools"][0]["type"] == "function"
assert result["tools"][1]["function"]["name"] == "read_file"
class TestAzureAIReasoningEffort:
def test_reasoning_effort_in_supported_params_for_reasoning_model(self):
config = AzureAIStudioConfig()
assert "reasoning_effort" in config.get_supported_openai_params("deepseek-v4-pro")
def test_reasoning_effort_mapped_for_reasoning_model(self):
config = AzureAIStudioConfig()
optional_params = config.map_openai_params(
non_default_params={"reasoning_effort": "high", "temperature": 0.5},
optional_params={},
model="deepseek-v4-pro",
drop_params=False,
)
assert optional_params.get("reasoning_effort") == "high"
assert optional_params.get("temperature") == 0.5
def test_reasoning_effort_dropped_for_non_reasoning_model(self):
config = AzureAIStudioConfig()
optional_params = config.map_openai_params(
non_default_params={"reasoning_effort": "high"},
optional_params={},
model="Phi-4",
drop_params=True,
)
assert "reasoning_effort" not in optional_params
def test_explicit_allowed_param_is_preserved_for_unknown_model(self):
optional_params = get_optional_params(
model="custom-reasoning-model",
custom_llm_provider="azure_ai",
reasoning_effort="high",
allowed_openai_params=["reasoning_effort"],
)
assert optional_params["reasoning_effort"] == "high"
def test_gpt_5_reasoning_effort_is_validated(self):
config = AzureAIStudioConfig()
with pytest.raises(UnsupportedParamsError):
config.map_openai_params(
non_default_params={"reasoning_effort": "minimal"},
optional_params={},
model="gpt-5.4-mini",
drop_params=False,
)
def test_gpt_5_reasoning_effort_dict_is_normalized(self):
config = AzureAIStudioConfig()
optional_params = config.map_openai_params(
non_default_params={
"reasoning_effort": {"effort": "high", "summary": "detailed"}
},
optional_params={},
model="gpt-5.4-mini",
drop_params=False,
)
assert optional_params["reasoning_effort"] == "high"