fix(anthropic): honor opus 5 aliases

This commit is contained in:
Vincent Koc
2026-07-25 19:22:19 +08:00
parent 85181e9db7
commit 4df130d0be
3 changed files with 38 additions and 10 deletions

View File

@@ -280,6 +280,14 @@ describe("anthropic stream wrappers", () => {
expect(captured.model?.cost.output).toBe(50);
});
it.each(["opus", "opus-5"])("uses native fast mode for the %s alias", (modelId) => {
const captured = runNativeFastModeWrapper({ modelId });
expect(captured.headers?.["anthropic-beta"]).toContain("fast-mode-2026-02-01");
expect(captured.payload).toEqual({ speed: "fast" });
expect(captured.model?.cost.output).toBe(50);
});
it("keeps standard Opus 5 payload and pricing when fast mode is disabled", () => {
const captured = runNativeFastModeWrapper({ enabled: false });

View File

@@ -99,9 +99,12 @@ export function resolveClaudeSonnet5ModelIdentity(ref: ClaudeModelRef): string |
return normalized.slice((match.index ?? 0) + (match[0].startsWith("-") ? 1 : 0));
}
/** Resolve Claude Opus 5 through direct ids, cloud ids, or deployment metadata. */
/** Resolve Claude Opus 5 through aliases, direct ids, cloud ids, or deployment metadata. */
export function resolveClaudeOpus5ModelIdentity(ref: ClaudeModelRef): string | undefined {
const normalized = resolveClaudeModelIdentity(ref);
if (normalized === "opus" || normalized === "opus-5") {
return "claude-opus-5";
}
const match = /(?:^|-)claude-opus-5(?=$|[^a-z0-9])/.exec(normalized);
if (!match) {
return undefined;
@@ -112,38 +115,51 @@ export function resolveClaudeOpus5ModelIdentity(ref: ClaudeModelRef): string | u
/** Return whether a Claude model supports adaptive thinking. */
export function supportsClaudeAdaptiveThinking(ref: ClaudeModelRef): boolean {
const modelId = resolveClaudeModelIdentity(ref);
return /(?:^|-)claude-(?:fable-5|mythos-(?:5|preview)|opus-(?:4-(?:6|7|8)|5)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
modelId,
return (
resolveClaudeOpus5ModelIdentity(ref) !== undefined ||
/(?:^|-)claude-(?:fable-5|mythos-(?:5|preview)|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
modelId,
)
);
}
/** Return whether a Claude model has a native 1M-token context window. */
export function supportsClaude1MContext(ref: ClaudeModelRef): boolean {
const modelId = resolveClaudeModelIdentity(ref);
return /(?:^|-)claude-(?:fable-5|mythos-(?:5|preview)|opus-(?:4-(?:6|7|8)|5)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
modelId,
return (
resolveClaudeOpus5ModelIdentity(ref) !== undefined ||
/(?:^|-)claude-(?:fable-5|mythos-(?:5|preview)|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
modelId,
)
);
}
/** Return whether a Claude model supports Anthropic's native fast mode. */
export function supportsClaudeFastMode(ref: ClaudeModelRef): boolean {
const modelId = resolveClaudeModelIdentity(ref);
return /(?:^|-)claude-opus-(?:4-8|5)(?=$|[^a-z0-9])/.test(modelId);
return (
resolveClaudeOpus5ModelIdentity(ref) !== undefined ||
/(?:^|-)claude-opus-4-8(?=$|[^a-z0-9])/.test(modelId)
);
}
/** Return whether a Claude model supports native max effort. */
export function supportsClaudeNativeMaxEffort(ref: ClaudeModelRef): boolean {
const modelId = resolveClaudeModelIdentity(ref);
return /(?:^|-)claude-(?:fable-5|mythos-5|opus-(?:4-(?:6|7|8)|5)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
modelId,
return (
resolveClaudeOpus5ModelIdentity(ref) !== undefined ||
/(?:^|-)claude-(?:fable-5|mythos-5|opus-4-(?:6|7|8)|sonnet-(?:5|4-6))(?=$|[^a-z0-9])/.test(
modelId,
)
);
}
/** Return whether a Claude model supports native xhigh effort. */
export function supportsClaudeNativeXhighEffort(ref: ClaudeModelRef): boolean {
const modelId = resolveClaudeModelIdentity(ref);
return /(?:^|-)claude-(?:fable-5|mythos-5|opus-(?:4-(?:7|8)|5)|sonnet-5)(?=$|[^a-z0-9])/.test(
modelId,
return (
resolveClaudeOpus5ModelIdentity(ref) !== undefined ||
/(?:^|-)claude-(?:fable-5|mythos-5|opus-4-(?:7|8)|sonnet-5)(?=$|[^a-z0-9])/.test(modelId)
);
}

View File

@@ -75,6 +75,8 @@ describe("Claude model contracts", () => {
it.each([
["Anthropic API", { id: "claude-opus-5" }, "claude-opus-5"],
["Anthropic alias", { id: "opus" }, "claude-opus-5"],
["Anthropic version alias", { id: "opus-5" }, "claude-opus-5"],
["Claude CLI", { id: "claude-opus-5" }, "claude-opus-5"],
["Vertex AI", { id: "claude-opus-5@20260701" }, "claude-opus-5@20260701"],
["Amazon Bedrock", { id: "global.anthropic.claude-opus-5" }, "claude-opus-5"],
@@ -108,6 +110,8 @@ describe("Claude model contracts", () => {
it("recognizes native fast-mode Claude models", () => {
expect(supportsClaudeFastMode({ id: "claude-opus-5" })).toBe(true);
expect(supportsClaudeFastMode({ id: "opus" })).toBe(true);
expect(supportsClaudeFastMode({ id: "opus-5" })).toBe(true);
expect(supportsClaudeFastMode({ id: "global.anthropic.claude-opus-5" })).toBe(true);
expect(supportsClaudeFastMode({ id: "claude-opus-4.8" })).toBe(true);
expect(supportsClaudeFastMode({ id: "claude-opus-4-7" })).toBe(false);