From 99f3c554c8595bb0c94b8f7bac66d61386cfeae1 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Wed, 8 Apr 2026 22:14:46 +0200 Subject: [PATCH] feat: support Azure v1 endpoint format (/openai/v1) (#23484) Azure offers two URL formats: the legacy deployment-based format (/openai/deployments/{model}/...) and the newer v1 format (/openai/v1/...) where the model stays in the payload body and no api-version query parameter is needed. Previously, the code always ran convert_to_azure_payload which rewrites the URL to the deployment format, causing 404 errors for users with v1-style base URLs. Now, when the base URL contains '/openai/v1', we skip deployment URL construction and route directly. Applied consistently across all three Azure routing paths: generate_chat_completion, /responses proxy, and generic proxy. --- backend/open_webui/routers/openai.py | 58 ++++++++++++++++++---------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index 0e7c67c1f6..836517df9d 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -1131,21 +1131,31 @@ async def generate_chat_completion( is_responses = api_config.get('api_type') == 'responses' if api_config.get('azure', False): - api_version = api_config.get('api_version', '2023-03-15-preview') - request_url, payload = convert_to_azure_payload(url, payload, api_version) - # Only set api-key header if not using Azure Entra ID authentication auth_type = api_config.get('auth_type', 'bearer') if auth_type not in ('azure_ad', 'microsoft_entra_id'): headers['api-key'] = key - headers['api-version'] = api_version + # Azure v1 format: base URL already ends with /openai/v1, + # model stays in the payload, no deployment URL rewriting. + is_azure_v1 = bool(re.search(r'/openai/v1(?:/|$)', url)) - if is_responses: - payload = convert_to_responses_payload(payload) - request_url = f'{request_url}/responses?api-version={api_version}' + if is_azure_v1: + if is_responses: + payload = convert_to_responses_payload(payload) + request_url = f'{url.rstrip("/")}/responses' + else: + request_url = f'{url.rstrip("/")}/chat/completions' else: - request_url = f'{request_url}/chat/completions?api-version={api_version}' + api_version = api_config.get('api_version', '2023-03-15-preview') + request_url, payload = convert_to_azure_payload(url, payload, api_version) + headers['api-version'] = api_version + + if is_responses: + payload = convert_to_responses_payload(payload) + request_url = f'{request_url}/responses?api-version={api_version}' + else: + request_url = f'{request_url}/chat/completions?api-version={api_version}' else: if is_responses: payload = convert_to_responses_payload(payload) @@ -1357,16 +1367,19 @@ async def responses( headers, cookies = await get_headers_and_cookies(request, url, key, api_config, user=user) if api_config.get('azure', False): - api_version = api_config.get('api_version', '2023-03-15-preview') - auth_type = api_config.get('auth_type', 'bearer') if auth_type not in ('azure_ad', 'microsoft_entra_id'): headers['api-key'] = key - headers['api-version'] = api_version + is_azure_v1 = bool(re.search(r'/openai/v1(?:/|$)', url)) - model = payload.get('model', '') - request_url = f'{url}/openai/deployments/{model}/responses?api-version={api_version}' + if is_azure_v1: + request_url = f'{url.rstrip("/")}/responses' + else: + api_version = api_config.get('api_version', '2023-03-15-preview') + headers['api-version'] = api_version + model = payload.get('model', '') + request_url = f'{url}/openai/deployments/{model}/responses?api-version={api_version}' else: request_url = f'{url}/responses' @@ -1459,20 +1472,25 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)): headers, cookies = await get_headers_and_cookies(request, url, key, api_config, user=user) if api_config.get('azure', False): - api_version = api_config.get('api_version', '2023-03-15-preview') - # Only set api-key header if not using Azure Entra ID authentication auth_type = api_config.get('auth_type', 'bearer') if auth_type not in ('azure_ad', 'microsoft_entra_id'): headers['api-key'] = key - headers['api-version'] = api_version + is_azure_v1 = bool(re.search(r'/openai/v1(?:/|$)', url)) - payload = json.loads(body) - url, payload = convert_to_azure_payload(url, payload, api_version) - body = json.dumps(payload).encode() + if is_azure_v1: + qs = request.url.query + request_url = f'{url.rstrip("/")}/{path}' + (f'?{qs}' if qs else '') + else: + api_version = api_config.get('api_version', '2023-03-15-preview') + headers['api-version'] = api_version - request_url = f'{url}/{path}?api-version={api_version}' + payload = json.loads(body) + url, payload = convert_to_azure_payload(url, payload, api_version) + body = json.dumps(payload).encode() + + request_url = f'{url}/{path}?api-version={api_version}' else: request_url = f'{url}/{path}'