From 850c210b77acadaf8ee11dab058953877b6c8ffd Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 11 Jul 2026 13:38:48 +0200 Subject: [PATCH] fix(ai): filter ambient auth markers in compat dispatch --- .../ai/src/api/bedrock-converse-stream.ts | 3 +- packages/ai/src/compat.ts | 3 +- .../test/bedrock-endpoint-resolution.test.ts | 28 +++++++++++-------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/ai/src/api/bedrock-converse-stream.ts b/packages/ai/src/api/bedrock-converse-stream.ts index d615d4ca..7a9e776e 100644 --- a/packages/ai/src/api/bedrock-converse-stream.ts +++ b/packages/ai/src/api/bedrock-converse-stream.ts @@ -101,7 +101,6 @@ export interface BedrockOptions extends StreamOptions { type Block = (TextContent | ThinkingContent | ToolCall) & { index?: number; partialJson?: string }; const EMPTY_TEXT_PLACEHOLDER = ""; -const AMBIENT_AUTH_MARKER = ""; export const stream: StreamFunction<"bedrock-converse-stream", BedrockOptions> = ( model: Model<"bedrock-converse-stream">, @@ -154,7 +153,7 @@ export const stream: StreamFunction<"bedrock-converse-stream", BedrockOptions> = const skipAuth = getProviderEnvValue("AWS_BEDROCK_SKIP_AUTH", options.env) === "1"; const bearerToken = options.bearerToken || - (options.apiKey !== AMBIENT_AUTH_MARKER ? options.apiKey : undefined) || + options.apiKey || getProviderEnvValue("AWS_BEARER_TOKEN_BEDROCK", options.env) || undefined; const useBearerToken = bearerToken !== undefined && !skipAuth; diff --git a/packages/ai/src/compat.ts b/packages/ai/src/compat.ts index 91ecda4f..eb1151fd 100644 --- a/packages/ai/src/compat.ts +++ b/packages/ai/src/compat.ts @@ -206,6 +206,7 @@ export function resetApiProviders(): void { registerBuiltInApiProviders(); const compatModels = builtinModels(); +const AMBIENT_AUTH_MARKER = ""; function hasExplicitApiKey(apiKey: string | undefined): apiKey is string { return typeof apiKey === "string" && apiKey.trim().length > 0; @@ -217,7 +218,7 @@ function withEnvApiKey( ): TOptions | undefined { if (hasExplicitApiKey(options?.apiKey)) return options; const apiKey = getEnvApiKey(model.provider, options?.env); - if (!apiKey) return options; + if (!apiKey || apiKey === AMBIENT_AUTH_MARKER) return options; return { ...options, apiKey } as TOptions; } diff --git a/packages/ai/test/bedrock-endpoint-resolution.test.ts b/packages/ai/test/bedrock-endpoint-resolution.test.ts index a7c1fee2..62797d15 100644 --- a/packages/ai/test/bedrock-endpoint-resolution.test.ts +++ b/packages/ai/test/bedrock-endpoint-resolution.test.ts @@ -44,8 +44,8 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => { }; }); -import { type BedrockOptions, stream as streamBedrock } from "../src/api/bedrock-converse-stream.ts"; -import { getModel } from "../src/compat.ts"; +import type { BedrockOptions } from "../src/api/bedrock-converse-stream.ts"; +import { getModel, stream as streamBedrock } from "../src/compat.ts"; import type { Context, Model } from "../src/types.ts"; const context: Context = { @@ -182,6 +182,21 @@ describe("bedrock endpoint resolution", () => { expect(config.region).toBe("us-gov-west-1"); }); + it("preserves ambient AWS auth for custom model IDs through compat dispatch", async () => { + process.env.AWS_PROFILE = "bedrock-profile"; + const baseModel = getModel("amazon-bedrock", "us.anthropic.claude-opus-4-8"); + const model: Model<"bedrock-converse-stream"> = { + ...baseModel, + id: "arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/example", + }; + + const config = await captureClientConfig(model); + + expect(config.profile).toBe("bedrock-profile"); + expect(config.token).toBeUndefined(); + expect(config.authSchemePreference).toBeUndefined(); + }); + it("uses the generic API key option as a Bedrock bearer token", async () => { const model = getModel("amazon-bedrock", "us.anthropic.claude-opus-4-8"); @@ -190,13 +205,4 @@ describe("bedrock endpoint resolution", () => { expect(config.token).toEqual({ token: "bedrock-api-key" }); expect(config.authSchemePreference).toEqual(["httpBearerAuth"]); }); - - it("does not use the ambient AWS auth marker as a bearer token", async () => { - const model = getModel("amazon-bedrock", "us.anthropic.claude-opus-4-8"); - - const config = await captureClientConfig(model, { apiKey: "" }); - - expect(config.token).toBeUndefined(); - expect(config.authSchemePreference).toBeUndefined(); - }); });