From ef9f82080186c1c5c0a777328f3e79e72619c3be Mon Sep 17 00:00:00 2001 From: Noa Flaherty Date: Fri, 17 Apr 2026 16:54:19 -0400 Subject: [PATCH] [managed] Enable OpenAI managed-proxy fallback routing in provider bootstrap --- .../__tests__/managed-proxy-context.test.ts | 8 ++- ...provider-managed-proxy-integration.test.ts | 50 +++++++++++++++---- .../secret-routes-managed-proxy.test.ts | 2 +- .../src/providers/managed-proxy/constants.ts | 3 +- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/assistant/src/__tests__/managed-proxy-context.test.ts b/assistant/src/__tests__/managed-proxy-context.test.ts index 1726060fccb..6e7b5ed7ad9 100644 --- a/assistant/src/__tests__/managed-proxy-context.test.ts +++ b/assistant/src/__tests__/managed-proxy-context.test.ts @@ -112,10 +112,12 @@ describe("buildManagedBaseUrl", () => { expect(await buildManagedBaseUrl("gemini")).toBe( "https://platform.example.com/v1/runtime-proxy/gemini", ); + expect(await buildManagedBaseUrl("openai")).toBe( + "https://platform.example.com/v1/runtime-proxy/openai", + ); }); test("returns undefined for non-managed providers", async () => { - expect(await buildManagedBaseUrl("openai")).toBeUndefined(); expect(await buildManagedBaseUrl("fireworks")).toBeUndefined(); expect(await buildManagedBaseUrl("openrouter")).toBeUndefined(); expect(await buildManagedBaseUrl("ollama")).toBeUndefined(); @@ -130,6 +132,7 @@ describe("buildManagedBaseUrl", () => { mockAssistantApiKey = null; expect(await buildManagedBaseUrl("anthropic")).toBeUndefined(); expect(await buildManagedBaseUrl("gemini")).toBeUndefined(); + expect(await buildManagedBaseUrl("openai")).toBeUndefined(); }); }); @@ -142,7 +145,7 @@ describe("managedFallbackEnabledFor", () => { test("returns true only for managed fallback providers with prerequisites", async () => { expect(await managedFallbackEnabledFor("anthropic")).toBe(true); expect(await managedFallbackEnabledFor("gemini")).toBe(true); - expect(await managedFallbackEnabledFor("openai")).toBe(false); + expect(await managedFallbackEnabledFor("openai")).toBe(true); }); test("returns false for non-managed provider", async () => { @@ -158,5 +161,6 @@ describe("managedFallbackEnabledFor", () => { mockAssistantApiKey = null; expect(await managedFallbackEnabledFor("anthropic")).toBe(false); expect(await managedFallbackEnabledFor("gemini")).toBe(false); + expect(await managedFallbackEnabledFor("openai")).toBe(false); }); }); diff --git a/assistant/src/__tests__/provider-managed-proxy-integration.test.ts b/assistant/src/__tests__/provider-managed-proxy-integration.test.ts index 2581be09cd3..89aea943147 100644 --- a/assistant/src/__tests__/provider-managed-proxy-integration.test.ts +++ b/assistant/src/__tests__/provider-managed-proxy-integration.test.ts @@ -90,7 +90,7 @@ const DIRECT_OR_MANAGED_PROVIDER_KEYS: string[] = [ "fireworks", "openrouter", ]; -const MANAGED_FALLBACK_PROVIDERS: string[] = ["anthropic", "gemini"]; +const MANAGED_FALLBACK_PROVIDERS: string[] = ["anthropic", "gemini", "openai"]; function enableManagedProxy() { mockPlatformBaseUrl = PLATFORM_BASE; @@ -172,14 +172,18 @@ describe("managed proxy integration — credential precedence", () => { }, ); - test("managed bootstrap registers anthropic and gemini only", async () => { + test("managed bootstrap registers anthropic, openai, and gemini", async () => { enableManagedProxy(); mockProviderKeys = {}; await initializeProviders(makeProvidersConfig("anthropic", "test-model")); - expect(listProviders()).toEqual(["anthropic", "gemini"]); + expect(listProviders()).toEqual( + expect.arrayContaining(["anthropic", "openai", "gemini"]), + ); + expect(listProviders()).toHaveLength(3); expect(getProviderRoutingSource("anthropic")).toBe("managed-proxy"); + expect(getProviderRoutingSource("openai")).toBe("managed-proxy"); expect(getProviderRoutingSource("gemini")).toBe("managed-proxy"); - for (const p of ["openai", "fireworks", "openrouter"]) { + for (const p of ["fireworks", "openrouter"]) { expect(getProviderRoutingSource(p)).toBeUndefined(); } }); @@ -205,6 +209,23 @@ describe("managed proxy integration — credential precedence", () => { expect(baseURL).toContain("/v1/runtime-proxy/anthropic"); }); + test("managed openai uses openai proxy path", async () => { + enableManagedProxy(); + mockProviderKeys = {}; + await initializeProviders(makeProvidersConfig("openai", "gpt-4o")); + + const provider = getProvider("openai"); + + // Unwrap RetryProvider → OpenAIResponsesProvider to inspect the OpenAI + // SDK client's baseURL. + const retryInner = (provider as any).inner; + const openaiClient = (retryInner as any).client; + + expect(openaiClient).toBeDefined(); + const baseURL: string = openaiClient.baseURL; + expect(baseURL).toContain("/v1/runtime-proxy/openai"); + }); + test("managed gemini uses gemini proxy path", async () => { enableManagedProxy(); mockProviderKeys = {}; @@ -242,16 +263,18 @@ describe("managed proxy integration — credential precedence", () => { }); describe("mixed: some user keys + managed fallback fills gaps", () => { - test("user key for anthropic routes direct and managed fallback only fills gemini", async () => { + test("user key for anthropic routes direct and managed fallback fills openai and gemini", async () => { enableManagedProxy(); setUserKeysFor("anthropic"); await initializeProviders(makeProvidersConfig("anthropic", "test-model")); const registered = listProviders(); expect(registered).toContain("anthropic"); expect(getProviderRoutingSource("anthropic")).toBe("user-key"); + expect(registered).toContain("openai"); + expect(getProviderRoutingSource("openai")).toBe("managed-proxy"); expect(registered).toContain("gemini"); expect(getProviderRoutingSource("gemini")).toBe("managed-proxy"); - for (const p of ["openai", "fireworks", "openrouter"]) { + for (const p of ["fireworks", "openrouter"]) { expect(registered).not.toContain(p); expect(getProviderRoutingSource(p)).toBeUndefined(); } @@ -268,6 +291,7 @@ describe("managed proxy integration — credential precedence", () => { expect(getProviderRoutingSource("anthropic")).toBe("managed-proxy"); expect(registered).toContain("gemini"); expect(getProviderRoutingSource("gemini")).toBe("managed-proxy"); + // OpenAI has a user key so it's user-key, not managed-proxy for (const p of ["fireworks", "openrouter"]) { expect(registered).not.toContain(p); expect(getProviderRoutingSource(p)).toBeUndefined(); @@ -307,8 +331,8 @@ describe("managed proxy integration — ollama exclusion", () => { }); describe("managed proxy integration — constants integrity", () => { - test("anthropic and gemini have metadata with managed=true and a proxyPath", () => { - for (const provider of ["anthropic", "gemini"]) { + test("anthropic, openai, and gemini have metadata with managed=true and a proxyPath", () => { + for (const provider of ["anthropic", "openai", "gemini"]) { const meta = MANAGED_PROVIDER_META[provider]; expect(meta).toBeDefined(); expect(meta.managed).toBe(true); @@ -329,8 +353,14 @@ describe("managed proxy integration — constants integrity", () => { ); }); - test("openai-compatible providers are not managed proxy capable", () => { - for (const provider of ["openai", "fireworks", "openrouter"]) { + test("openai routes through openai proxy path", () => { + expect(MANAGED_PROVIDER_META.openai.proxyPath).toBe( + "/v1/runtime-proxy/openai", + ); + }); + + test("fireworks and openrouter are not managed proxy capable", () => { + for (const provider of ["fireworks", "openrouter"]) { expect(MANAGED_PROVIDER_META[provider].managed).toBe(false); expect(MANAGED_PROVIDER_META[provider].proxyPath).toBeUndefined(); } diff --git a/assistant/src/__tests__/secret-routes-managed-proxy.test.ts b/assistant/src/__tests__/secret-routes-managed-proxy.test.ts index 327cb00925f..97f358b855f 100644 --- a/assistant/src/__tests__/secret-routes-managed-proxy.test.ts +++ b/assistant/src/__tests__/secret-routes-managed-proxy.test.ts @@ -11,7 +11,7 @@ let providerRefreshCalls = 0; const PLATFORM_BASE_URL = "https://platform.example.com"; const ASSISTANT_API_KEY_PATH = credentialKey("vellum", "assistant_api_key"); const PLATFORM_BASE_URL_PATH = credentialKey("vellum", "platform_base_url"); -const MANAGED_PROVIDERS = ["anthropic", "gemini"] as const; +const MANAGED_PROVIDERS = ["anthropic", "openai", "gemini"] as const; let platformBaseUrlOverride: string | undefined; diff --git a/assistant/src/providers/managed-proxy/constants.ts b/assistant/src/providers/managed-proxy/constants.ts index fa532411e66..7082b5cf8f8 100644 --- a/assistant/src/providers/managed-proxy/constants.ts +++ b/assistant/src/providers/managed-proxy/constants.ts @@ -28,7 +28,8 @@ export interface ManagedProviderMeta { export const MANAGED_PROVIDER_META: Record = { openai: { name: "openai", - managed: false, + managed: true, + proxyPath: "/v1/runtime-proxy/openai", }, anthropic: { name: "anthropic",