From 7f1d168045d622fa1db3aab29084042387141108 Mon Sep 17 00:00:00 2001 From: Dulani Woods Date: Mon, 6 Apr 2026 16:08:58 -0400 Subject: [PATCH 1/3] fix(azure): skip /v1 and api-version when baseURL is provided When createAzure({ baseURL }) is used, the provider now constructs {baseURL}{path} instead of {baseURL}/v1{path}?api-version=... This allows custom API gateways and proxies that handle routing internally to work without receiving unexpected path segments or query parameters. Closes #13956. Also addresses #14009. --- .changeset/fix-azure-baseurl-no-v1.md | 12 ++++ .../azure/src/azure-openai-provider.test.ts | 55 ++++++++++++++++--- packages/azure/src/azure-openai-provider.ts | 12 +++- 3 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 .changeset/fix-azure-baseurl-no-v1.md diff --git a/.changeset/fix-azure-baseurl-no-v1.md b/.changeset/fix-azure-baseurl-no-v1.md new file mode 100644 index 000000000000..eba93d7fdad4 --- /dev/null +++ b/.changeset/fix-azure-baseurl-no-v1.md @@ -0,0 +1,12 @@ +--- +'@ai-sdk/azure': patch +--- + +fix(azure): skip /v1 path segment and api-version query param when baseURL is provided + +When `createAzure({ baseURL: '...' })` is used, the provider now constructs +`{baseURL}{path}` instead of `{baseURL}/v1{path}?api-version=...`. This allows +custom API gateways and proxies that handle routing internally to work without +receiving unexpected path segments or query parameters. + +Closes #13956. Also fixes the api-version issue reported in #14009. diff --git a/packages/azure/src/azure-openai-provider.test.ts b/packages/azure/src/azure-openai-provider.test.ts index d49f37a6d99f..3f768ebd98c4 100644 --- a/packages/azure/src/azure-openai-provider.test.ts +++ b/packages/azure/src/azure-openai-provider.test.ts @@ -89,6 +89,9 @@ const server = createTestServer({ 'https://test-resource.openai.azure.com/openai/v1/audio/speech': {}, 'https://test-resource.openai.azure.com/openai/deployments/whisper-1/audio/transcriptions': {}, + 'https://test-resource.openai.azure.com/openai/responses': {}, + 'https://test-resource.openai.azure.com/openai/chat/completions': {}, + 'https://test-resource.openai.azure.com/openai/images/generations': {}, }); describe('responses (default language model)', () => { @@ -187,8 +190,14 @@ describe('responses (default language model)', () => { ); }); - it('should use the baseURL correctly', async () => { + it('should use the baseURL without appending /v1 or api-version', async () => { prepareJsonResponse(); + server.urls[ + 'https://test-resource.openai.azure.com/openai/responses' + ].response = + server.urls[ + 'https://test-resource.openai.azure.com/openai/v1/responses' + ].response; const provider = createAzure({ baseURL: 'https://test-resource.openai.azure.com/openai', @@ -199,8 +208,11 @@ describe('responses (default language model)', () => { prompt: TEST_PROMPT, }); expect(server.calls[0].requestUrl).toMatchInlineSnapshot( - `"https://test-resource.openai.azure.com/openai/v1/responses?api-version=v1"`, + `"https://test-resource.openai.azure.com/openai/responses"`, ); + expect( + server.calls[0].requestUrlSearchParams.get('api-version'), + ).toBeNull(); }); }); }); @@ -292,8 +304,14 @@ describe('chat', () => { ); }); - it('should use the baseURL correctly', async () => { + it('should use the baseURL without appending /v1 or api-version', async () => { prepareJsonResponse(); + server.urls[ + 'https://test-resource.openai.azure.com/openai/chat/completions' + ].response = + server.urls[ + 'https://test-resource.openai.azure.com/openai/v1/chat/completions' + ].response; const provider = createAzure({ baseURL: 'https://test-resource.openai.azure.com/openai', @@ -304,8 +322,11 @@ describe('chat', () => { prompt: TEST_PROMPT, }); expect(server.calls[0].requestUrl).toMatchInlineSnapshot( - `"https://test-resource.openai.azure.com/openai/v1/chat/completions?api-version=v1"`, + `"https://test-resource.openai.azure.com/openai/chat/completions"`, ); + expect( + server.calls[0].requestUrlSearchParams.get('api-version'), + ).toBeNull(); }); }); }); @@ -650,8 +671,14 @@ describe('image', () => { ); }); - it('should use the baseURL correctly', async () => { + it('should use the baseURL without appending /v1 or api-version', async () => { prepareJsonResponse(); + server.urls[ + 'https://test-resource.openai.azure.com/openai/images/generations' + ].response = + server.urls[ + 'https://test-resource.openai.azure.com/openai/v1/images/generations' + ].response; const provider = createAzure({ baseURL: 'https://test-resource.openai.azure.com/openai', @@ -670,8 +697,11 @@ describe('image', () => { }); expect(server.calls[0].requestUrl).toMatchInlineSnapshot( - `"https://test-resource.openai.azure.com/openai/v1/images/generations?api-version=v1"`, + `"https://test-resource.openai.azure.com/openai/images/generations"`, ); + expect( + server.calls[0].requestUrlSearchParams.get('api-version'), + ).toBeNull(); }); it('should extract the generated images', async () => { @@ -873,8 +903,14 @@ describe('responses', () => { ); }); - it('should use the baseURL correctly', async () => { + it('should use the baseURL without appending /v1 or api-version', async () => { prepareJsonFixtureResponse('azure-text.1'); + server.urls[ + 'https://test-resource.openai.azure.com/openai/responses' + ].response = + server.urls[ + 'https://test-resource.openai.azure.com/openai/v1/responses' + ].response; const provider = createAzure({ baseURL: 'https://test-resource.openai.azure.com/openai', @@ -886,8 +922,11 @@ describe('responses', () => { }); expect(server.calls[0].requestUrl).toMatchInlineSnapshot( - `"https://test-resource.openai.azure.com/openai/v1/responses?api-version=v1"`, + `"https://test-resource.openai.azure.com/openai/responses"`, ); + expect( + server.calls[0].requestUrlSearchParams.get('api-version'), + ).toBeNull(); }); it('should handle Azure file IDs with assistant- prefix', async () => { diff --git a/packages/azure/src/azure-openai-provider.ts b/packages/azure/src/azure-openai-provider.ts index 8afa0a7d13e6..d3107d2a07db 100644 --- a/packages/azure/src/azure-openai-provider.ts +++ b/packages/azure/src/azure-openai-provider.ts @@ -105,7 +105,9 @@ export interface AzureOpenAIProviderSettings { * Use a different URL prefix for API calls, e.g. to use proxy servers. Either this or `resourceName` can be used. * When a baseURL is provided, the resourceName is ignored. * - * With a baseURL, the resolved URL is `{baseURL}/v1{path}`. + * With a baseURL, the resolved URL is `{baseURL}{path}` — no `/v1` is appended and no + * `api-version` query parameter is added. This allows custom gateways and proxies that + * handle routing internally to receive the URL exactly as provided. */ baseURL?: string; @@ -174,12 +176,18 @@ export function createAzure( if (options.useDeploymentBasedUrls) { // Use deployment-based format for compatibility with certain Azure OpenAI models fullUrl = new URL(`${baseUrlPrefix}/deployments/${modelId}${path}`); + } else if (options.baseURL) { + // When baseURL is explicitly provided, use it as-is without appending /v1. + // Callers using a custom gateway or proxy control the URL shape themselves. + fullUrl = new URL(`${baseUrlPrefix}${path}`); } else { // Use v1 API format - no deployment ID in URL fullUrl = new URL(`${baseUrlPrefix}/v1${path}`); } - fullUrl.searchParams.set('api-version', apiVersion); + if (!options.baseURL) { + fullUrl.searchParams.set('api-version', apiVersion); + } return fullUrl.toString(); }; From 56942ca3df6e53fe9e8647e2c04ff1da839bb296 Mon Sep 17 00:00:00 2001 From: Dulani Woods Date: Wed, 8 Apr 2026 12:51:32 -0400 Subject: [PATCH 2/3] fix(azure): preserve api-version when baseURL + useDeploymentBasedUrls are both set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When both options are provided, the deployment-based URL format is used (first branch of the if/else), so the api-version guard should not suppress it. Fix: `!options.baseURL` → `!options.baseURL || options.useDeploymentBasedUrls`. --- packages/azure/src/azure-openai-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/azure/src/azure-openai-provider.ts b/packages/azure/src/azure-openai-provider.ts index d3107d2a07db..674f37da1ce4 100644 --- a/packages/azure/src/azure-openai-provider.ts +++ b/packages/azure/src/azure-openai-provider.ts @@ -185,7 +185,7 @@ export function createAzure( fullUrl = new URL(`${baseUrlPrefix}/v1${path}`); } - if (!options.baseURL) { + if (!options.baseURL || options.useDeploymentBasedUrls) { fullUrl.searchParams.set('api-version', apiVersion); } return fullUrl.toString(); From 513493b3353dee5a729b2bbf25b9dbcf3a63e5a2 Mon Sep 17 00:00:00 2001 From: Dulani Woods Date: Wed, 8 Apr 2026 12:51:34 -0400 Subject: [PATCH 3/3] test(azure): add coverage for baseURL + useDeploymentBasedUrls edge case --- .../azure/src/azure-openai-provider.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/azure/src/azure-openai-provider.test.ts b/packages/azure/src/azure-openai-provider.test.ts index 3f768ebd98c4..0c68604efa25 100644 --- a/packages/azure/src/azure-openai-provider.test.ts +++ b/packages/azure/src/azure-openai-provider.test.ts @@ -92,6 +92,7 @@ const server = createTestServer({ 'https://test-resource.openai.azure.com/openai/responses': {}, 'https://test-resource.openai.azure.com/openai/chat/completions': {}, 'https://test-resource.openai.azure.com/openai/images/generations': {}, + 'https://test-resource.openai.azure.com/openai/deployments/test-deployment/chat/completions': {}, }); describe('responses (default language model)', () => { @@ -328,6 +329,32 @@ describe('chat', () => { server.calls[0].requestUrlSearchParams.get('api-version'), ).toBeNull(); }); + + it('should preserve api-version when both baseURL and useDeploymentBasedUrls are set', async () => { + prepareJsonResponse(); + server.urls[ + 'https://test-resource.openai.azure.com/openai/deployments/test-deployment/chat/completions' + ].response = + server.urls[ + 'https://test-resource.openai.azure.com/openai/v1/chat/completions' + ].response; + + const provider = createAzure({ + baseURL: 'https://test-resource.openai.azure.com/openai', + apiKey: 'test-api-key', + useDeploymentBasedUrls: true, + }); + + await provider.chat('test-deployment').doGenerate({ + prompt: TEST_PROMPT, + }); + expect(server.calls[0].requestUrl).toMatchInlineSnapshot( + `"https://test-resource.openai.azure.com/openai/deployments/test-deployment/chat/completions?api-version=v1"`, + ); + expect( + server.calls[0].requestUrlSearchParams.get('api-version'), + ).toBe('v1'); + }); }); });