mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
Merge b2ed84139b into 80d3b69d9c
This commit is contained in:
commit
0c49914091
5 changed files with 264 additions and 7 deletions
|
|
@ -131,14 +131,36 @@ class AzureOpenAIGPT5Config(AzureOpenAIConfig, OpenAIGPT5Config):
|
|||
),
|
||||
)
|
||||
|
||||
azure_gated_params = {
|
||||
param: value
|
||||
for param, value in non_default_params.items()
|
||||
if param in ("tool_choice", "response_format")
|
||||
}
|
||||
|
||||
result = OpenAIGPT5Config.map_openai_params(
|
||||
self,
|
||||
non_default_params=non_default_params,
|
||||
non_default_params={
|
||||
param: value
|
||||
for param, value in non_default_params.items()
|
||||
if param not in azure_gated_params
|
||||
},
|
||||
optional_params=optional_params,
|
||||
model=model,
|
||||
drop_params=drop_params,
|
||||
)
|
||||
|
||||
# tool_choice and response_format need Azure's api_version gating, which the
|
||||
# OpenAI gpt-5 mapper does not apply.
|
||||
if azure_gated_params:
|
||||
result = AzureOpenAIConfig.map_openai_params(
|
||||
self,
|
||||
non_default_params=azure_gated_params,
|
||||
optional_params=result,
|
||||
model=model,
|
||||
drop_params=drop_params,
|
||||
api_version=api_version,
|
||||
)
|
||||
|
||||
# Only drop reasoning_effort='none' for models that don't support it
|
||||
result_effort = _get_effort_level(result.get("reasoning_effort"))
|
||||
if result_effort == "none" and not supports_none:
|
||||
|
|
|
|||
|
|
@ -20,9 +20,57 @@ from litellm.types.llms.openai import AllMessageValues
|
|||
from litellm.utils import get_model_info, supports_reasoning
|
||||
|
||||
from ...openai.chat.o_series_transformation import OpenAIOSeriesConfig
|
||||
from .gpt_transformation import AzureOpenAIConfig
|
||||
|
||||
|
||||
class AzureOpenAIO1Config(OpenAIOSeriesConfig):
|
||||
def map_openai_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
api_version: str = "",
|
||||
) -> dict:
|
||||
"""
|
||||
Map OpenAI params for Azure O-Series models.
|
||||
|
||||
O-Series specific translations (e.g. max_tokens -> max_completion_tokens) are
|
||||
handled by the OpenAI o-series mapper. tool_choice and response_format must
|
||||
additionally go through the Azure api_version gating in AzureOpenAIConfig, which
|
||||
the OpenAI mapper is not aware of.
|
||||
"""
|
||||
azure_gated_params = {
|
||||
param: value
|
||||
for param, value in non_default_params.items()
|
||||
if param in ("tool_choice", "response_format")
|
||||
}
|
||||
|
||||
optional_params = super().map_openai_params(
|
||||
non_default_params={
|
||||
param: value
|
||||
for param, value in non_default_params.items()
|
||||
if param not in azure_gated_params
|
||||
},
|
||||
optional_params=optional_params,
|
||||
model=model,
|
||||
drop_params=drop_params,
|
||||
)
|
||||
|
||||
if azure_gated_params:
|
||||
# AzureOpenAIO1Config does not inherit from AzureOpenAIConfig, so the
|
||||
# Azure gating is reached via an instance rather than the unbound-self
|
||||
# dispatch used in the GPT-5 path.
|
||||
optional_params = AzureOpenAIConfig().map_openai_params(
|
||||
non_default_params=azure_gated_params,
|
||||
optional_params=optional_params,
|
||||
model=model,
|
||||
drop_params=drop_params,
|
||||
api_version=api_version,
|
||||
)
|
||||
|
||||
return optional_params
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> list:
|
||||
"""
|
||||
Get the supported OpenAI params for the Azure O-Series models
|
||||
|
|
|
|||
|
|
@ -4868,6 +4868,12 @@ def get_optional_params(
|
|||
)
|
||||
elif custom_llm_provider == "azure":
|
||||
_azure_detection_model = base_model or model
|
||||
api_version = (
|
||||
api_version
|
||||
or litellm.api_version
|
||||
or get_secret("AZURE_API_VERSION")
|
||||
or litellm.AZURE_DEFAULT_API_VERSION
|
||||
)
|
||||
if litellm.AzureOpenAIO1Config().is_o_series_model(
|
||||
model=_azure_detection_model
|
||||
):
|
||||
|
|
@ -4875,6 +4881,7 @@ def get_optional_params(
|
|||
non_default_params=non_default_params,
|
||||
optional_params=optional_params,
|
||||
model=_azure_detection_model,
|
||||
api_version=api_version, # type: ignore
|
||||
drop_params=(
|
||||
drop_params
|
||||
if drop_params is not None and isinstance(drop_params, bool)
|
||||
|
|
@ -4888,6 +4895,7 @@ def get_optional_params(
|
|||
non_default_params=non_default_params,
|
||||
optional_params=optional_params,
|
||||
model=_azure_detection_model,
|
||||
api_version=api_version, # type: ignore
|
||||
drop_params=(
|
||||
drop_params
|
||||
if drop_params is not None and isinstance(drop_params, bool)
|
||||
|
|
@ -4900,12 +4908,6 @@ def get_optional_params(
|
|||
api_version, litellm.api_version, get_secret("AZURE_API_VERSION")
|
||||
)
|
||||
)
|
||||
api_version = (
|
||||
api_version
|
||||
or litellm.api_version
|
||||
or get_secret("AZURE_API_VERSION")
|
||||
or litellm.AZURE_DEFAULT_API_VERSION
|
||||
)
|
||||
optional_params = litellm.AzureOpenAIConfig().map_openai_params(
|
||||
non_default_params=non_default_params,
|
||||
optional_params=optional_params,
|
||||
|
|
|
|||
|
|
@ -28,3 +28,102 @@ async def test_azure_chat_o_series_transformation():
|
|||
)
|
||||
print(response)
|
||||
assert response["model"] == "web-interface-o1-mini"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", ["o1", "o3-mini"])
|
||||
def test_azure_o_series_tool_choice_required_gated_by_api_version(model):
|
||||
"""
|
||||
tool_choice='required' is not supported by Azure on api_version<=2024-05-01.
|
||||
|
||||
Azure o-series deployments must honor the same api_version gating as the
|
||||
non-o-series Azure path instead of silently forwarding 'required'.
|
||||
"""
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "f",
|
||||
"parameters": {"type": "object", "properties": {}},
|
||||
},
|
||||
}
|
||||
]
|
||||
with pytest.raises(litellm.UnsupportedParamsError):
|
||||
litellm.get_optional_params(
|
||||
model=model,
|
||||
custom_llm_provider="azure",
|
||||
tool_choice="required",
|
||||
tools=tools,
|
||||
api_version="2024-05-01-preview",
|
||||
drop_params=False,
|
||||
)
|
||||
|
||||
# newer api_version supports it
|
||||
params = litellm.get_optional_params(
|
||||
model=model,
|
||||
custom_llm_provider="azure",
|
||||
tool_choice="required",
|
||||
tools=tools,
|
||||
api_version="2025-01-01-preview",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["tool_choice"] == "required"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", ["o1", "o3-mini"])
|
||||
def test_azure_o_series_response_format_falls_back_to_tools_on_old_api_version(model):
|
||||
"""
|
||||
On api_versions that predate native json_schema support, Azure o-series should
|
||||
convert response_format into a tool call, matching the non-o-series Azure path.
|
||||
"""
|
||||
response_format = {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "r",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"a": {"type": "string"}},
|
||||
"required": ["a"],
|
||||
},
|
||||
"strict": True,
|
||||
},
|
||||
}
|
||||
params = litellm.get_optional_params(
|
||||
model=model,
|
||||
custom_llm_provider="azure",
|
||||
response_format=response_format,
|
||||
api_version="2024-02-01",
|
||||
drop_params=False,
|
||||
)
|
||||
assert "response_format" not in params
|
||||
assert "tools" in params
|
||||
|
||||
|
||||
def test_azure_o_series_still_maps_max_tokens():
|
||||
"""The o-series max_tokens -> max_completion_tokens translation must be preserved."""
|
||||
params = litellm.get_optional_params(
|
||||
model="o3-mini",
|
||||
custom_llm_provider="azure",
|
||||
max_tokens=64,
|
||||
api_version="2025-01-01-preview",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["max_completion_tokens"] == 64
|
||||
assert "max_tokens" not in params
|
||||
|
||||
|
||||
def test_azure_o_series_without_gated_params_is_unchanged():
|
||||
"""
|
||||
When no api_version-gated params (tool_choice/response_format) are passed,
|
||||
the o-series mapping should behave exactly like the OpenAI o-series path.
|
||||
"""
|
||||
params = litellm.get_optional_params(
|
||||
model="o3-mini",
|
||||
custom_llm_provider="azure",
|
||||
max_tokens=64,
|
||||
reasoning_effort="high",
|
||||
api_version="2024-02-01",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["max_completion_tokens"] == 64
|
||||
assert params["reasoning_effort"] == "high"
|
||||
assert "tools" not in params
|
||||
|
|
|
|||
|
|
@ -299,3 +299,89 @@ def test_azure_gpt5_1_does_not_support_logprobs(config: AzureOpenAIGPT5Config):
|
|||
supported_params = config.get_supported_openai_params(model="gpt-5.1")
|
||||
assert "logprobs" not in supported_params
|
||||
assert "top_logprobs" not in supported_params
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", ["gpt-5", "gpt-5.4-mini"])
|
||||
def test_azure_gpt5_tool_choice_required_gated_by_api_version(model):
|
||||
"""
|
||||
tool_choice='required' is not supported by Azure on api_version<=2024-05-01.
|
||||
|
||||
Azure GPT-5 deployments must honor the same api_version gating as the
|
||||
non-GPT-5 Azure path instead of silently forwarding 'required'.
|
||||
"""
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "f",
|
||||
"parameters": {"type": "object", "properties": {}},
|
||||
},
|
||||
}
|
||||
]
|
||||
with pytest.raises(litellm.UnsupportedParamsError):
|
||||
litellm.get_optional_params(
|
||||
model=model,
|
||||
custom_llm_provider="azure",
|
||||
tool_choice="required",
|
||||
tools=tools,
|
||||
api_version="2024-05-01-preview",
|
||||
drop_params=False,
|
||||
)
|
||||
|
||||
# newer api_version supports it
|
||||
params = litellm.get_optional_params(
|
||||
model=model,
|
||||
custom_llm_provider="azure",
|
||||
tool_choice="required",
|
||||
tools=tools,
|
||||
api_version="2025-01-01-preview",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["tool_choice"] == "required"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", ["gpt-5", "gpt-5.4-mini"])
|
||||
def test_azure_gpt5_response_format_falls_back_to_tools_on_old_api_version(model):
|
||||
"""
|
||||
On api_versions that predate native json_schema support, Azure GPT-5 should
|
||||
convert response_format into a tool call, matching the non-GPT-5 Azure path.
|
||||
"""
|
||||
response_format = {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "r",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"a": {"type": "string"}},
|
||||
"required": ["a"],
|
||||
},
|
||||
"strict": True,
|
||||
},
|
||||
}
|
||||
params = litellm.get_optional_params(
|
||||
model=model,
|
||||
custom_llm_provider="azure",
|
||||
response_format=response_format,
|
||||
api_version="2024-02-01",
|
||||
drop_params=False,
|
||||
)
|
||||
assert "response_format" not in params
|
||||
assert "tools" in params
|
||||
|
||||
|
||||
def test_azure_gpt5_without_gated_params_is_unchanged():
|
||||
"""
|
||||
When no api_version-gated params (tool_choice/response_format) are passed,
|
||||
the GPT-5 mapping should behave exactly like the OpenAI GPT-5 path.
|
||||
"""
|
||||
params = litellm.get_optional_params(
|
||||
model="gpt-5",
|
||||
custom_llm_provider="azure",
|
||||
max_tokens=64,
|
||||
reasoning_effort="high",
|
||||
api_version="2024-02-01",
|
||||
drop_params=False,
|
||||
)
|
||||
assert params["max_completion_tokens"] == 64
|
||||
assert params["reasoning_effort"] == "high"
|
||||
assert "tools" not in params
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue