Skip to content

Commit 066700b

Browse files
committed
refactor(anthropic): share Foundry bearer auth policy
1 parent 470a0f8 commit 066700b

3 files changed

Lines changed: 46 additions & 66 deletions

File tree

src/agents/anthropic-transport-stream.ts

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import type {
2121
ThinkingLevel,
2222
} from "../llm/types.js";
2323
import { parseStreamingJson } from "../llm/utils/json-parse.js";
24+
import {
25+
omitFoundryBearerCredentialHeaders,
26+
usesFoundryBearerAuth,
27+
} from "../shared/anthropic-auth-headers.js";
2428
import {
2529
resolveClaudeNativeThinkingLevelMap,
2630
requiresClaudeAdaptiveThinking,
@@ -256,39 +260,6 @@ function isAnthropicOAuthToken(apiKey: string): boolean {
256260
return apiKey.includes("sk-ant-oat");
257261
}
258262

259-
function hasBearerAuthorizationHeader(headers?: Record<string, string>): boolean {
260-
if (!headers) {
261-
return false;
262-
}
263-
return Object.entries(headers).some(
264-
([key, value]) => key.toLowerCase() === "authorization" && /^bearer\s+\S+/i.test(value.trim()),
265-
);
266-
}
267-
268-
function usesFoundryBearerAuth(model: AnthropicTransportModel): boolean {
269-
return (
270-
model.provider === "microsoft-foundry" &&
271-
(model.authHeader === true || hasBearerAuthorizationHeader(model.headers))
272-
);
273-
}
274-
275-
function omitFoundryBearerCredentialHeaders(
276-
headers?: Record<string, string>,
277-
): Record<string, string> | undefined {
278-
if (!headers) {
279-
return undefined;
280-
}
281-
const next: Record<string, string> = {};
282-
for (const [key, value] of Object.entries(headers)) {
283-
const lower = key.toLowerCase();
284-
if (lower === "authorization" || lower === "x-api-key" || lower === "api-key") {
285-
continue;
286-
}
287-
next[key] = value;
288-
}
289-
return Object.keys(next).length > 0 ? next : undefined;
290-
}
291-
292263
function isDirectAnthropicModel(model: Pick<AnthropicTransportModel, "provider" | "baseUrl">) {
293264
if (normalizeLowercaseStringOrEmpty(model.provider) !== "anthropic") {
294265
return false;

src/llm/providers/anthropic.ts

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import {
1919
splitSystemPromptCacheBoundary,
2020
stripSystemPromptCacheBoundary,
2121
} from "../../agents/system-prompt-cache-boundary.js";
22+
import {
23+
omitFoundryBearerCredentialHeaders,
24+
usesFoundryBearerAuth,
25+
} from "../../shared/anthropic-auth-headers.js";
2226
import {
2327
resolveClaudeNativeThinkingLevelMap,
2428
requiresClaudeAdaptiveThinking,
@@ -246,39 +250,6 @@ function mergeHeaders(
246250
return merged;
247251
}
248252

249-
function hasBearerAuthorizationHeader(headers?: Record<string, string>): boolean {
250-
if (!headers) {
251-
return false;
252-
}
253-
return Object.entries(headers).some(
254-
([key, value]) => key.toLowerCase() === "authorization" && /^bearer\s+\S+/i.test(value.trim()),
255-
);
256-
}
257-
258-
function usesFoundryBearerAuth(model: Model<"anthropic-messages">): boolean {
259-
return (
260-
model.provider === "microsoft-foundry" &&
261-
(model.authHeader === true || hasBearerAuthorizationHeader(model.headers))
262-
);
263-
}
264-
265-
function omitFoundryBearerCredentialHeaders(
266-
headers?: Record<string, string>,
267-
): Record<string, string> | undefined {
268-
if (!headers) {
269-
return undefined;
270-
}
271-
const next: Record<string, string> = {};
272-
for (const [key, value] of Object.entries(headers)) {
273-
const lower = key.toLowerCase();
274-
if (lower === "authorization" || lower === "x-api-key" || lower === "api-key") {
275-
continue;
276-
}
277-
next[key] = value;
278-
}
279-
return Object.keys(next).length > 0 ? next : undefined;
280-
}
281-
282253
interface ServerSentEvent {
283254
event: string | null;
284255
data: string;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
type AnthropicAuthModel = {
2+
provider?: string;
3+
authHeader?: boolean;
4+
headers?: Record<string, string>;
5+
};
6+
7+
export function usesFoundryBearerAuth(model: AnthropicAuthModel): boolean {
8+
return (
9+
model.provider === "microsoft-foundry" &&
10+
(model.authHeader === true || hasBearerAuthorizationHeader(model.headers))
11+
);
12+
}
13+
14+
function hasBearerAuthorizationHeader(headers?: Record<string, string>): boolean {
15+
if (!headers) {
16+
return false;
17+
}
18+
return Object.entries(headers).some(
19+
([key, value]) => key.toLowerCase() === "authorization" && /^bearer\s+\S+/i.test(value.trim()),
20+
);
21+
}
22+
23+
export function omitFoundryBearerCredentialHeaders(
24+
headers?: Record<string, string>,
25+
): Record<string, string> | undefined {
26+
if (!headers) {
27+
return undefined;
28+
}
29+
const next: Record<string, string> = {};
30+
for (const [key, value] of Object.entries(headers)) {
31+
const lower = key.toLowerCase();
32+
if (lower === "authorization" || lower === "x-api-key" || lower === "api-key") {
33+
continue;
34+
}
35+
next[key] = value;
36+
}
37+
return Object.keys(next).length > 0 ? next : undefined;
38+
}

0 commit comments

Comments
 (0)