diff --git a/packages/llm-core/src/types.ts b/packages/llm-core/src/types.ts index 127bbc5cb8d2..27338354de0d 100644 --- a/packages/llm-core/src/types.ts +++ b/packages/llm-core/src/types.ts @@ -606,6 +606,8 @@ export interface Model { /** Provider-specific request/runtime parameters passed through to provider plugins. */ params?: Record; headers?: Record; + /** Sends runtime credentials as Authorization: Bearer instead of provider-specific key headers. */ + authHeader?: boolean; /** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */ compat?: TApi extends "openai-completions" ? OpenAICompletionsCompat diff --git a/src/agents/embedded-agent-runner/model.ts b/src/agents/embedded-agent-runner/model.ts index ac480ffdbbf6..ae31fda666c3 100644 --- a/src/agents/embedded-agent-runner/model.ts +++ b/src/agents/embedded-agent-runner/model.ts @@ -669,6 +669,7 @@ function applyConfiguredProviderOverrides(params: { ...(resolvedParams ? { params: resolvedParams } : {}), ...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}), headers: passthroughRequestConfig.headers, + ...(providerConfig.authHeader !== undefined ? { authHeader: providerConfig.authHeader } : {}), }; } const resolvedParams = mergeModelParams( @@ -760,6 +761,9 @@ function applyConfiguredProviderOverrides(params: { ...(resolvedParams ? { params: resolvedParams } : {}), ...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}), headers: requestConfig.headers, + ...(providerConfig.authHeader !== undefined + ? { authHeader: providerConfig.authHeader } + : {}), compat: resolvedCompat, mediaInput: mergeModelMediaInput( discoveredModel.mediaInput, @@ -1143,6 +1147,9 @@ function resolveConfiguredFallbackModel(params: { ...(resolvedParams ? { params: resolvedParams } : {}), ...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}), headers: requestConfig.headers, + ...(providerConfig?.authHeader !== undefined + ? { authHeader: providerConfig.authHeader } + : {}), compat: fallbackCompat, mediaInput: fallbackMediaInput, } as Model, diff --git a/src/llm/providers/anthropic.test.ts b/src/llm/providers/anthropic.test.ts index bac3328cdb22..d3020833815e 100644 --- a/src/llm/providers/anthropic.test.ts +++ b/src/llm/providers/anthropic.test.ts @@ -87,8 +87,8 @@ describe("Anthropic provider", () => { const model = makeAnthropicModel({ provider: "microsoft-foundry", baseUrl: "https://example.services.ai.azure.com/anthropic", + authHeader: true, headers: { - Authorization: "Bearer entra-access-token", "x-api-key": "stale-resource-key", }, }); diff --git a/src/llm/providers/anthropic.ts b/src/llm/providers/anthropic.ts index 0688f40eac7e..fd372410be66 100644 --- a/src/llm/providers/anthropic.ts +++ b/src/llm/providers/anthropic.ts @@ -266,6 +266,13 @@ function hasBearerAuthorizationHeader(headers?: Record): boolean ); } +function usesFoundryBearerAuth(model: Model<"anthropic-messages">): boolean { + return ( + model.provider === "microsoft-foundry" && + (model.authHeader === true || hasBearerAuthorizationHeader(model.headers)) + ); +} + function omitFoundryBearerCredentialHeaders( headers?: Record, ): Record | undefined { @@ -988,7 +995,7 @@ function createClient( return { client, isOAuthToken: false }; } - if (model.provider === "microsoft-foundry" && hasBearerAuthorizationHeader(model.headers)) { + if (usesFoundryBearerAuth(model)) { const client = new Anthropic({ apiKey: null, authToken: apiKey,