diff --git a/packages/openai-adapters/src/apis/Azure.ts b/packages/openai-adapters/src/apis/Azure.ts index 4d161c8438a..a4f4203d621 100644 --- a/packages/openai-adapters/src/apis/Azure.ts +++ b/packages/openai-adapters/src/apis/Azure.ts @@ -35,6 +35,10 @@ export class AzureApi extends OpenAIApi { return apiType === "azure-openai" || apiType === "azure"; } + private _isAzureFoundry(apiType?: string): boolean { + return apiType === "azure-foundry"; + } + private _getAzureBaseURL(config: z.infer): { baseURL: string; defaultQuery: Record; @@ -50,6 +54,17 @@ export class AzureApi extends OpenAIApi { url.pathname = url.pathname.replace(/\/$/, ""); // Remove trailing slash if present url.search = ""; // Clear original search params + // Azure AI Foundry (a.k.a. Microsoft Foundry / Azure serverless) uses an + // OpenAI-compatible routing path (`/openai/v1/...`) that the user supplies + // in apiBase. We must NOT append `/openai/deployments/{deployment}` like the + // legacy Azure OpenAI Service path — doing so 404s on Foundry endpoints. + if (this._isAzureFoundry(config.env?.apiType)) { + return { + baseURL: url.toString(), + defaultQuery: queryParams, + }; + } + // Default is `azure-openai` in docs, but previously was `azure` if (this._isAzureOpenAI(config.env?.apiType)) { if (!config.env?.deployment) { diff --git a/packages/openai-adapters/src/test/main.test.ts b/packages/openai-adapters/src/test/main.test.ts index 8a10c8b727f..a64f177288a 100644 --- a/packages/openai-adapters/src/test/main.test.ts +++ b/packages/openai-adapters/src/test/main.test.ts @@ -330,6 +330,38 @@ describe("Configuration", () => { expect((azure as OpenAIApi).openai.apiKey).toBe("sk-xxx"); }); + it("should configure Azure AI Foundry client without appending /openai/deployments/{deployment}", () => { + const azure = constructLlmApi({ + provider: "azure", + apiKey: "sk-xxx", + apiBase: "https://my-resource.services.ai.azure.com/openai/v1", + env: { + apiType: "azure-foundry", + }, + }); + + // Azure AI Foundry endpoints use the apiBase as-is (OpenAI-compatible + // routing path supplied by the user). Appending /openai/deployments/{deployment} + // would 404 because Foundry does not expose that legacy path. + expect((azure as OpenAIApi).openai.baseURL).toBe( + "https://my-resource.services.ai.azure.com/openai/v1", + ); + expect((azure as OpenAIApi).openai.apiKey).toBe("sk-xxx"); + }); + + it("should not require env.deployment or env.apiVersion for Azure AI Foundry", () => { + expect(() => + constructLlmApi({ + provider: "azure", + apiKey: "sk-xxx", + apiBase: "https://my-resource.services.ai.azure.com/openai/v1", + env: { + apiType: "azure-foundry", + }, + }), + ).not.toThrow(); + }); + describe("ollama api base", () => { it('should have correct default API base for "ollama"', () => { const ollama = constructLlmApi({