

























@@ -4,6 +4,7 @@ import { CLAUDE_CLI_BACKEND_ID, CLAUDE_CLI_MODEL_ALIASES } from "./cli-constants
44const DEFAULT_CLAUDE_MODEL_BY_FAMILY: Record<string, string> = {
55opus: "claude-opus-4-7",
66sonnet: "claude-sonnet-4-6",
7+haiku: "claude-sonnet-4-6",
78};
89910export type ClaudeCliAnthropicModelRefs = {
@@ -12,6 +13,47 @@ export type ClaudeCliAnthropicModelRefs = {
1213rewriteRef?: string;
1314};
141516+function splitTrailingModelAuthProfile(raw: string): { model: string; profile?: string } {
17+const trimmed = raw.trim();
18+if (!trimmed) {
19+return { model: "" };
20+}
21+const lastSlash = trimmed.lastIndexOf("/");
22+let delimiter = trimmed.indexOf("@", lastSlash + 1);
23+if (delimiter <= 0) {
24+return { model: trimmed };
25+}
26+if (/^\d{8}(?:@|$)/.test(trimmed.slice(delimiter + 1))) {
27+const nextDelimiter = trimmed.indexOf("@", delimiter + 9);
28+if (nextDelimiter < 0) {
29+return { model: trimmed };
30+}
31+delimiter = nextDelimiter;
32+}
33+const model = trimmed.slice(0, delimiter).trim();
34+const profile = trimmed.slice(delimiter + 1).trim();
35+return model && profile ? { model, profile } : { model: trimmed };
36+}
37+38+function attachModelAuthProfile(model: string, profile?: string): string {
39+return profile ? `${model}@${profile}` : model;
40+}
41+42+function hasRetiredVersionPrefix(normalized: string, prefix: string): boolean {
43+if (normalized === prefix) {
44+return true;
45+}
46+if (!normalized.startsWith(prefix)) {
47+return false;
48+}
49+const next = normalized[prefix.length];
50+return next === "-" || next === "." || next === ":" || next === "@";
51+}
52+53+function hasAnyRetiredVersionPrefix(normalized: string, prefixes: readonly string[]): boolean {
54+return prefixes.some((prefix) => hasRetiredVersionPrefix(normalized, prefix));
55+}
56+1557function parseProviderModelRef(
1658raw: string,
1759defaultProvider: string,
@@ -37,17 +79,22 @@ function parseProviderModelRef(
3779}
38803981function canonicalizeKnownClaudeCliModelId(modelId: string): string | null {
40-const trimmed = modelId.trim();
82+const split = splitTrailingModelAuthProfile(modelId);
83+const trimmed = split.model.trim();
4184const normalized = normalizeLowercaseStringOrEmpty(trimmed);
4285if (!normalized) {
4386return null;
4487}
88+const upgraded = upgradeOldClaudeModelId(normalized);
89+if (upgraded) {
90+return attachModelAuthProfile(upgraded, split.profile);
91+}
4592if (normalized.startsWith("claude-")) {
46-return trimmed;
93+return attachModelAuthProfile(trimmed, split.profile);
4794}
4895const defaultModel = DEFAULT_CLAUDE_MODEL_BY_FAMILY[normalized];
4996if (defaultModel) {
50-return defaultModel;
97+return attachModelAuthProfile(defaultModel, split.profile);
5198}
5299const family = CLAUDE_CLI_MODEL_ALIASES[normalized];
53100if (!family) {
@@ -57,7 +104,81 @@ function canonicalizeKnownClaudeCliModelId(modelId: string): string | null {
57104if (!version || version === normalized) {
58105return null;
59106}
60-return `claude-${family}-${version.replaceAll(".", "-")}`;
107+return attachModelAuthProfile(`claude-${family}-${version.replaceAll(".", "-")}`, split.profile);
108+}
109+110+function upgradeOldClaudeModelId(normalized: string): string | null {
111+if (normalized.startsWith("claude-opus-4-7") || normalized.startsWith("claude-opus-4.7")) {
112+return null;
113+}
114+if (normalized.startsWith("claude-opus-4-6") || normalized.startsWith("claude-opus-4.6")) {
115+return null;
116+}
117+if (normalized.startsWith("claude-sonnet-4-6") || normalized.startsWith("claude-sonnet-4.6")) {
118+return null;
119+}
120+if (
121+normalized === "claude-opus-4" ||
122+hasAnyRetiredVersionPrefix(normalized, [
123+"claude-opus-4-5",
124+"claude-opus-4.5",
125+"claude-opus-4-1",
126+"claude-opus-4.1",
127+"claude-opus-4-0",
128+"claude-opus-4.0",
129+]) ||
130+/^claude-opus-4-20\d{6}/.test(normalized)
131+) {
132+return "claude-opus-4-7";
133+}
134+if (
135+normalized === "claude-sonnet-4" ||
136+hasAnyRetiredVersionPrefix(normalized, [
137+"claude-sonnet-4-5",
138+"claude-sonnet-4.5",
139+"claude-sonnet-4-1",
140+"claude-sonnet-4.1",
141+"claude-sonnet-4-0",
142+"claude-sonnet-4.0",
143+"claude-haiku-4-5",
144+"claude-haiku-4.5",
145+]) ||
146+/^claude-sonnet-4-20\d{6}/.test(normalized)
147+) {
148+return "claude-sonnet-4-6";
149+}
150+if (normalized.startsWith("claude-3") && normalized.includes("opus")) {
151+return "claude-opus-4-7";
152+}
153+if (
154+normalized.startsWith("claude-3") &&
155+(normalized.includes("sonnet") || normalized.includes("haiku"))
156+) {
157+return "claude-sonnet-4-6";
158+}
159+if (
160+normalized === "opus-4.5" ||
161+normalized === "opus-4.1" ||
162+normalized === "opus-4" ||
163+normalized === "opus-3"
164+) {
165+return "claude-opus-4-7";
166+}
167+if (
168+normalized === "sonnet-4.5" ||
169+normalized === "sonnet-4.1" ||
170+normalized === "sonnet-4.0" ||
171+normalized === "sonnet-4" ||
172+normalized === "sonnet-3.7" ||
173+normalized === "sonnet-3.5" ||
174+normalized === "sonnet-3" ||
175+normalized === "haiku-4.5" ||
176+normalized === "haiku-3.5" ||
177+normalized === "haiku-3"
178+) {
179+return "claude-sonnet-4-6";
180+}
181+return null;
61182}
6218363184export function resolveClaudeCliAnthropicModelRefs(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。