diff --git a/packages/ai/src/providers/azure.ts b/packages/ai/src/providers/azure.ts index 94d5edb4c85..75e89e506bc 100644 --- a/packages/ai/src/providers/azure.ts +++ b/packages/ai/src/providers/azure.ts @@ -90,7 +90,8 @@ function endpoint(input: Config, modelID: string | ModelID) { if (input.baseURL !== undefined && !new URL(input.baseURL).hostname.endsWith(".openai.azure.com")) { return { baseURL, query: input.queryParams } } - return { baseURL: `${baseURL}/v1`, query } + // Azure's v1 API serves from /openai/v1; callers may pass the base URL with or without the version segment. + return { baseURL: baseURL.endsWith("/v1") ? baseURL : `${baseURL}/v1`, query } } export const configure = (input: Config) => { diff --git a/packages/ai/test/provider/openai-chat.test.ts b/packages/ai/test/provider/openai-chat.test.ts index 8d1febd6b8e..ed557fe2b79 100644 --- a/packages/ai/test/provider/openai-chat.test.ts +++ b/packages/ai/test/provider/openai-chat.test.ts @@ -255,7 +255,7 @@ describe("OpenAI Chat route", () => { LLMClient.generate( LLMRequest.update(request, { model: Azure.configure({ - baseURL: "https://opencode-test.openai.azure.com/openai/v1/", + baseURL: "https://opencode-test.openai.azure.com/openai/", apiKey: "azure-key", headers: { authorization: "Bearer stale" }, }).chat("gpt-4o-mini"), @@ -277,6 +277,29 @@ describe("OpenAI Chat route", () => { ), ) + it.effect("does not duplicate /v1 for already-versioned Azure Chat base URLs", () => + LLMClient.generate( + LLMRequest.update(request, { + model: Azure.configure({ + baseURL: "https://opencode-test.openai.azure.com/openai/v1/", + apiKey: "azure-key", + }).chat("gpt-4o-mini"), + }), + ).pipe( + Effect.provide( + dynamicResponse((input) => + Effect.gen(function* () { + const web = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie) + expect(web.url).toBe("https://opencode-test.openai.azure.com/openai/v1/chat/completions?api-version=v1") + return input.respond(sseEvents(deltaChunk({}, "stop")), { + headers: { "content-type": "text/event-stream" }, + }) + }), + ), + ), + ), + ) + it.effect("applies serializable HTTP overlays after payload lowering", () => LLMClient.generate( LLMRequest.update(request, {