mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-13 08:52:02 +00:00
fix(azure): use supported API versions for Responses requests (#51116)
This commit is contained in:
parent
8fb9f2f090
commit
13089b191b
2 changed files with 72 additions and 12 deletions
|
|
@ -37,6 +37,16 @@ _AZURE_RESPONSES_PROMPT_CACHE_BREAKPOINT: Final = TypeAdapter(AzureResponsesProm
|
|||
|
||||
|
||||
_DATED_GA_API_VERSION: Final = re.compile(r"\d{4}-\d{2}-\d{2}")
|
||||
_DATED_API_VERSION: Final = re.compile(r"\d{4}-\d{2}-\d{2}(?:-preview)?")
|
||||
_MIN_RESPONSES_API_DATE: Final = "2025-03-01"
|
||||
|
||||
|
||||
def _predates_responses_api(api_version: str | None) -> bool:
|
||||
return (
|
||||
api_version is not None
|
||||
and _DATED_API_VERSION.fullmatch(api_version) is not None
|
||||
and api_version[:10] < _MIN_RESPONSES_API_DATE
|
||||
)
|
||||
|
||||
|
||||
def _str_param(litellm_params: Mapping[str, object], key: str) -> str | None:
|
||||
|
|
@ -45,16 +55,14 @@ def _str_param(litellm_params: Mapping[str, object], key: str) -> str | None:
|
|||
|
||||
|
||||
def _resolve_responses_api_version(api_base: str | None, litellm_params: Mapping[str, object]) -> str | None:
|
||||
"""
|
||||
Dated GA api_versions (e.g. 2024-10-21) predate the Responses API on Azure and 404 there, so a deployment
|
||||
pinned to one for chat gets the default Responses version unless it sets responses_api_version.
|
||||
"""
|
||||
api_version: Final = _str_param(litellm_params, "api_version")
|
||||
if api_base is not None and "api-version" in httpx.URL(api_base).params:
|
||||
return api_version
|
||||
responses_api_version: Final = _str_param(litellm_params, "responses_api_version")
|
||||
if responses_api_version is not None:
|
||||
return responses_api_version
|
||||
api_version: Final = _str_param(litellm_params, "api_version")
|
||||
if api_base is not None and "api-version" in httpx.URL(api_base).params:
|
||||
return api_version
|
||||
if _predates_responses_api(api_version):
|
||||
return None
|
||||
if api_version is not None and _DATED_GA_API_VERSION.fullmatch(api_version):
|
||||
return AZURE_DEFAULT_RESPONSES_API_VERSION
|
||||
return api_version
|
||||
|
|
@ -188,7 +196,7 @@ class AzureOpenAIResponsesAPIConfig(OpenAIResponsesAPIConfig):
|
|||
def get_complete_url(
|
||||
self,
|
||||
api_base: str | None,
|
||||
litellm_params: dict,
|
||||
litellm_params: dict[str, object],
|
||||
) -> str:
|
||||
"""
|
||||
Constructs a complete URL for the API request.
|
||||
|
|
@ -206,7 +214,17 @@ class AzureOpenAIResponsesAPIConfig(OpenAIResponsesAPIConfig):
|
|||
- A complete URL string, e.g.,
|
||||
"https://litellm8397336933.openai.azure.com/openai/responses?api-version=2024-05-01-preview"
|
||||
"""
|
||||
resolved_api_base: Final = BaseAzureLLM.resolve_azure_api_base(api_base)
|
||||
base: Final = BaseAzureLLM.resolve_azure_api_base(api_base)
|
||||
parsed_url: Final = httpx.URL(base) if base is not None else None
|
||||
resolved_api_base: Final = (
|
||||
str(parsed_url.copy_with(params=parsed_url.params.remove("api-version")))
|
||||
if parsed_url is not None
|
||||
and (
|
||||
_predates_responses_api(parsed_url.params.get("api-version"))
|
||||
or _str_param(litellm_params, "responses_api_version") is not None
|
||||
)
|
||||
else base
|
||||
)
|
||||
responses_params: Final = _with_responses_api_version(resolved_api_base, litellm_params)
|
||||
parsed_base: Final = urlsplit(resolved_api_base or "")
|
||||
path: Final = parsed_base.path.rstrip("/")
|
||||
|
|
|
|||
|
|
@ -93,13 +93,13 @@ def test_get_complete_url():
|
|||
"""
|
||||
azure_openai_responses_apiconfig = AzureOpenAIResponsesAPIConfig()
|
||||
api_base = "https://litellm8397336933.openai.azure.com"
|
||||
litellm_params = {"api_version": "2024-05-01-preview"}
|
||||
litellm_params = {"api_version": "2025-04-01-preview"}
|
||||
|
||||
result = azure_openai_responses_apiconfig.get_complete_url(
|
||||
api_base=api_base, litellm_params=litellm_params
|
||||
)
|
||||
|
||||
expected = "https://litellm8397336933.openai.azure.com/openai/responses?api-version=2024-05-01-preview"
|
||||
expected = "https://litellm8397336933.openai.azure.com/openai/responses?api-version=2025-04-01-preview"
|
||||
|
||||
assert result == expected
|
||||
|
||||
|
|
@ -467,7 +467,12 @@ class TestAzureResponsesAPIConfig:
|
|||
litellm_params={"api_version": "2024-10-21", "responses_api_version": responses_api_version},
|
||||
)
|
||||
|
||||
assert url == api_base
|
||||
expected: Final = (
|
||||
"https://litellm8397336933.openai.azure.com/openai/v1/responses?api-version=v1"
|
||||
if responses_api_version is not None
|
||||
else api_base
|
||||
)
|
||||
assert url == expected
|
||||
|
||||
def test_api_version_in_env_api_base_query_wins_over_a_dated_ga_api_version(self, monkeypatch: pytest.MonkeyPatch):
|
||||
env_api_base: Final = (
|
||||
|
|
@ -1015,6 +1020,43 @@ def _unset_api_version_responses_url() -> httpx.URL:
|
|||
return httpx.URL(AzureOpenAIResponsesAPIConfig().get_complete_url(api_base=_AZURE_RESOURCE, litellm_params={}))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api_version", ["2025-02-01-preview", "2025-02-28", "2024-05-01-preview"])
|
||||
@pytest.mark.parametrize("source", ["params", "query", "both"])
|
||||
def test_pre_responses_api_version_uses_the_native_responses_default(api_version: str, source: str) -> None:
|
||||
base: Final = (
|
||||
f"{_AZURE_RESOURCE}?api-version={api_version}&tenant=test"
|
||||
if source in ("query", "both")
|
||||
else f"{_AZURE_RESOURCE}?tenant=test"
|
||||
)
|
||||
params: Final = {"api_version": api_version} if source in ("params", "both") else {}
|
||||
url: Final = httpx.URL(AzureOpenAIResponsesAPIConfig().get_complete_url(api_base=base, litellm_params=params))
|
||||
expected: Final = _unset_api_version_responses_url()
|
||||
|
||||
assert url.copy_with(query=None) == expected.copy_with(query=None)
|
||||
assert dict(url.params) == {**dict(expected.params), "tenant": "test"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api_version", ["2025-03-01-preview", "2025-04-01-preview", "preview", "latest", "v1"])
|
||||
@pytest.mark.parametrize("source", ["params", "query"])
|
||||
def test_supported_responses_api_versions_are_preserved(api_version: str, source: str) -> None:
|
||||
base: Final = f"{_AZURE_RESOURCE}?api-version={api_version}" if source == "query" else _AZURE_RESOURCE
|
||||
params: Final = {"api_version": api_version} if source == "params" else {}
|
||||
url: Final = httpx.URL(AzureOpenAIResponsesAPIConfig().get_complete_url(api_base=base, litellm_params=params))
|
||||
|
||||
assert url.params["api-version"] == api_version
|
||||
|
||||
|
||||
@pytest.mark.parametrize("api_version", ["2025-02-01-preview", "2025-04-01-preview"])
|
||||
def test_responses_override_wins_over_query_and_chat_version(api_version: str) -> None:
|
||||
config: Final = AzureOpenAIResponsesAPIConfig()
|
||||
params: Final = {"api_version": api_version, "responses_api_version": "2025-03-01-preview"}
|
||||
url: Final = config.get_complete_url(
|
||||
api_base=f"{_AZURE_RESOURCE}?api-version={api_version}", litellm_params=params
|
||||
)
|
||||
|
||||
assert url == f"{_AZURE_RESOURCE}/openai/responses?api-version=2025-03-01-preview"
|
||||
|
||||
|
||||
def _azure_responses_create(api_version: str) -> str | None:
|
||||
response: Final = litellm.responses(
|
||||
model="azure/gpt-5.4-nano",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue