harnesses: offer each hosted model's reasoning levels and default (OpenCode, DeepSeek Harness, OpenClaw, Claude Code)
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-aiadapter, which Wally registers our provider with, shows Effort only for a model whose entry declaresreasoningEfforts(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 inreasoning_effort. The route setscompat.supportsDeveloperRole: false, because pi-ai would otherwise send the system prompt asdeveloperto 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 owndefault_level. - No Off. pi-ai sends
off's wire value both for Off and when no level is chosen, so declaring off asnonewould turn thinking off under Provider default.
- No default. The adapter takes a default only route-wide (
- OpenCode: one variant per level (
reasoningEffort, which@ai-sdk/openai-compatiblesends asreasoning_effort), plusnonewherethinking_offis true. The model'sdefault_levelgoes in as its model-leveloptions.reasoningEffort, which a chosen variant overrides. OpenCode ignores a#variantin the config'smodelkey, so the default can only go there. - Docs:
docs/EDITORS.mddescribes 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:
| model | levels | default | thinking off |
|---|---|---|---|
| glm-5.3-flash | low, high, max | max | yes |
| deepseek-v4.1-flash | low, high, xhigh, max | max, high once InferenceInfra #5210 ships (Wally reads whichever is current) | yes |
| muse-spark | minimal, low, medium, high, xhigh | medium | no |
| mimo-v2.6-pro | none (on/off only) | none | yes: OpenCode none variant; no dsh control |
Verification
Capture server (the exact request bodies):
| harness | selection | reasoning_effort sent |
|---|---|---|
| OpenCode 2.0.28 | #high | high |
| OpenCode 2.0.28 | #max | max |
| OpenCode 2.0.28 | no variant | none (with the model option set: the default) |
| OpenCode 2.0.28 | #none | none |
| dsh 0.1.5-rc.2 | Effort low | low |
| dsh 0.1.5-rc.2 | Effort xhigh | xhigh |
| dsh 0.1.5-rc.2 | Provider default | none |
The system prompt stays system in every case.
Production, Aman's sign-in, deepseek-v4.1-flash (settled usage rows):
| run | prompt tokens | reasoning tokens |
|---|---|---|
OpenCode #low | 13,253 | 33 |
OpenCode #max | 13,253 | 42 |
OpenCode #none | 13,228 | 0 |
#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
noneonly 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
/thinkoffers each model's levels, sendsnonefor 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
--effortunless the user passes their own;/effortstill 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 highwas refused and noreasoning_effortwas ever sent. Each model with levels now declaresreasoning,compat.supportsReasoningEffortandcompat.supportedReasoningEfforts(nonefirst where thinking can be turned off, so/think offsendsnone). Its default level goes in asagents.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--effortwins, and/effortstill changes the level. Before, every model started at the default of the stand-in Claude model. Verified against a stand-inclaude(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.