fix(anthropic): stop migrating current claude-haiku-4-5 to sonnet (#87719)

Summary:
- The branch preserves current Claude Haiku 4.5 refs in the Anthropic resolver and doctor migration, repoints the bare `haiku` family alias to `claude-haiku-4-5`, and updates regression tests.
- PR surface: Source +5, Tests +21. Total +26 across 4 files.
- Reproducibility: yes. Current main source maps the bare `haiku` alias and explicit Haiku 4.5 migration path  ... de-sonnet-4-6`; the PR body also supplies before/after terminal proof for the resolver and migration tests.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head 64429e23b3.
- Required merge gates passed before the squash merge.

Prepared head SHA: 64429e23b3
Review: https://github.com/openclaw/openclaw/pull/87719#issuecomment-4566419633

Co-authored-by: alkor2000 <200923177@qq.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
This commit is contained in:
alkor2000
2026-05-29 02:50:36 +08:00
committed by GitHub
parent 0786f586af
commit b3db1dba85
4 changed files with 37 additions and 11 deletions

View File

@@ -4,7 +4,7 @@ import { CLAUDE_CLI_BACKEND_ID, CLAUDE_CLI_MODEL_ALIASES } from "./cli-constants
const DEFAULT_CLAUDE_MODEL_BY_FAMILY: Record<string, string> = {
opus: "claude-opus-4-7",
sonnet: "claude-sonnet-4-6",
haiku: "claude-sonnet-4-6",
haiku: "claude-haiku-4-5",
};
export type ClaudeCliAnthropicModelRefs = {
@@ -117,6 +117,10 @@ function upgradeOldClaudeModelId(normalized: string): string | null {
if (normalized.startsWith("claude-sonnet-4-6") || normalized.startsWith("claude-sonnet-4.6")) {
return null;
}
// claude-haiku-4-5 is a current production model and must not be migrated.
if (normalized.startsWith("claude-haiku-4-5") || normalized.startsWith("claude-haiku-4.5")) {
return null;
}
if (
normalized === "claude-opus-4" ||
hasAnyRetiredVersionPrefix(normalized, [
@@ -140,8 +144,6 @@ function upgradeOldClaudeModelId(normalized: string): string | null {
"claude-sonnet-4.1",
"claude-sonnet-4-0",
"claude-sonnet-4.0",
"claude-haiku-4-5",
"claude-haiku-4.5",
]) ||
/^claude-sonnet-4-20\d{6}/.test(normalized)
) {
@@ -172,7 +174,6 @@ function upgradeOldClaudeModelId(normalized: string): string | null {
normalized === "sonnet-3.7" ||
normalized === "sonnet-3.5" ||
normalized === "sonnet-3" ||
normalized === "haiku-4.5" ||
normalized === "haiku-3.5" ||
normalized === "haiku-3"
) {

View File

@@ -55,6 +55,28 @@ describe("anthropic Claude model refs", () => {
expect(resolveKnownAnthropicModelRef("anthropic/claude-sonnet-4-7")).toBe(
"anthropic/claude-sonnet-4-7",
);
expect(resolveKnownAnthropicModelRef("anthropic/claude-haiku-4-5")).toBe(
"anthropic/claude-haiku-4-5",
);
});
it("preserves the current claude-haiku-4-5 model and its bare alias", () => {
// claude-haiku-4-5 is a current production model (not retired), so neither
// its full ref, its dotted variant, nor the bare "haiku" family alias must
// be rewritten to sonnet.
expect(resolveKnownAnthropicModelRef("anthropic/claude-haiku-4-5")).toBe(
"anthropic/claude-haiku-4-5",
);
expect(resolveKnownAnthropicModelRef("anthropic/claude-haiku-4.5")).toBe(
"anthropic/claude-haiku-4.5",
);
expect(resolveKnownAnthropicModelRef("anthropic/claude-haiku-4-5@anthropic:work")).toBe(
"anthropic/claude-haiku-4-5@anthropic:work",
);
// Genuinely retired Claude 3 Haiku still upgrades to the current sonnet.
expect(resolveKnownAnthropicModelRef("anthropic/claude-3-5-haiku-20241022")).toBe(
"anthropic/claude-sonnet-4-6",
);
});
});

View File

@@ -1707,7 +1707,7 @@ describe("legacy model compat migrate", () => {
},
});
expect(res.config?.agents?.defaults?.imageModel).toBe("anthropic/claude-sonnet-4-6");
expect(res.config?.agents?.defaults?.imageModel).toBe("anthropic/claude-haiku-4-5");
expect(res.config?.agents?.defaults?.imageGenerationModel).toEqual({
primary: "github-copilot/claude-sonnet-4.6",
fallbacks: ["github-copilot/gpt-5.4-mini"],
@@ -1751,6 +1751,7 @@ describe("legacy model compat migrate", () => {
});
expect(res.config?.agents?.defaults?.workspace).toBe("/tmp/claude-3-sonnet");
expect(res.config?.agents?.defaults?.models).toEqual({
"anthropic/claude-haiku-4-5": { alias: "haiku" },
"anthropic/claude-sonnet-4-6": { alias: "current-sonnet" },
"github-copilot/claude-opus-4.7": { alias: "copilot-opus" },
"openai/gpt-5.5-pro": { alias: "old-pro" },
@@ -1770,10 +1771,9 @@ describe("legacy model compat migrate", () => {
subagent?: { allowedModels?: string[] };
}
)?.subagent?.allowedModels,
).toEqual(["anthropic/claude-sonnet-4-6", "*"]);
).toEqual(["anthropic/claude-haiku-4-5", "*"]);
expect(res.config?.channels?.modelByChannel?.telegram?.["*"]).toBe("anthropic/claude-opus-4-7");
expectMigrationChangesToIncludeFragments(res.changes, [
'config.agents.defaults.imageModel from "anthropic/claude-haiku-4-5" to "anthropic/claude-sonnet-4-6"',
'config.agents.defaults.imageGenerationModel.primary from "github-copilot/claude-sonnet-4" to "github-copilot/claude-sonnet-4.6"',
'config.agents.defaults.imageGenerationModel.fallbacks.0 from "github-copilot/grok-code-fast-1" to "github-copilot/gpt-5.4-mini"',
'config.agents.defaults.musicGenerationModel from "vercel-ai-gateway/anthropic/claude-opus-4-5" to "vercel-ai-gateway/anthropic/claude-opus-4-6"',
@@ -1800,7 +1800,6 @@ describe("legacy model compat migrate", () => {
'config.agents.defaults.models key from "openai/gpt-5.2-pro" to "openai/gpt-5.5-pro"',
'config.agents.defaults.models key from "github-copilot/gpt-5-mini" to "github-copilot/gpt-5.4-mini"',
'config.plugins.entries.lossless-claw.config.summaryModel from "anthropic/claude-3-5-sonnet" to "anthropic/claude-sonnet-4-6"',
'config.plugins.entries.lossless-claw.subagent.allowedModels.0 from "anthropic/claude-haiku-4-5" to "anthropic/claude-sonnet-4-6"',
'config.channels.modelByChannel.telegram.* from "anthropic/claude-opus-4-5" to "anthropic/claude-opus-4-7"',
]);
});

View File

@@ -635,6 +635,13 @@ function upgradeOldClaudeToken(
) {
return null;
}
// claude-haiku-4-5 is a current production model and must not be migrated.
if (
normalized.startsWith("claude-haiku-4-5") ||
normalized.startsWith("claude-haiku-4.5")
) {
return null;
}
if (
normalized === "claude-opus-4" ||
hasAnyRetiredVersionPrefix(normalized, [
@@ -658,8 +665,6 @@ function upgradeOldClaudeToken(
"claude-sonnet-4.1",
"claude-sonnet-4-0",
"claude-sonnet-4.0",
"claude-haiku-4-5",
"claude-haiku-4.5",
]) ||
/^claude-sonnet-4-20\d{6}/.test(normalized)
) {
@@ -714,7 +719,6 @@ function upgradeOldClaudeToken(
normalized === "sonnet-3.7" ||
normalized === "sonnet-3.5" ||
normalized === "sonnet-3" ||
normalized === "haiku-4.5" ||
normalized === "haiku-3.5" ||
normalized === "haiku-3"
) {