diff --git a/packages/core/src/session/runner/model.ts b/packages/core/src/session/runner/model.ts index 4749e6f72d..e002aa6c21 100644 --- a/packages/core/src/session/runner/model.ts +++ b/packages/core/src/session/runner/model.ts @@ -182,12 +182,11 @@ export const fromCatalogModel = ( credential?: Credential.Value, dependencies: Dependencies = {}, ): Effect.Effect => { - const resolved = - credential?.type !== "key" || credential.metadata === undefined - ? model - : produce(model, (draft) => { - draft.body = ProviderV2.mergeOverlay(draft.body, credential.metadata) - }) + const resolved = produce(model, (draft) => { + if (draft.settings?.apiKey === "") delete draft.settings.apiKey + if (credential?.type === "key" && credential.metadata !== undefined) + draft.body = ProviderV2.mergeOverlay(draft.body, credential.metadata) + }) const packageName = ProviderV2.packageName(resolved.package) const key = apiKey(resolved, credential) diff --git a/packages/core/test/session-runner-model.test.ts b/packages/core/test/session-runner-model.test.ts index c2f942cd09..291cd66cc3 100644 --- a/packages/core/test/session-runner-model.test.ts +++ b/packages/core/test/session-runner-model.test.ts @@ -78,6 +78,25 @@ describe("SessionRunnerModel", () => { }), ) + it.effect("treats an empty configured API key as omitted", () => + Effect.gen(function* () { + const resolved = yield* SessionRunnerModel.fromCatalogModel( + model(ProviderV2.aisdk("@ai-sdk/openai"), { + settings: { apiKey: "", baseURL: "https://openai.example/v1" }, + }), + ) + const headers = yield* resolved.route.auth.apply({ + request: LLM.request({ model: resolved, prompt: "Hello" }), + method: "POST", + url: "https://openai.example/v1/responses", + body: "{}", + headers: Headers.empty, + }) + + expect(headers.authorization).toBeUndefined() + }), + ) + it.effect("uses merged API settings for OpenAI-compatible auth and request defaults", () => Effect.gen(function* () { const resolved = yield* SessionRunnerModel.fromCatalogModel( @@ -637,6 +656,29 @@ describe("SessionRunnerModel", () => { }), ) + it.effect("drops an empty API key before loading an AISDK package", () => + Effect.gen(function* () { + const native = yield* SessionRunnerModel.fromCatalogModel( + model(ProviderV2.aisdk("@ai-sdk/openai"), { + settings: { baseURL: "https://openai.example/v1" }, + }), + ) + yield* SessionRunnerModel.fromCatalogModel( + model(ProviderV2.aisdk("@ai-sdk/google"), { + settings: { apiKey: "", baseURL: "https://google.example/v1" }, + }), + undefined, + { + loadAISDK: (runtime) => + Effect.sync(() => { + expect(runtime.settings).not.toHaveProperty("apiKey") + return native + }), + }, + ) + }), + ) + it.effect("reports whether a catalog model declares a provider package", () => Effect.sync(() => { expect(SessionRunnerModel.supported(model(ProviderV2.aisdk("@ai-sdk/openai")))).toBe(true)