harnesses: offer each hosted model's reasoning levels and default (OpenCode, DeepSeek Harness, OpenClaw, Claude Code)

AM
7h ago
+517-3

Why

Our hosted models grade their reasoning by reasoning_effort, but two of the harnesses Wally opens gave customers no way to pick a level:

  • DeepSeek Harness (wally deepseek) showed only "Model: deepseek-v4.1-flash", where DeepSeek's own model shows a second row, "Effort: High".
  • OpenCode offered no effort choice for any of our models.

Root cause

  • dsh draws the Effort row from per-model reasoning metadata. Its llm-pi-ai adapter, which Wally registers our provider with, shows Effort only for a model whose entry declares reasoningEfforts (README: "An adapter without reasoning metadata leaves the Effort row absent"). Wally declared only id, name, input, contextWindow and maxTokens. DeepSeek's own adapter declares off / low / high / max.
  • OpenCode offers effort only through per-model variants, and Wally declared none ({name, limit, cost, modalities, attachment}).

Fix

Both read the console's /v1/models reasoning block (levels, default_level, thinking_off), which Wally already parses. No level list is hard-coded.

  • dsh: each model declares its levels as reasoningEfforts, each sent as itself in reasoning_effort. The route sets compat.supportsDeveloperRole: false, because pi-ai would otherwise send the system prompt as developer to a model that reasons. Two adapter limits shape this:
    • No default. The adapter takes a default only route-wide (reasoning) and refuses a request on any model that lacks that level (MiMo has none). So dsh shows "Provider default" first, sends no level, and the endpoint runs the model's own default_level.
    • No Off. pi-ai sends off's wire value both for Off and when no level is chosen, so declaring off as none would turn thinking off under Provider default.
  • OpenCode: one variant per level (reasoningEffort, which @ai-sdk/openai-compatible sends as reasoning_effort), plus none where thinking_off is true. The model's default_level goes in as its model-level options.reasoningEffort, which a chosen variant overrides. OpenCode ignores a #variant in the config's model key, so the default can only go there.
  • Docs: docs/EDITORS.md describes what each harness offers, and adds "Reasoning effort without wally" for customers who point OpenCode, dsh or any OpenAI-compatible client at the API directly.

What each model gets, from production /v1/models today:

modellevelsdefaultthinking off
glm-5.3-flashlow, high, maxmaxyes
deepseek-v4.1-flashlow, high, xhigh, maxmax, high once InferenceInfra #5210 ships (Wally reads whichever is current)yes
muse-sparkminimal, low, medium, high, xhighmediumno
mimo-v2.6-pronone (on/off only)noneyes: OpenCode none variant; no dsh control

Verification

Capture server (the exact request bodies):

harnessselectionreasoning_effort sent
OpenCode 2.0.28#highhigh
OpenCode 2.0.28#maxmax
OpenCode 2.0.28no variantnone (with the model option set: the default)
OpenCode 2.0.28#nonenone
dsh 0.1.5-rc.2Effort lowlow
dsh 0.1.5-rc.2Effort xhighxhigh
dsh 0.1.5-rc.2Provider defaultnone

The system prompt stays system in every case.

Production, Aman's sign-in, deepseek-v4.1-flash (settled usage rows):

runprompt tokensreasoning tokens
OpenCode #low13,25333
OpenCode #max13,25342
OpenCode #none13,2280

#none turned thinking off: zero reasoning tokens, and a prompt 25 tokens shorter (the thinking render). The dsh headless runs with the effort set through an isolated DSH_HOME settings file answered with thinking on. Before this change, OpenCode refused -m runanywhere/deepseek-v4.1-flash#max with "Variant unavailable".

Tests: two new unit tests. Break test: with each declaration removed, the new test fails (agents.rs:2263, opencode.rs:562); restored, both pass. The full cargo test passes, and cargo fmt --check and clippy -D warnings are clean.

A per-harness matrix (claude-code, claude-desktop, codex, chatgpt, opencode, deepseek, hermes, openclaw, prime-agent: level picker, default, reasoning_effort on the wire, thinking off, images), each column checked live against production, follows as a comment.

Nothing Wally writes outlives the run: OpenCode gets its config through OPENCODE_CONFIG_CONTENT, and dsh a temporary patch.


Summary by cubic

Adds model-specific reasoning effort controls to DeepSeek Harness, OpenCode, OpenClaw, and Claude Code. They previously showed no effort picker (DeepSeek Harness, OpenCode), treated the models as non-reasoning (OpenClaw), or started at another model's default effort (Claude Code); all now read each model's /v1/models reasoning metadata.

  • OpenCode exposes each level as a variant, adds none only when thinking can be disabled, and starts at the model's default level.
  • DeepSeek Harness shows the levels with "Provider default" first and offers no Off, because its adapter treats Off and no selection the same.
  • OpenClaw's /think offers each model's levels, sends none for off where allowed, and sets the model's default unless the user's own config already sets one.
  • Claude Code starts each model at its own default effort via --effort unless the user passes their own; /effort still changes it in the session.
  • Editor docs explain each harness's controls and how to set reasoning effort when connecting to the API directly.

Summary by CodeRabbit

  • New Features
    • Reasoning controls are now available in OpenCode, DeepSeek Harness, and OpenClaw when supported by the selected model. OpenCode and OpenClaw reflect model defaults and whether reasoning can be turned off; DeepSeek Harness does not offer an Off option.
    • Claude Code now uses the selected model’s default reasoning effort when no effort is specified.
    • OpenAI-compatible clients can set reasoning effort directly in requests.
  • Documentation
    • The editor guide now covers reasoning controls for OpenCode, DeepSeek Harness, OpenClaw, Claude Code, Codex, ChatGPT, Claude Desktop, Hermes, and Prime Agent, plus direct client configuration.

Added after the first push

  • OpenClaw: it took our models not to reason: --thinking high was refused and no reasoning_effort was ever sent. Each model with levels now declares reasoning, compat.supportsReasoningEffort and compat.supportedReasoningEfforts (none first where thinking can be turned off, so /think off sends none). Its default level goes in as agents.defaults.models["runanywhere/<id>"].params.thinking, unless the user's config sets one; that table does not restrict OpenClaw's picker. Verified with OpenClaw 2026.10.4 on Node 24, against the capture server and against production through Wally. Break test included.
  • Claude Code: Wally passes --effort <model's default_level> the way it passes --model. The user's own --effort wins, and /effort still changes the level. Before, every model started at the default of the stand-in Claude model. Verified against a stand-in claude (argv) and with a production run.
  • EDITORS.md names the three tools that keep their own default (Hermes, Claude Desktop, Prime Agent) and the workaround for each.

The per-harness matrix is in the comment below.