Merge pull request #4131 from code-yeongyu/fix/3563-effort-max-pre-set-clamp
fix(anthropic-effort): clamp pre-set effort=max for constrained providers regardless of variant (fixes #3563)
This commit is contained in:
@@ -76,23 +76,35 @@ export function createAnthropicEffortHook() {
|
||||
const { agent, model, message } = input
|
||||
if (!model?.modelID || !model?.providerID) return
|
||||
if (isEffortUnsupportedModel(model.modelID)) return
|
||||
if (message.variant !== "max") return
|
||||
if (!isClaudeProvider(model.providerID, model.modelID)) return
|
||||
if (shouldSkipForInternalAgent(agent?.name)) return
|
||||
if (output.options.effort !== undefined) return
|
||||
|
||||
const opus = isOpusModel(model.modelID)
|
||||
const constrained = isConstrainedProvider(model.providerID)
|
||||
|
||||
if (output.options.effort !== undefined) {
|
||||
if (output.options.effort === "max" && constrained) {
|
||||
const clamped = clampVariant("max", opus, constrained)
|
||||
output.options.effort = clamped
|
||||
;(message as { variant?: string }).variant = clamped
|
||||
log("anthropic-effort: clamped pre-set effort max→high", {
|
||||
sessionID: input.sessionID,
|
||||
provider: model.providerID,
|
||||
model: model.modelID,
|
||||
reason: "constrained-provider",
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (message.variant !== "max") return
|
||||
|
||||
const clamped = clampVariant(message.variant, opus, constrained)
|
||||
output.options.effort = clamped
|
||||
|
||||
const shouldOverrideMessageVariant = !opus || constrained
|
||||
|
||||
if (shouldOverrideMessageVariant) {
|
||||
// Override the variant so OpenCode doesn't pass "max" to the API.
|
||||
// Non-Opus models cap at high; Anthropic OAuth (Claude Pro/Max) also
|
||||
// caps at high even on Opus because the OAuth API only accepts
|
||||
// low | medium | high.
|
||||
;(message as { variant?: string }).variant = clamped
|
||||
log("anthropic-effort: clamped variant max→high", {
|
||||
sessionID: input.sessionID,
|
||||
|
||||
@@ -221,6 +221,80 @@ describe("createAnthropicEffortHook", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#given pre-set effort via session params — regression for #3563", () => {
|
||||
it("#given pre-set effort=max + variant=max + github-copilot Opus #when hook runs #then effort and variant are both clamped to high", async () => {
|
||||
// given
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({
|
||||
providerID: "github-copilot",
|
||||
modelID: "claude-opus-4-7",
|
||||
variant: "max",
|
||||
existingOptions: { effort: "max" },
|
||||
})
|
||||
|
||||
// when
|
||||
await hook["chat.params"](input, output)
|
||||
|
||||
// then
|
||||
expect(output.options.effort).toBe("high")
|
||||
expect(input.message.variant).toBe("high")
|
||||
})
|
||||
|
||||
it("#given pre-set effort=max + variant=high + github-copilot Opus #when hook runs #then effort and variant are both clamped to high", async () => {
|
||||
// given — this is the cubic violation case from PR #3608:
|
||||
// message.variant is NOT "max", but pre-set effort IS "max"
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({
|
||||
providerID: "github-copilot",
|
||||
modelID: "claude-opus-4-7",
|
||||
variant: "high",
|
||||
existingOptions: { effort: "max" },
|
||||
})
|
||||
|
||||
// when
|
||||
await hook["chat.params"](input, output)
|
||||
|
||||
// then — constrained provider must clamp pre-set effort=max regardless of variant
|
||||
expect(output.options.effort).toBe("high")
|
||||
expect(input.message.variant).toBe("high")
|
||||
})
|
||||
|
||||
it("#given pre-set effort=max + non-constrained Opus #when hook runs #then effort stays max", async () => {
|
||||
// given — anthropic (non-OAuth, non-constrained) Opus with pre-set effort=max
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-opus-4-7",
|
||||
variant: "max",
|
||||
existingOptions: { effort: "max" },
|
||||
})
|
||||
|
||||
// when
|
||||
await hook["chat.params"](input, output)
|
||||
|
||||
// then — non-constrained Opus keeps max
|
||||
expect(output.options.effort).toBe("max")
|
||||
expect(input.message.variant).toBe("max")
|
||||
})
|
||||
|
||||
it("#given pre-set effort=high + github-copilot Opus #when hook runs #then effort stays high", async () => {
|
||||
// given — legitimate pre-set effort=high should not be overwritten
|
||||
const hook = createAnthropicEffortHook()
|
||||
const { input, output } = createMockParams({
|
||||
providerID: "github-copilot",
|
||||
modelID: "claude-opus-4-7",
|
||||
variant: "max",
|
||||
existingOptions: { effort: "high" },
|
||||
})
|
||||
|
||||
// when
|
||||
await hook["chat.params"](input, output)
|
||||
|
||||
// then — high is already valid for constrained providers, don't touch it
|
||||
expect(output.options.effort).toBe("high")
|
||||
})
|
||||
})
|
||||
|
||||
describe("#given anthropic OAuth auth (Claude Pro/Max) — regression for #3429", () => {
|
||||
let tempDataDir: string
|
||||
const originalXdgDataHome = process.env.XDG_DATA_HOME
|
||||
|
||||
Reference in New Issue
Block a user