fix: keep Opus 4.7 effort separate from adaptive thinking

This commit is contained in:
Peter Steinberger
2026-04-17 01:25:50 +01:00
parent c73a6d2f68
commit a2753e2d9f
5 changed files with 28 additions and 15 deletions

View File

@@ -15,13 +15,14 @@ title: "Thinking Levels"
- low → “think hard”
- medium → “think harder”
- high → “ultrathink” (max budget)
- xhigh → “ultrathink+” (GPT-5.2 + Codex models and Anthropic Claude Opus 4.7)
- adaptive → provider-managed adaptive reasoning budget (supported for Anthropic Claude 4.6 and Opus 4.7)
- xhigh → “ultrathink+” (GPT-5.2 + Codex models and Anthropic Claude Opus 4.7 effort)
- adaptive → provider-managed adaptive thinking (supported for Anthropic Claude 4.6 and Opus 4.7)
- `x-high`, `x_high`, `extra-high`, `extra high`, and `extra_high` map to `xhigh`.
- `highest`, `max` map to `high`.
- Provider notes:
- Anthropic Claude 4.6 and Opus 4.7 models default to `adaptive` when no explicit thinking level is set.
- Anthropic Claude Opus 4.7 maps `/think xhigh` to `output_config.effort: "xhigh"`.
- Anthropic Claude 4.6 models default to `adaptive` when no explicit thinking level is set.
- Anthropic Claude Opus 4.7 does not default to adaptive thinking. Its API effort default remains provider-owned unless you explicitly set a thinking level.
- Anthropic Claude Opus 4.7 maps `/think xhigh` to adaptive thinking plus `output_config.effort: "xhigh"`, because `/think` is a thinking directive and `xhigh` is the Opus 4.7 effort setting.
- MiniMax (`minimax/*`) on the Anthropic-compatible streaming path defaults to `thinking: { type: "disabled" }` unless you explicitly set thinking in model params or request params. This avoids leaked `reasoning_content` deltas from MiniMax's non-native Anthropic stream format.
- Z.AI (`zai/*`) only supports binary thinking (`on`/`off`). Any non-`off` level is treated as `on` (mapped to `low`).
- Moonshot (`moonshot/*`) maps `/think off` to `thinking: { type: "disabled" }` and any non-`off` level to `thinking: { type: "enabled" }`. When thinking is enabled, Moonshot only accepts `tool_choice` `auto|none`; OpenClaw normalizes incompatible values to `auto`.
@@ -32,7 +33,7 @@ title: "Thinking Levels"
2. Session override (set by sending a directive-only message).
3. Per-agent default (`agents.list[].thinkingDefault` in config).
4. Global default (`agents.defaults.thinkingDefault` in config).
5. Fallback: `adaptive` for Anthropic Claude 4.6 and Opus 4.7 models, `low` for other reasoning-capable models, `off` otherwise.
5. Fallback: `adaptive` for Anthropic Claude 4.6 models, `off` for Anthropic Claude Opus 4.7 unless explicitly configured, `low` for other reasoning-capable models, `off` otherwise.
## Setting a session default
@@ -105,7 +106,7 @@ title: "Thinking Levels"
- The web chat thinking selector mirrors the session's stored level from the inbound session store/config when the page loads.
- Picking another level writes the session override immediately via `sessions.patch`; it does not wait for the next send and it is not a one-shot `thinkingOnce` override.
- The first option is always `Default (<resolved level>)`, where the resolved default comes from the active session model: `adaptive` for Claude 4.6 and Opus 4.7 on Anthropic, `low` for other reasoning-capable models, `off` otherwise.
- The first option is always `Default (<resolved level>)`, where the resolved default comes from the active session model: `adaptive` for Claude 4.6 on Anthropic, `off` for Anthropic Claude Opus 4.7 unless configured, `low` for other reasoning-capable models, `off` otherwise.
- The picker stays provider-aware:
- most providers show `off | minimal | low | medium | high | adaptive`
- Anthropic Claude Opus 4.7 shows `off | minimal | low | medium | high | xhigh | adaptive`

View File

@@ -199,6 +199,12 @@ describe("anthropic provider replay hooks", () => {
provider: "anthropic",
modelId: "claude-opus-4-7",
} as never),
).toBe("off");
expect(
provider.resolveDefaultThinkingLevel?.({
provider: "anthropic",
modelId: "claude-opus-4-6",
} as never),
).toBe("adaptive");
expect(
provider.supportsXHighThinking?.({

View File

@@ -260,7 +260,6 @@ function resolveAnthropicForwardCompatModel(
function shouldUseAnthropicAdaptiveThinkingDefault(modelId: string): boolean {
const lowerModelId = normalizeLowercaseStringOrEmpty(modelId);
return (
isAnthropicOpus47Model(lowerModelId) ||
lowerModelId.startsWith(ANTHROPIC_OPUS_46_MODEL_ID) ||
lowerModelId.startsWith(ANTHROPIC_OPUS_46_DOT_MODEL_ID) ||
lowerModelId.startsWith(ANTHROPIC_SONNET_46_MODEL_ID) ||
@@ -491,9 +490,11 @@ export function registerAnthropicPlugin(api: OpenClawPluginApi): void {
supportsXHighThinking: ({ modelId }) => isAnthropicOpus47Model(modelId),
wrapStreamFn: wrapAnthropicProviderStream,
resolveDefaultThinkingLevel: ({ modelId }) =>
matchesAnthropicModernModel(modelId) && shouldUseAnthropicAdaptiveThinkingDefault(modelId)
? "adaptive"
: undefined,
isAnthropicOpus47Model(modelId)
? "off"
: matchesAnthropicModernModel(modelId) && shouldUseAnthropicAdaptiveThinkingDefault(modelId)
? "adaptive"
: undefined,
resolveUsageAuth: async (ctx) => await ctx.resolveOAuthToken(),
fetchUsageSnapshot: async (ctx) =>
await fetchClaudeUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),

View File

@@ -1176,7 +1176,7 @@ describe("model-selection", () => {
expect(resolveAnthropicOpusThinking(cfg)).toBe("adaptive");
});
it("uses adaptive fallback for explicitly configured Anthropic Opus 4.7", () => {
it("keeps thinking off by default for explicitly configured Anthropic Opus 4.7", () => {
const cfg = {
agents: {
defaults: {
@@ -1185,7 +1185,7 @@ describe("model-selection", () => {
},
} as OpenClawConfig;
expect(resolveAnthropicOpus47Thinking(cfg)).toBe("adaptive");
expect(resolveAnthropicOpus47Thinking(cfg)).toBe("off");
});
it("falls back to low when no provider thinking hook is active", () => {

View File

@@ -54,13 +54,18 @@ export function resolveThinkingDefault(params: {
if (configured) {
return configured;
}
if (
normalizedProvider === "anthropic" &&
(normalizedModel.startsWith("claude-opus-4-7") || normalizedModel.startsWith("claude-opus-4.7"))
) {
return "off";
}
if (
normalizedProvider === "anthropic" &&
explicitModelConfigured &&
typeof catalogCandidate?.name === "string" &&
/4\.[67]\b/.test(catalogCandidate.name) &&
(normalizedModel.startsWith("claude-opus-4-7") ||
normalizedModel.startsWith("claude-opus-4-6") ||
/4\.6\b/.test(catalogCandidate.name) &&
(normalizedModel.startsWith("claude-opus-4-6") ||
normalizedModel.startsWith("claude-sonnet-4-6"))
) {
return "adaptive";