Merge pull request #3471 from code-yeongyu/fix/copilot-effort-max

fix(anthropic-effort): clamp variant=max for github-copilot Claude models
This commit is contained in:
YeonGyu-Kim
2026-04-16 18:41:29 +09:00
committed by GitHub
2 changed files with 33 additions and 16 deletions
+14 -13
View File
@@ -26,13 +26,15 @@ function shouldSkipForInternalAgent(agentName: string | undefined): boolean {
} }
/** /**
* Claude Pro/Max subscriptions expose a constrained OAuth API that rejects * Providers that expose constrained APIs rejecting `output_config.effort: "max"`
* `output_config.effort: "max"` (supported values: low | medium | high) even on * (supported values: low | medium | high). Includes:
* Opus models. Detect OAuth auth by inspecting OpenCode's auth.json. * - Anthropic OAuth (Claude Pro/Max via third-party clients)
* - GitHub Copilot (proxied Anthropic, doesn't support "max")
*/ */
function isAnthropicOAuth(providerID: string): boolean { function isConstrainedProvider(providerID: string): boolean {
if (providerID !== "anthropic") return false if (providerID === "github-copilot") return true
return isProviderUsingOAuth(providerID) if (providerID === "anthropic") return isProviderUsingOAuth(providerID)
return false
} }
interface ChatParamsInput { interface ChatParamsInput {
@@ -59,9 +61,9 @@ const MAX_VARIANT_BY_TIER: Record<string, string> = {
default: "high", default: "high",
} }
function clampVariant(variant: string, isOpus: boolean, isOAuth: boolean): string { function clampVariant(variant: string, isOpus: boolean, isConstrained: boolean): string {
if (variant !== "max") return variant if (variant !== "max") return variant
if (isOAuth) return MAX_VARIANT_BY_TIER.default if (isConstrained) return MAX_VARIANT_BY_TIER.default
return isOpus ? MAX_VARIANT_BY_TIER.opus : MAX_VARIANT_BY_TIER.default return isOpus ? MAX_VARIANT_BY_TIER.opus : MAX_VARIANT_BY_TIER.default
} }
@@ -76,16 +78,15 @@ export function createAnthropicEffortHook() {
if (isEffortUnsupportedModel(model.modelID)) return if (isEffortUnsupportedModel(model.modelID)) return
if (message.variant !== "max") return if (message.variant !== "max") return
if (!isClaudeProvider(model.providerID, model.modelID)) return if (!isClaudeProvider(model.providerID, model.modelID)) return
if (model.providerID === "github-copilot") return
if (shouldSkipForInternalAgent(agent?.name)) return if (shouldSkipForInternalAgent(agent?.name)) return
if (output.options.effort !== undefined) return if (output.options.effort !== undefined) return
const opus = isOpusModel(model.modelID) const opus = isOpusModel(model.modelID)
const oauth = isAnthropicOAuth(model.providerID) const constrained = isConstrainedProvider(model.providerID)
const clamped = clampVariant(message.variant, opus, oauth) const clamped = clampVariant(message.variant, opus, constrained)
output.options.effort = clamped output.options.effort = clamped
const shouldOverrideMessageVariant = !opus || oauth const shouldOverrideMessageVariant = !opus || constrained
if (shouldOverrideMessageVariant) { if (shouldOverrideMessageVariant) {
// Override the variant so OpenCode doesn't pass "max" to the API. // Override the variant so OpenCode doesn't pass "max" to the API.
@@ -97,7 +98,7 @@ export function createAnthropicEffortHook() {
sessionID: input.sessionID, sessionID: input.sessionID,
provider: model.providerID, provider: model.providerID,
model: model.modelID, model: model.modelID,
reason: oauth ? "anthropic-oauth" : "non-opus", reason: constrained ? "constrained-provider" : "non-opus",
}) })
} else { } else {
log("anthropic-effort: injected effort=max", { log("anthropic-effort: injected effort=max", {
+19 -3
View File
@@ -153,7 +153,7 @@ describe("createAnthropicEffortHook", () => {
expect(output.options.effort).toBeUndefined() expect(output.options.effort).toBeUndefined()
}) })
it("#given github-copilot + claude model #then effort NOT injected", async () => { it("#given github-copilot + claude opus model #then effort clamped to high (constrained provider)", async () => {
// given // given
const hook = createAnthropicEffortHook() const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({ const { input, output } = createMockParams({
@@ -164,9 +164,25 @@ describe("createAnthropicEffortHook", () => {
// when // when
await hook["chat.params"](input, output) await hook["chat.params"](input, output)
// then — github-copilot is a constrained provider, clamps max→high
expect(output.options.effort).toBe("high")
expect(input.message.variant).toBe("high")
})
it("#given github-copilot + claude sonnet model #then effort clamped to high", async () => {
// given
const hook = createAnthropicEffortHook()
const { input, output } = createMockParams({
providerID: "github-copilot",
modelID: "claude-sonnet-4-6",
})
// when
await hook["chat.params"](input, output)
// then // then
expect(output.options.effort).toBeUndefined() expect(output.options.effort).toBe("high")
expect(input.message.variant).toBe("max") expect(input.message.variant).toBe("high")
}) })
describe("#given haiku models (effort unsupported)", () => { describe("#given haiku models (effort unsupported)", () => {