mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:10:44 +00:00
feat: default Anthropic to Opus 4.7
This commit is contained in:
@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Changes
|
||||
|
||||
- Google/TTS: add Gemini text-to-speech support to the bundled `google` plugin, including provider registration, voice selection, WAV reply output, PCM telephony output, and setup/docs guidance. (#67515) Thanks @barronlroth.
|
||||
- Anthropic/models: default Anthropic selections, `opus` aliases, Claude CLI defaults, and bundled image understanding to Claude Opus 4.7.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
||||
@@ -101,11 +101,11 @@ describe("anthropic cli migration", () => {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "anthropic/claude-sonnet-4-6",
|
||||
primary: "anthropic/claude-opus-4-7",
|
||||
fallbacks: ["anthropic/claude-opus-4-6", "openai/gpt-5.2"],
|
||||
},
|
||||
models: {
|
||||
"anthropic/claude-sonnet-4-6": { alias: "Sonnet" },
|
||||
"anthropic/claude-opus-4-7": { alias: "Opus" },
|
||||
"anthropic/claude-opus-4-6": { alias: "Opus" },
|
||||
"openai/gpt-5.2": {},
|
||||
},
|
||||
@@ -114,16 +114,17 @@ describe("anthropic cli migration", () => {
|
||||
});
|
||||
|
||||
expect(result.profiles).toEqual([]);
|
||||
expect(result.defaultModel).toBe("claude-cli/claude-sonnet-4-6");
|
||||
expect(result.defaultModel).toBe("claude-cli/claude-opus-4-7");
|
||||
expect(result.configPatch).toEqual({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "claude-cli/claude-sonnet-4-6",
|
||||
primary: "claude-cli/claude-opus-4-7",
|
||||
fallbacks: ["claude-cli/claude-opus-4-6", "openai/gpt-5.2"],
|
||||
},
|
||||
models: {
|
||||
"claude-cli/claude-sonnet-4-6": { alias: "Sonnet" },
|
||||
"claude-cli/claude-opus-4-7": { alias: "Opus" },
|
||||
"claude-cli/claude-sonnet-4-6": {},
|
||||
"claude-cli/claude-opus-4-6": { alias: "Opus" },
|
||||
"claude-cli/claude-opus-4-5": {},
|
||||
"claude-cli/claude-sonnet-4-5": {},
|
||||
@@ -147,12 +148,13 @@ describe("anthropic cli migration", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.defaultModel).toBe("claude-cli/claude-sonnet-4-6");
|
||||
expect(result.defaultModel).toBe("claude-cli/claude-opus-4-7");
|
||||
expect(result.configPatch).toEqual({
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-5.2": {},
|
||||
"claude-cli/claude-opus-4-7": {},
|
||||
"claude-cli/claude-sonnet-4-6": {},
|
||||
"claude-cli/claude-opus-4-6": {},
|
||||
"claude-cli/claude-opus-4-5": {},
|
||||
@@ -168,9 +170,9 @@ describe("anthropic cli migration", () => {
|
||||
const result = buildAnthropicCliMigrationResult({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "claude-cli/claude-sonnet-4-6" },
|
||||
model: { primary: "claude-cli/claude-opus-4-7" },
|
||||
models: {
|
||||
"claude-cli/claude-sonnet-4-6": {},
|
||||
"claude-cli/claude-opus-4-7": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -180,6 +182,7 @@ describe("anthropic cli migration", () => {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"claude-cli/claude-opus-4-7": {},
|
||||
"claude-cli/claude-sonnet-4-6": {},
|
||||
"claude-cli/claude-opus-4-6": {},
|
||||
"claude-cli/claude-opus-4-5": {},
|
||||
@@ -217,11 +220,11 @@ describe("anthropic cli migration", () => {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "anthropic/claude-sonnet-4-6",
|
||||
primary: "anthropic/claude-opus-4-7",
|
||||
fallbacks: ["anthropic/claude-opus-4-6", "openai/gpt-5.2"],
|
||||
},
|
||||
models: {
|
||||
"anthropic/claude-sonnet-4-6": { alias: "Sonnet" },
|
||||
"anthropic/claude-opus-4-7": { alias: "Opus" },
|
||||
"anthropic/claude-opus-4-6": { alias: "Opus" },
|
||||
"openai/gpt-5.2": {},
|
||||
},
|
||||
@@ -297,11 +300,11 @@ describe("anthropic cli migration", () => {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "anthropic/claude-sonnet-4-6",
|
||||
primary: "anthropic/claude-opus-4-7",
|
||||
fallbacks: ["anthropic/claude-opus-4-6", "openai/gpt-5.2"],
|
||||
},
|
||||
models: {
|
||||
"anthropic/claude-sonnet-4-6": { alias: "Sonnet" },
|
||||
"anthropic/claude-opus-4-7": { alias: "Opus" },
|
||||
"anthropic/claude-opus-4-6": { alias: "Opus" },
|
||||
"openai/gpt-5.2": {},
|
||||
},
|
||||
@@ -315,11 +318,11 @@ describe("anthropic cli migration", () => {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "claude-cli/claude-sonnet-4-6",
|
||||
primary: "claude-cli/claude-opus-4-7",
|
||||
fallbacks: ["claude-cli/claude-opus-4-6", "openai/gpt-5.2"],
|
||||
},
|
||||
models: {
|
||||
"claude-cli/claude-sonnet-4-6": { alias: "Sonnet" },
|
||||
"claude-cli/claude-opus-4-7": { alias: "Opus" },
|
||||
"claude-cli/claude-opus-4-6": { alias: "Opus" },
|
||||
"openai/gpt-5.2": {},
|
||||
},
|
||||
|
||||
@@ -2,9 +2,10 @@ import type { CliBackendConfig } from "openclaw/plugin-sdk/cli-backend";
|
||||
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
|
||||
|
||||
export const CLAUDE_CLI_BACKEND_ID = "claude-cli";
|
||||
export const CLAUDE_CLI_DEFAULT_MODEL_REF = `${CLAUDE_CLI_BACKEND_ID}/claude-sonnet-4-6`;
|
||||
export const CLAUDE_CLI_DEFAULT_MODEL_REF = `${CLAUDE_CLI_BACKEND_ID}/claude-opus-4-7`;
|
||||
export const CLAUDE_CLI_DEFAULT_ALLOWLIST_REFS = [
|
||||
CLAUDE_CLI_DEFAULT_MODEL_REF,
|
||||
`${CLAUDE_CLI_BACKEND_ID}/claude-sonnet-4-6`,
|
||||
`${CLAUDE_CLI_BACKEND_ID}/claude-opus-4-6`,
|
||||
`${CLAUDE_CLI_BACKEND_ID}/claude-opus-4-5`,
|
||||
`${CLAUDE_CLI_BACKEND_ID}/claude-sonnet-4-5`,
|
||||
@@ -13,9 +14,11 @@ export const CLAUDE_CLI_DEFAULT_ALLOWLIST_REFS = [
|
||||
|
||||
export const CLAUDE_CLI_MODEL_ALIASES: Record<string, string> = {
|
||||
opus: "opus",
|
||||
"opus-4.7": "opus",
|
||||
"opus-4.6": "opus",
|
||||
"opus-4.5": "opus",
|
||||
"opus-4": "opus",
|
||||
"claude-opus-4-7": "opus",
|
||||
"claude-opus-4-6": "opus",
|
||||
"claude-opus-4-5": "opus",
|
||||
"claude-opus-4": "opus",
|
||||
|
||||
@@ -87,7 +87,7 @@ function resolveAnthropicPrimaryModelRef(raw?: string): string | null {
|
||||
}
|
||||
const aliasKey = normalizeLowercaseStringOrEmpty(trimmed);
|
||||
if (aliasKey === "opus") {
|
||||
return "anthropic/claude-opus-4-6";
|
||||
return "anthropic/claude-opus-4-7";
|
||||
}
|
||||
if (aliasKey === "sonnet") {
|
||||
return "anthropic/claude-sonnet-4-6";
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import type {
|
||||
ProviderResolveDynamicModelContext,
|
||||
ProviderRuntimeModel,
|
||||
} from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { capturePluginRegistration } from "openclaw/plugin-sdk/testing";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
|
||||
@@ -18,6 +22,19 @@ vi.mock("./cli-auth-seam.js", () => {
|
||||
|
||||
import anthropicPlugin from "./index.js";
|
||||
|
||||
function createModelRegistry(models: ProviderRuntimeModel[]) {
|
||||
return {
|
||||
find(providerId: string, modelId: string) {
|
||||
return (
|
||||
models.find(
|
||||
(model) =>
|
||||
model.provider === providerId && model.id.toLowerCase() === modelId.toLowerCase(),
|
||||
) ?? null
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("anthropic provider replay hooks", () => {
|
||||
it("registers the claude-cli backend", async () => {
|
||||
const captured = capturePluginRegistration({ register: anthropicPlugin.register });
|
||||
@@ -129,9 +146,9 @@ describe("anthropic provider replay hooks", () => {
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "claude-cli/claude-sonnet-4-6" },
|
||||
model: { primary: "claude-cli/claude-opus-4-7" },
|
||||
models: {
|
||||
"claude-cli/claude-sonnet-4-6": {},
|
||||
"claude-cli/claude-opus-4-7": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -142,6 +159,7 @@ describe("anthropic provider replay hooks", () => {
|
||||
every: "1h",
|
||||
});
|
||||
expect(next?.agents?.defaults?.models).toMatchObject({
|
||||
"claude-cli/claude-opus-4-7": {},
|
||||
"claude-cli/claude-sonnet-4-6": {},
|
||||
"claude-cli/claude-opus-4-6": {},
|
||||
"claude-cli/claude-opus-4-5": {},
|
||||
@@ -150,6 +168,40 @@ describe("anthropic provider replay hooks", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves explicit claude-opus-4-7 refs from the 4.6 template family", async () => {
|
||||
const provider = await registerSingleProviderPlugin(anthropicPlugin);
|
||||
const resolved = provider.resolveDynamicModel?.({
|
||||
provider: "anthropic",
|
||||
modelId: "claude-opus-4-7",
|
||||
modelRegistry: createModelRegistry([
|
||||
{
|
||||
id: "claude-opus-4-6",
|
||||
name: "Claude Opus 4.6",
|
||||
provider: "anthropic",
|
||||
api: "anthropic-messages",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 200_000,
|
||||
maxTokens: 32_000,
|
||||
} as ProviderRuntimeModel,
|
||||
]),
|
||||
} as ProviderResolveDynamicModelContext);
|
||||
|
||||
expect(resolved).toMatchObject({
|
||||
provider: "anthropic",
|
||||
id: "claude-opus-4-7",
|
||||
api: "anthropic-messages",
|
||||
reasoning: true,
|
||||
});
|
||||
expect(
|
||||
provider.resolveDefaultThinkingLevel?.({
|
||||
provider: "anthropic",
|
||||
modelId: "claude-opus-4-7",
|
||||
} as never),
|
||||
).toBe("adaptive");
|
||||
});
|
||||
|
||||
it("resolves claude-cli synthetic oauth auth", async () => {
|
||||
readClaudeCliCredentialsForRuntimeMock.mockReset();
|
||||
readClaudeCliCredentialsForRuntimeMock.mockReturnValue({
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
export const anthropicMediaUnderstandingProvider: MediaUnderstandingProvider = {
|
||||
id: "anthropic",
|
||||
capabilities: ["image"],
|
||||
defaultModels: { image: "claude-opus-4-6" },
|
||||
defaultModels: { image: "claude-opus-4-7" },
|
||||
autoPriority: { image: 20 },
|
||||
nativeDocumentInputs: ["pdf"],
|
||||
describeImage: describeImageWithModel,
|
||||
|
||||
@@ -38,14 +38,23 @@ import { buildAnthropicReplayPolicy } from "./replay-policy.js";
|
||||
import { wrapAnthropicProviderStream } from "./stream-wrappers.js";
|
||||
|
||||
const PROVIDER_ID = "anthropic";
|
||||
const DEFAULT_ANTHROPIC_MODEL = "anthropic/claude-sonnet-4-6";
|
||||
const DEFAULT_ANTHROPIC_MODEL = "anthropic/claude-opus-4-7";
|
||||
const ANTHROPIC_OPUS_47_MODEL_ID = "claude-opus-4-7";
|
||||
const ANTHROPIC_OPUS_47_DOT_MODEL_ID = "claude-opus-4.7";
|
||||
const ANTHROPIC_OPUS_46_MODEL_ID = "claude-opus-4-6";
|
||||
const ANTHROPIC_OPUS_46_DOT_MODEL_ID = "claude-opus-4.6";
|
||||
const ANTHROPIC_OPUS_47_TEMPLATE_MODEL_IDS = [
|
||||
ANTHROPIC_OPUS_46_MODEL_ID,
|
||||
ANTHROPIC_OPUS_46_DOT_MODEL_ID,
|
||||
"claude-opus-4-5",
|
||||
"claude-opus-4.5",
|
||||
] as const;
|
||||
const ANTHROPIC_OPUS_TEMPLATE_MODEL_IDS = ["claude-opus-4-5", "claude-opus-4.5"] as const;
|
||||
const ANTHROPIC_SONNET_46_MODEL_ID = "claude-sonnet-4-6";
|
||||
const ANTHROPIC_SONNET_46_DOT_MODEL_ID = "claude-sonnet-4.6";
|
||||
const ANTHROPIC_SONNET_TEMPLATE_MODEL_IDS = ["claude-sonnet-4-5", "claude-sonnet-4.5"] as const;
|
||||
const ANTHROPIC_MODERN_MODEL_PREFIXES = [
|
||||
"claude-opus-4-7",
|
||||
"claude-opus-4-6",
|
||||
"claude-sonnet-4-6",
|
||||
"claude-opus-4-5",
|
||||
@@ -221,6 +230,14 @@ function resolveAnthropicForwardCompatModel(
|
||||
ctx: ProviderResolveDynamicModelContext,
|
||||
): ProviderRuntimeModel | undefined {
|
||||
return (
|
||||
resolveAnthropic46ForwardCompatModel({
|
||||
ctx,
|
||||
dashModelId: ANTHROPIC_OPUS_47_MODEL_ID,
|
||||
dotModelId: ANTHROPIC_OPUS_47_DOT_MODEL_ID,
|
||||
dashTemplateId: ANTHROPIC_OPUS_46_MODEL_ID,
|
||||
dotTemplateId: ANTHROPIC_OPUS_46_DOT_MODEL_ID,
|
||||
fallbackTemplateIds: ANTHROPIC_OPUS_47_TEMPLATE_MODEL_IDS,
|
||||
}) ??
|
||||
resolveAnthropic46ForwardCompatModel({
|
||||
ctx,
|
||||
dashModelId: ANTHROPIC_OPUS_46_MODEL_ID,
|
||||
@@ -243,6 +260,8 @@ function resolveAnthropicForwardCompatModel(
|
||||
function shouldUseAnthropicAdaptiveThinkingDefault(modelId: string): boolean {
|
||||
const lowerModelId = normalizeLowercaseStringOrEmpty(modelId);
|
||||
return (
|
||||
lowerModelId.startsWith(ANTHROPIC_OPUS_47_MODEL_ID) ||
|
||||
lowerModelId.startsWith(ANTHROPIC_OPUS_47_DOT_MODEL_ID) ||
|
||||
lowerModelId.startsWith(ANTHROPIC_OPUS_46_MODEL_ID) ||
|
||||
lowerModelId.startsWith(ANTHROPIC_OPUS_46_DOT_MODEL_ID) ||
|
||||
lowerModelId.startsWith(ANTHROPIC_SONNET_46_MODEL_ID) ||
|
||||
@@ -372,7 +391,7 @@ async function runAnthropicCliMigrationNonInteractive(ctx: {
|
||||
|
||||
export function registerAnthropicPlugin(api: OpenClawPluginApi): void {
|
||||
const providerId = "anthropic";
|
||||
const defaultAnthropicModel = "anthropic/claude-sonnet-4-6";
|
||||
const defaultAnthropicModel = DEFAULT_ANTHROPIC_MODEL;
|
||||
api.registerCliBackend(buildAnthropicCliBackend());
|
||||
api.registerProvider({
|
||||
id: providerId,
|
||||
|
||||
@@ -8,6 +8,7 @@ export type ModelRef = {
|
||||
};
|
||||
|
||||
const HIGH_SIGNAL_LIVE_MODEL_PRIORITY = [
|
||||
"anthropic/claude-opus-4-7",
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-sonnet-4-6",
|
||||
"google/gemini-3.1-pro-preview",
|
||||
|
||||
@@ -485,6 +485,7 @@ describe("isHighSignalLiveModelRef", () => {
|
||||
describe("selectHighSignalLiveItems", () => {
|
||||
it("prefers curated Google replacements before fallback provider spread", () => {
|
||||
const items = [
|
||||
{ provider: "anthropic", id: "claude-opus-4-7" },
|
||||
{ provider: "anthropic", id: "claude-opus-4-6" },
|
||||
{ provider: "google", id: "gemini-3.1-pro-preview" },
|
||||
{ provider: "google", id: "gemini-3-flash-preview" },
|
||||
@@ -500,10 +501,10 @@ describe("selectHighSignalLiveItems", () => {
|
||||
(item) => item.provider,
|
||||
),
|
||||
).toEqual([
|
||||
{ provider: "anthropic", id: "claude-opus-4-7" },
|
||||
{ provider: "anthropic", id: "claude-opus-4-6" },
|
||||
{ provider: "google", id: "gemini-3.1-pro-preview" },
|
||||
{ provider: "google", id: "gemini-3-flash-preview" },
|
||||
{ provider: "openai", id: "gpt-5.2" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ let defaultWarnState: WarnState = { warned: false };
|
||||
|
||||
const DEFAULT_MODEL_ALIASES: Readonly<Record<string, string>> = {
|
||||
// Anthropic (pi-ai catalog uses "latest" ids without date suffix)
|
||||
opus: "anthropic/claude-opus-4-6",
|
||||
opus: "anthropic/claude-opus-4-7",
|
||||
sonnet: "anthropic/claude-sonnet-4-6",
|
||||
|
||||
// OpenAI
|
||||
|
||||
@@ -63,7 +63,7 @@ describe("applyModelDefaults", () => {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"anthropic/claude-opus-4-6": {},
|
||||
"anthropic/claude-opus-4-7": {},
|
||||
"openai/gpt-5.4": {},
|
||||
},
|
||||
},
|
||||
@@ -71,7 +71,7 @@ describe("applyModelDefaults", () => {
|
||||
} satisfies OpenClawConfig;
|
||||
const next = applyModelDefaults(cfg);
|
||||
|
||||
expect(next.agents?.defaults?.models?.["anthropic/claude-opus-4-6"]?.alias).toBe("opus");
|
||||
expect(next.agents?.defaults?.models?.["anthropic/claude-opus-4-7"]?.alias).toBe("opus");
|
||||
expect(next.agents?.defaults?.models?.["openai/gpt-5.4"]?.alias).toBe("gpt");
|
||||
});
|
||||
|
||||
@@ -80,7 +80,7 @@ describe("applyModelDefaults", () => {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"anthropic/claude-opus-4-6": { alias: "Opus" },
|
||||
"anthropic/claude-opus-4-7": { alias: "Opus" },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -88,7 +88,7 @@ describe("applyModelDefaults", () => {
|
||||
|
||||
const next = applyModelDefaults(cfg);
|
||||
|
||||
expect(next.agents?.defaults?.models?.["anthropic/claude-opus-4-6"]?.alias).toBe("Opus");
|
||||
expect(next.agents?.defaults?.models?.["anthropic/claude-opus-4-7"]?.alias).toBe("Opus");
|
||||
});
|
||||
|
||||
it("respects explicit empty alias disables", () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ const BUNDLED_MEDIA_PROVIDER_DEFAULTS: Record<string, BundledMediaProviderDefaul
|
||||
defaultModels: { image: "gpt-5.4" },
|
||||
},
|
||||
anthropic: {
|
||||
defaultModels: { image: "claude-opus-4-6" },
|
||||
defaultModels: { image: "claude-opus-4-7" },
|
||||
autoPriority: { image: 20 },
|
||||
nativeDocumentInputs: ["pdf"],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user