Skip to content

effort "off" does not disable thinking on Sonnet 5 / Opus 5 / Fable (DisableWire::None omits the parameter, which no longer means off) #827

Description

@chrismurrph

Claim

From the 0.24.1 changelog, introducing /effort:

default clears the override; off is a real level that disables reasoning.

On current Anthropic models it does not. effort: "off" (and /effort off) leaves thinking fully enabled on claude-sonnet-5, claude-opus-5 and claude-fable-5, so a user who explicitly opts out still pays the latency and the reasoning tokens — silently, on the one setting whose whole purpose is opting out.

Cause

adapter.rs gives each provider a DisableWire describing how thinking is turned off. Anthropic's is None — "omit the parameter":

("anthropic", EffortWire::AnthropicBudget, DisableWire::None),

Omitting thinking was a correct disable for Claude 4.6 and earlier. It stopped being one on the current generation:

Model thinking omitted
Sonnet 4.6 / Opus 4.6 no thinking — omit still works
Sonnet 5 thinks (adaptive by default)
Opus 5 thinks (adaptive by default)
Fable 5 always thinks, cannot be disabled

So the wire shape didn't change; what omitting it means did.

The payload already exists in the codebase

Anthropic is one of only three providers left on DisableWire::None; five others carry real disable wires (ThinkingToggle, ChatTemplateKwargs, GeminiZeroBudget, OllamaThink).

Notably GLM — which mimics Anthropic's API — is on ThinkingToggle, which emits:

{"thinking": {"type": "disabled"}}

That is Anthropic's own documented shape. dirge already sends exactly the payload Anthropic needs; it just doesn't send it to Anthropic.

Why the fix is not a one-line table change

Model support is uneven, so a blanket switch to ThinkingToggle would trade this bug for 400s:

  • Sonnet 5 accepts {"type": "disabled"}.
  • Opus 5 accepts it only at effort high or below — pairing it with xhigh/max is rejected. Since off means no thinking at all, that combination shouldn't arise, but the constraint is worth encoding rather than assuming.
  • Fable 5 rejects it outright — thinking is unconditional there. off can never be honoured; warning that the level is unavailable seems more honest than silently accepting it.
  • Claude 4.6 and earlier must keep the current omit-the-parameter behaviour.

So it wants to be per-model, not per-provider — which may mean DisableWire needs a model-aware variant, or the choice moves to where the model id is known.

Impact

This is what forces a non-thinking tier to be Haiku 4.5. There is currently no way to get a fast, non-thinking turn out of Opus 5 or Sonnet 5 through dirge, even though the API supports it.

Happy to put up a PR if the shape above looks right — say if you'd rather have it structured differently (e.g. keep DisableWire per-provider and gate on the model at the call site).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions