You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
desktop: model picker shows "No reasoning" for all V2 models — capabilities.reasoning is never set #50257
The desktop model picker tooltip shows "No reasoning" for every V2 model — including models that clearly support reasoning (e.g. opencode-go/deepseek-v4.1-flash, opencode-go/kimi-k3, opencode-go/glm-5.3, …).
Root cause
The tooltip decides based on model.capabilities.reasoning (desktop bundle, app.asar):
The corresponding models.dev catalog entry has "reasoning": true, but the V2 model transform does not carry it into capabilities.
Impact
Display-only — reasoning itself works. Requests are sent with reasoningMode: "effort" / reasoningEffort: "max" and responses contain reasoning tokens; only the tooltip is wrong. It affects all V2 models, not just DeepSeek.
Environment
OpenCode Desktop 2.0.11, macOS (Apple Silicon)
Server-side check: GET /api/model (all 82 models missing the field)
Suggested fix
Either populate capabilities.reasoning from the catalog reasoning field in the V2 model transform, or fall back to the presence of reasoning variants in the tooltip logic.
Reacted by Adam Suban-Loewen, tomatoo, Wang Zishi, mixailka222 and Nail Ismangulov
Also affects built-in catalog models such as minimax-cn/MiniMax-M3 — not only custom providers
Confirmed display-only: reasoning works through the variant selector, reasoning tokens are counted in context stats, and requests carry reasoningEffort
Re-confirmed there is no config escape hatch: a model-level reasoning: true is dropped by the V2 schema, canonical inherits family/variants/cost but not the flag, and capabilities.output: ["text","reasoning"] is ignored
Following up from #54195 (closed as a duplicate of this issue): the same desktop adapter that hardcodes reasoning: false also hardcodes two other capability flags in the same object:
So beyond the "No reasoning" tooltip, any UI reading capabilities.temperature or capabilities.interleaved is also wrong for every model.
Both are derivable from data the server already has:
temperature — the models.dev catalog entry has temperature: true for these models (e.g. nvidia/deepseek-ai/deepseek-v4.1-flash, opencode/mimo-v2.6-flash-free).
interleaved — the catalog carries interleaved: { field: "reasoning_content" }, and the server already maps this into compatibility.reasoningField on Model.Info.
If PR #50283 is threading the reasoning flag through from catalog to app capabilities, it may be worth deriving these two in the same pass so the capability set doesn't stay half-wrong.
Verified on Desktop 2.0.26 (Windows 11): the effort/variant selector works correctly (it reads the separate variants array), so this remains display-only.
Adding an update for anyone landing here: this is still unfixed on the current dev (default branch), and the existing fix PRs were closed by automation, not by technical review.
Still broken in latest source (checked against ref=dev)
packages/schema/src/model.ts (L90-94): Model.Capabilities is still only { tools, input, output } — no reasoning field.
packages/app/src/runtime/server/global-sync/utils.ts:84: the app mapper still hardcodes reasoning: false.
Reproducible on the latest desktop release 2.0.26 (current latest per the official update manifest), same as #54195 / #54229.
same sweep (2026-10-10 18:53:49Z), verified on Windows
All three were closed by the v1-pr-dev-promoted-v2 auto-close template ("…port it over and open it for V2"), not by a maintainer deciding the change was wrong.
Scope note
Purely a tooltip/label bug: V2 exposes reasoning through variants (thinking-level selector) and compatibility.reasoningField (supportsEffortUpdates), and the intensity selector works in-session — only the model-picker tooltip ("No reasoning") is misleading.
Maintainers: would a fresh PR against dev implementing either approach (thread capabilities.reasoning through the chain as in #50283, or derive the flag from variants as in #48417) be welcome? Happy to open one.
Update: the fix has been ported to the current V2 line and re-opened as #55100 (it is a port of #50283, which the V1→V2 promotion sweep closed). Same minimal approach: optional reasoning on Model.Capabilities, populated from the models.dev catalog materialization, threaded through mergeCapabilities, and the app mapper derives the flag instead of hardcoding false. Typecheck 36/36 and the affected app/core tests pass against dev.
Custom provider via @ai-sdk/openai-compatible (local router), no plugins
Observations
Every model from the custom provider shows Reasoning: No reasoning in the model picker tooltip, while Context correctly reflects the configured limit.context (1,000,000).
In the desktop bundle (app.asar), the V2 → legacy model adapter builds capabilities with reasoning:!1 hardcoded, so the tooltip can never show "Allows reasoning" regardless of config.
Setting "reasoning": true on the model in opencode.jsonc has no effect on 2.0.29; the server log drops it:
The bundled TUI in 2.0.29 derives reasoning from variants (reasoning:(model.variants?.length??0)!==0), so a similar derivation in the desktop adapter (or exposing a config field for it) would fix the label.
Reasoning itself does work at request time for models that emit it; only the tooltip label is wrong.
Additional data point: the hardcoded reasoning: false is not only in the desktop app.asar — the Web UI bundle served by the CLI itself has it too, and I can show the tooltip value cannot be influenced by server data or configuration.
Environment
opencode 2.0.29 (@opencode/cli, npm global install), Windows 11 (NT 10.0.26200)
Shell cmd.exe, no plugins
Web UI served by opencode serve --port 4096
Where the hardcode lives (CLI-served web bundle)
GET /_assets/runtime-*.js contains the v1 → app model adapter with three flags hardcoded:
The tooltip component (/_assets/incompatible-server-panel-*.js) reads exactly that value, and its model.reasoning branch is unreachable because the adapter always attaches a capabilities object:
A/B proof — same browser, same config, only the bundle differs
Entry point
opencode/nemotron-3.5-lightning-free → 推理 row
http://127.0.0.1:4096 (stock bundle)
不支持推理
http://127.0.0.1:8789 (same server, runtime-*.js rewritten to reasoning:!!e.compatibility?.reasoningField)
支持推理
Everything else in the same tooltip is already correct (提供商 OpenCode, 输入 文本, 上下文 262,144), and that context value matches GET /api/model for the same model:
So the data needed for the row is present on the client; only capabilities.reasoning is wrong. No server-side change alone can fix the label, which also means a config workaround cannot exist until the adapter maps the flag (as in #50283) or the tooltip derives it from compatibility.reasoningField / variants.
CLI-served web UI: that bundle is built from the same source as the desktop adapter — packages/app (assetsDir: "_assets"), served by packages/cli/src/services/web-ui.ts — so this is one hardcode in one source location, not two. The open PR fix(core): expose model reasoning capability #55100 fixes it for both the desktop app.asar and the CLI's /_assets/runtime-*.js.
Config declaration: a bare models.<id>.reasoning in a V2-style file is V1 syntax, which normalizes as "unsupported legacy setting" (expected, exactly as your log line shows). Once fix(core): expose model reasoning capability #55100 lands, the supported declaration is capabilities: { reasoning: true } — the config overlay schema previously mapped only tools/input/output and silently stripped it; genuine V1 files are migrated and keep reasoning instead of dropping it.
To be clear: #55100 is under review and not released yet, so the tooltip will keep showing "No reasoning" on 2.0.29 until it ships.
(The variants-derived display the TUI uses is the alternative approach from #48417; #55100 is the capabilities-threading one from #50283.)
83/83: capabilities is {tools, input, output} only — 0 have capabilities.reasoning, 0 have top-level reasoning
tooltip shows 推理: 不支持推理 for every model
Caveat: neither single derivation signal is sufficient
Splitting the same 83-model inventory by the two reasoning signals that ARE present in GET /api/model:
variants (reasoningEffort/thinkingConfig)
compatibility.reasoningField
count
variants only
✓
–
40
reasoningField only
–
✓
24
both
✓
✓
19
neither
–
–
0
The TUI's variants.length !== 0 derivation (noted in this thread) would correctly label 59 models but still show "No reasoning" for the 24 reasoningField-only models — including every xiaomi*/mimo-* and */longcat-2.5-preview-free, which do stream thinking via reasoning_content.
The A/B patch from @yukunmeng (reasoning: !!e.compatibility?.reasoningField) covers 43 but misses the 40 variants-only models.
So the client mapper should derive capabilities.reasoning ||= !!compatibility.reasoningField || variants.length > 0 — on this inventory that is 83/83 correct with zero false positives, using data the server already sends. (Once #55100 threads the catalog flag through, this OR also acts as the fallback for models absent from models.dev.)
Independent repro on Desktop 2.0.31 — BYOK deepseek provider, where reasoning is demonstrably working
Adding a data point for a provider type not covered above: the built-in deepseek provider (BYOK, package @opencode/ai/providers/deepseek), model deepseek/deepseek-flash.
capabilities is { tools, input, output, temperature } — no reasoning key, matching the pattern reported above for other providers.
Model picker tooltip shows "No reasoning".
This model is not merely declared as reasoning — it is observably reasoning
With the variant recorded as default for the entire session (i.e. no user-applied variant, so this is the out-of-the-box path), every assistant message in session_message (opencode.db) carries a reasoning content part — including the first message, which predates any local config file:
seq
time (local)
variant
reasoning part present
6
13:21:10
default
yes
21
13:23:58
default
yes
...
...
default
yes
242
13:32:07
default
yes
The empty default variant sends neither thinking nor reasoning_effort, so DeepSeek's own defaults apply (thinking enabled, effort high), reasoning_content is streamed back via compatibility.reasoningField, and OpenCode stores it as a reasoning part.
So for this model the tooltip is not merely conservative — it asserts the opposite of the actual behaviour.
Confirming on 2.0.31 with a scenario that is not covered by the models.dev catalog path — a custom OpenAI-compatible provider whose model is declared entirely in opencode.json.
So the picker shows model.tooltip.reasoning.none ("No reasoning" / 不支持推理) even though reasoning works end to end for this model — the same session has 38 type: "reasoning" parts and usage like "tokens": { "input": 575, "output": 116, "reasoning": 147 }. The TUI is unaffected because it derives support from model.variants.length > 0; only the app mapper in packages/app/src/runtime/server/global-sync/utils.ts hard-codes reasoning: false while mapping variants correctly one line below.
Materializing reasoning from the models.dev catalog fixes the catalog case, but models that only exist via user config (self-hosted / gateway providers, and the new "generate reasoning variants from reasoning options" behavior) have no catalog entry to read from. Would it be worth deriving the flag as capabilities.reasoning === true || model.variants.length > 0 in the app mapper as a belt-and-braces fallback, so config-declared reasoning controls map to true as well?
If Model.Capabilities.reasoning is going to be an explicit field, it would be good if the server also reported it for config-declared reasoning controls, not just for catalog flags — that is the difference between "listed in models.dev" and "actually able to reason" for a lot of local / proxied models.
Confirmed on macOS (desktop app 2.0.31, Darwin 27.0.0 arm64) with the NVIDIA provider (integrate.api.nvidia.com, @opencode/ai/providers/openai-compatible).
GET /api/model returns 41 NVIDIA models; 0/41 include capabilities.reasoning, but they do include variants (e.g. none/thinking).
Example: nvidia/z-ai/glm-5.3 → capabilities:{tools,input,output,temperature}, variants:[none,thinking]. The tooltip shows "No reasoning".
The model does reason: opencode run -m "nvidia/nvidia/nemotron-3.5-lightning-30b-a3b#thinking" returned a reasoning block (reasoningField: reasoning_content), and a session with nvidia/z-ai/glm-5.3 stored 61 reasoning tokens.
Root cause re-confirmed in the bundle: the V2→legacy adapter (app.asar, out/renderer/assets/main-*.js) builds capabilities:{temperature:!1, reasoning:!1, ...} hard-coded, so the tooltip (capabilities.reasoning) always reports "No reasoning". Suggested fix: derive reasoning support from variants.
Environment: opencode 2.0.31, Darwin 27.0.0 (darwin arm64), web/desktop client, shell /bin/zsh, no relevant local plugins.
Same root cause, still reproducible on 2.0.32 (Windows x64, Chinese UI). Additional measurements that may help to close this out:
GET /api/model on a live 2.0.32 server returns 47 models: 0 / 47 have capabilities.reasoning, 0 / 47 have a top-level reasoning field, and all 47 have capabilities present — so the tooltip's model.reasoning fallback is unreachable.
In packages/core/src/models-dev.ts, modelInfo() builds capabilities from tools / input / output / temperature only; models.dev's top-level reasoning: true and reasoning_options are only converted into variants by supports(), never copied into capabilities.
Reasoning execution is unaffected: sessions on opencode/space-bunny-free#max and opencode/minimax-m3.1-flash-free#max report tokens.reasoning of 71263 and 16437 respectively.
One config-related detail, since the issue mentions the config path: Config.Model.Capabilities.reasoning in the config schema is the v2 object shape (supported / effort / budget / toggle) and is only used to build reasoning variants, while the runtime Model.Capabilities type has no reasoning field at all. Defining providers.<id>.models.<model>.capabilities.reasoning therefore cannot change what the tooltip reads. It also replaces (rather than merges with) the models.dev record: the model name falls back to the raw id, limit falls back to {context: 200000, output: 32000}, the video modality is lost and compatibility.reasoningField is dropped.
Thanks for measuring this on 2.0.32 — the 0/47 result matches the root cause. #55100 carries the catalog's reasoning flag through to Model.Capabilities, and the app falls back to compatibility.reasoningField / non-none variants when the catalog is silent.
One correction on the config path: in #55100 the config's rich capabilities.reasoning is reduced to that runtime boolean in mergeCapabilities, so { "supported": true } does reach the tooltip. The metadata loss you saw (name / limit / modalities / compatibility falling back to defaults) is separate from the reasoning flag and probably worth its own issue.
Independent repro on v2.1.3 (Linux, self-hosted server) — and a config-path data point
Confirming this is still present on v2.1.3 (latest as of 2026-10-23), with a server where the models are declared in opencode.json (custom providers) and the config path is exercised:
opencode-go: 28 models → 0/28 have capabilities.reasoning; 27/28 have variants (e.g. deepseek-v4.1-flash → variants: [low, high, max]).
GET /api/model for opencode-go/deepseek-v4.1-flash returns capabilities: {tools,input,output,temperature} — no reasoning, no top-level reasoning.
The Config.Model.Capabilities schema does have reasoning (config side), and providers.<id>.models.<id>.capabilities.reasoning validates and is retained in opencode debug config — but Model.Capabilities (API response) has no such field, so it is stripped before the client sees it.
Adding explicit variants to models does not change the tooltip (the tooltip reads capabilities.reasoning).
So the client has no signal at all, regardless of config. This matches the modelInfo()/mergeCapabilities analysis above; #55100 ("fix(core): expose model reasoning capability", still OPEN targeting dev) looks like the correct fix. Adding this data point so the config path is covered by the test matrix: after #55100, {"capabilities":{"reasoning":{"supported":true}}} from opencode.json should reach the tooltip.
Repro environment: opencode v2.1.3, opencode serve --service, 149 models across 10 providers, 116 with variants.
Fresh repro on Desktop 2.1.4 (latest channel, Windows 11 x64, 2026-10-25) — still broken on the newest build.
Measurements (GET /api/model, live server):
71 models total: 0/71 have capabilities.reasoning, 0/71 have a top-level reasoning; 71/71 have capabilities present → the tooltip's model.reasoning fallback stays unreachable
31/71 expose compatibility.reasoningField, 28/71 have effort variants (low/medium/high/…) — reasoning works at runtime, only the capability flag is missing
Config-path data point (custom providers declared in opencode.jsonc):
Provider-level compatibility.reasoningField merges correctly → visible for 14/14 models in /api/model
capabilities.reasoning never reaches the store: the config capabilities merge keeps only tools/input/output/temperature, so the declared effort is dropped and variants remain whatever the package fallback generated
Bundle check confirms the picker still branches on model.capabilities.reasoning (app.asar, model.tooltip.reasoning.*).
Hoping #55100 lands — the config path above should be covered by it.
Still reproducible on OpenCode Desktop 2.1.4 (macOS 26 / Darwin 25.6.0, Apple Silicon), and it affects the free OpenCode Zen model too, not just V2 paid ones.
What the UI shows
Model picker → free model opencode/deepseek-v4.1-flash-free → tooltip:
Model: DeepSeek V4.1 Flash
Provider: OpenCode
Input: text, image
Reasoning: "No reasoning" (RU: «Без рассуждений»)
Context limit: 200,000
Reasoning actually works on the free tier
The catalog says reasoning: true with toggle + effort [low, high, max]:
providers/opencode/models/deepseek-v4.1-flash-free.toml on models.dev
local catalog cache (kv → models-dev:catalog)
Empirical check through the bundled CLI (same endpoint, --format json --thinking), one prompt: "Do not use any tools. Compute 87341 * 92837 in your head and reply with just the number."
Variant
reasoning block
#low
564 chars of CoT
#high
733 chars
#max
616 chars
default (no variant)
699 chars
The stream contains a real reasoning part with metadata.reasoningField: "reasoning_content", e.g. on #max the model decomposed the multiplication, cross-checked it two ways, and answered 8,108,476,417. No "variant unavailable" error for any variant.
Same root cause in current dev
packages/app/src/runtime/server/global-sync/utils.ts still hardcodes:
So this is display-only: the request carries reasoningEffort, the response carries reasoning_content, only the tooltip lies. Deriving reasoning from the catalog flag (or from variants.length > 0) would fix the free model as a side effect.
Thanks for the detailed report. I was able to confirm this.
On 2.0.11, I started opencode serve and queried GET /api/model?all=true. No model in the catalog has capabilities.reasoning (or a top-level reasoning). That includes models that offer reasoningEffort variants and are marked reasoning: true in models.dev, such as DeepSeek Flash. On top of that, the app's model mapper (packages/app/src/runtime/server/global-sync/utils.ts) hard-codes capabilities.reasoning: false, so the tooltip can only ever show "No reasoning". I didn't open the desktop app itself; the API output and that code already settle it.
It still happens on the latest dev (fa1260d). The script below reproduces the API side.
Open PR #55100 addresses this. The later duplicates (#53520, #54229, #56317, #58901) were closed in favour of this issue.
Reproduction script (repro.sh)
#!/usr/bin/env bash# Reproduces #50257: no V2 model carries a reasoning capability, so the app's# model tooltip ("Reasoning") reports "No reasoning" for every model.# Usage: ./repro.sh -> uses `opencode` from PATH# OC_DEV=/path/to/repo ./repro.sh -> runs the CLI from that checkoutset -euo pipefail
PORT=${PORT:-4111}
WORK=$(mktemp -d)export HOME="$WORK/home"; mkdir -p "$HOME""$WORK/proj";cd"$WORK/proj"if [ -n"${OC_DEV:-}" ];then
OC=(bun --conditions=browser --preload="$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js""$OC_DEV/packages/cli/src/index.ts")
else
OC=(opencode)
fiecho"version: $("${OC[@]}" --version)""${OC[@]}" serve --port "$PORT" --hostname 127.0.0.1 >"$WORK/serve.log"2>&1&
PID=$!;trap'kill $PID 2>/dev/null || true' EXIT
for_in$(seq 60);do grep -q "server password""$WORK/serve.log"2>/dev/null &&break; sleep 1;done
PASS=$(grep "server password""$WORK/serve.log"| awk '{print $3}')# wait until the models.dev catalog has loadedfor_in$(seq 30);do
curl -s -u "opencode:$PASS""http://127.0.0.1:$PORT/api/model?all=true">"$WORK/models.json"
grep -q '"capabilities"'"$WORK/models.json"&&break; sleep 1
done
python3 - "$WORK/models.json"<<'EOF'import json, sysd = json.load(open(sys.argv[1]))["data"]print("models:", len(d))print("with capabilities.reasoning:", sum("reasoning" in m["capabilities"] for m in d))print("with top-level reasoning:", sum("reasoning" in m for m in d))print("with reasoningEffort variants:", sum(any("reasoningEffort" in (v.get("settings") or {}) for v in m.get("variants") or []) for m in d))m = next((m for m in d if m["providerID"] == "deepseek"), None) or next(m for m in d if m.get("variants"))print("example:", json.dumps({k: m.get(k) for k in ("id", "providerID", "capabilities", "variants")})[:600])EOF
Activity
zhengkaics commentedon Sep 21, 2026
修复已提交:#50283。根因不只是 tooltip 读取的字段——models.dev 目录里的
reasoning标志在构造 V2Model.Capabilities时被丢掉,桌面端 mapper 又硬编码了reasoning: false。PR 把这个标志从目录一路传到应用 store,tooltip 本身不用改。8mdphgg5yn-eng commentedon Sep 30, 2026
Additional data point — still reproducible on the latest release (2.0.20).
@opencode/cli2.0.20,latestchannel — the updater reports 2.0.20 is the newest buildGET /api/modelon this machine: 160 models, 0 withcapabilities.reasoning; 72 have reasoning variantsdeepseek/deepseek-flash:capabilities: { tools: true, input: ["text","image"], output: ["text"] },variants: none / low / high / max,compatibility.reasoningField: "reasoning_content"→ tooltip shows 推理: 不支持推理minimax-cn/MiniMax-M3— not only custom providersreasoningEffortreasoning: trueis dropped by the V2 schema,canonicalinherits family/variants/cost but not the flag, andcapabilities.output: ["text","reasoning"]is ignoredreasoningflag is dropped when V2Model.Capabilitiesis built, and the app mapper hardcodesreasoning: falsejunhuwh commentedon Oct 9, 2026
Following up from #54195 (closed as a duplicate of this issue): the same desktop adapter that hardcodes
reasoning: falsealso hardcodes two other capability flags in the same object:So beyond the "No reasoning" tooltip, any UI reading
capabilities.temperatureorcapabilities.interleavedis also wrong for every model.Both are derivable from data the server already has:
temperature— the models.dev catalog entry hastemperature: truefor these models (e.g.nvidia/deepseek-ai/deepseek-v4.1-flash,opencode/mimo-v2.6-flash-free).interleaved— the catalog carriesinterleaved: { field: "reasoning_content" }, and the server already maps this intocompatibility.reasoningFieldonModel.Info.If PR #50283 is threading the reasoning flag through from catalog to app capabilities, it may be worth deriving these two in the same pass so the capability set doesn't stay half-wrong.
Verified on Desktop 2.0.26 (Windows 11): the effort/variant selector works correctly (it reads the separate
variantsarray), so this remains display-only.AirMonkeys-Kun commentedon Oct 11, 2026
Adding an update for anyone landing here: this is still unfixed on the current
dev(default branch), and the existing fix PRs were closed by automation, not by technical review.Still broken in latest source (checked against
ref=dev)packages/schema/src/model.ts(L90-94):Model.Capabilitiesis still only{ tools, input, output }— noreasoningfield.packages/app/src/runtime/server/global-sync/utils.ts:84: the app mapper still hardcodesreasoning: false.Reproducible on the latest desktop release 2.0.26 (current latest per the official update manifest), same as #54195 / #54229.
The fix PRs were closed without review
variantsreasoningtoCapabilities, populate it from the models.dev catalog, map it in the appdevpromotion sweep (2026-10-10 18:30:41Z), after ~3 weeks waiting for first reviewreasoningin the v1→v2 config migrationAll three were closed by the
v1-pr-dev-promoted-v2auto-close template ("…port it over and open it for V2"), not by a maintainer deciding the change was wrong.Scope note
Purely a tooltip/label bug: V2 exposes reasoning through
variants(thinking-level selector) andcompatibility.reasoningField(supportsEffortUpdates), and the intensity selector works in-session — only the model-picker tooltip ("No reasoning") is misleading.Maintainers: would a fresh PR against
devimplementing either approach (threadcapabilities.reasoningthrough the chain as in #50283, or derive the flag fromvariantsas in #48417) be welcome? Happy to open one.zhengkaics commentedon Oct 13, 2026
Update: the fix has been ported to the current V2 line and re-opened as #55100 (it is a port of #50283, which the V1→V2 promotion sweep closed). Same minimal approach: optional
reasoningonModel.Capabilities, populated from the models.dev catalog materialization, threaded throughmergeCapabilities, and the app mapper derives the flag instead of hardcodingfalse. Typecheck 36/36 and the affected app/core tests pass againstdev.tiftazani commentedon Oct 15, 2026
Same issue here. Additional data points in case useful:
Environment
opencode-cliv2.0.29), channel: latest@ai-sdk/openai-compatible(local router), no pluginsObservations
Reasoning: No reasoningin the model picker tooltip, whileContextcorrectly reflects the configuredlimit.context(1,000,000).app.asar), the V2 → legacy model adapter buildscapabilitieswithreasoning:!1hardcoded, so the tooltip can never show "Allows reasoning" regardless of config."reasoning": trueon the model inopencode.jsonchas no effect on 2.0.29; the server log drops it:reasoning:(model.variants?.length??0)!==0), so a similar derivation in the desktop adapter (or exposing a config field for it) would fix the label.Reasoning itself does work at request time for models that emit it; only the tooltip label is wrong.
yukunmeng commentedon Oct 15, 2026
Additional data point: the hardcoded
reasoning: falseis not only in the desktopapp.asar— the Web UI bundle served by the CLI itself has it too, and I can show the tooltip value cannot be influenced by server data or configuration.Environment
2.0.29(@opencode/cli, npm global install), Windows 11 (NT 10.0.26200)cmd.exe, no pluginsopencode serve --port 4096Where the hardcode lives (CLI-served web bundle)
GET /_assets/runtime-*.jscontains the v1 → app model adapter with three flags hardcoded:The tooltip component (
/_assets/incompatible-server-panel-*.js) reads exactly that value, and itsmodel.reasoningbranch is unreachable because the adapter always attaches acapabilitiesobject:A/B proof — same browser, same config, only the bundle differs
opencode/nemotron-3.5-lightning-free→ 推理 rowhttp://127.0.0.1:4096(stock bundle)http://127.0.0.1:8789(same server,runtime-*.jsrewritten toreasoning:!!e.compatibility?.reasoningField)Everything else in the same tooltip is already correct (提供商
OpenCode, 输入文本, 上下文262,144), and that context value matchesGET /api/modelfor the same model:{ "id": "nemotron-3.5-lightning-free", "providerID": "opencode", "capabilities": { "tools": true, "input": ["text"], "output": ["text"] }, "compatibility": { "reasoningField": "reasoning_content" }, "limit": { "context": 262144, "output": 262144 } }So the data needed for the row is present on the client; only
capabilities.reasoningis wrong. No server-side change alone can fix the label, which also means a config workaround cannot exist until the adapter maps the flag (as in #50283) or the tooltip derives it fromcompatibility.reasoningField/variants.Related
provider/npm/optionsconfig shape silently dropscompatibilityfor custom providers entirely, socompatibility.reasoningFieldnever reaches the server for them (the V2providersshape keeps it).temperature:!1andinterleaved:!1are hardcoded in the same object.zhengkaics commentedon Oct 15, 2026
Thanks both — two useful confirmations on 2.0.29.
CLI-served web UI: that bundle is built from the same source as the desktop adapter —
packages/app(assetsDir: "_assets"), served bypackages/cli/src/services/web-ui.ts— so this is one hardcode in one source location, not two. The open PR fix(core): expose model reasoning capability #55100 fixes it for both the desktopapp.asarand the CLI's/_assets/runtime-*.js.Config declaration: a bare
models.<id>.reasoningin a V2-style file is V1 syntax, which normalizes as "unsupported legacy setting" (expected, exactly as your log line shows). Once fix(core): expose model reasoning capability #55100 lands, the supported declaration iscapabilities: { reasoning: true }— the config overlay schema previously mapped onlytools/input/outputand silently stripped it; genuine V1 files are migrated and keepreasoninginstead of dropping it.To be clear: #55100 is under review and not released yet, so the tooltip will keep showing "No reasoning" on 2.0.29 until it ships.
(The variants-derived display the TUI uses is the alternative approach from #48417; #55100 is the capabilities-threading one from #50283.)
wangzishi commentedon Oct 15, 2026
Fresh repro on Desktop 2.0.29 (Windows 11, bundled opencode-cli 2.0.29) plus a derivation caveat for #55100 that hasn't been quantified yet.
Repro data (4 provider types, 83 models)
opencode(44),opencode-go(29),xiaomi+xiaomi-token-plan-cn(6+4,@opencode/ai/providers/openai-compatible)capabilitiesis{tools, input, output}only — 0 havecapabilities.reasoning, 0 have top-levelreasoningCaveat: neither single derivation signal is sufficient
Splitting the same 83-model inventory by the two reasoning signals that ARE present in
GET /api/model:reasoningEffort/thinkingConfig)compatibility.reasoningFieldvariants.length !== 0derivation (noted in this thread) would correctly label 59 models but still show "No reasoning" for the 24 reasoningField-only models — including everyxiaomi*/mimo-*and*/longcat-2.5-preview-free, which do stream thinking viareasoning_content.reasoning: !!e.compatibility?.reasoningField) covers 43 but misses the 40 variants-only models.So the client mapper should derive
capabilities.reasoning ||= !!compatibility.reasoningField || variants.length > 0— on this inventory that is 83/83 correct with zero false positives, using data the server already sends. (Once #55100 threads the catalog flag through, this OR also acts as the fallback for models absent from models.dev.)fix(app): derive reasoning from variants and reasoningField when the …
benjohnbarnes commentedon Oct 16, 2026
Independent repro on Desktop 2.0.31 — BYOK
deepseekprovider, where reasoning is demonstrably workingAdding a data point for a provider type not covered above: the built-in
deepseekprovider (BYOK, package@opencode/ai/providers/deepseek), modeldeepseek/deepseek-flash.GET /api/model→deepseek/deepseek-flash:{ "id": "deepseek-flash", "providerID": "deepseek", "package": "@opencode/ai/providers/deepseek", "compatibility": { "reasoningField": "reasoning_content" }, "capabilities": { "tools": true, "input": ["text", "image"], "output": ["text"], "temperature": true }, "variants": [ { "id": "none", "body": { "thinking": { "type": "disabled" } } }, { "id": "low", "settings": { "reasoningEffort": "low" }, "body": { "thinking": { "type": "enabled" } } }, { "id": "high", "settings": { "reasoningEffort": "high" }, "body": { "thinking": { "type": "enabled" } } }, { "id": "max", "settings": { "reasoningEffort": "max" }, "body": { "thinking": { "type": "enabled" } } } ] }capabilitiesis{ tools, input, output, temperature }— noreasoningkey, matching the pattern reported above for other providers.This model is not merely declared as reasoning — it is observably reasoning
With the variant recorded as
defaultfor the entire session (i.e. no user-applied variant, so this is the out-of-the-box path), every assistant message insession_message(opencode.db) carries areasoningcontent part — including the first message, which predates any local config file:defaultdefaultdefaultdefaultThe empty
defaultvariant sends neitherthinkingnorreasoning_effort, so DeepSeek's own defaults apply (thinking enabled, efforthigh),reasoning_contentis streamed back viacompatibility.reasoningField, and OpenCode stores it as areasoningpart.So for this model the tooltip is not merely conservative — it asserts the opposite of the actual behaviour.
Environment
opencode-cli2.0.31), channel: latestfix(core): expose model reasoning capability
fix(app): derive reasoning from variants and reasoningField when the …
xuyongroger commentedon Oct 17, 2026
Confirming on 2.0.31 with a scenario that is not covered by the models.dev catalog path — a custom OpenAI-compatible provider whose model is declared entirely in
opencode.json.Model config (redacted):
{ "providers": { "acme": { "package": "@opencode/ai/providers/openai-compatible", "settings": { "baseURL": "https://llm.internal.example/v1" }, "models": { "qwen-flash": { "settings": { "reasoningEffort": "xhigh" }, "capabilities": { "reasoning": { "supported": true, "effort": ["low", "medium", "xhigh", "none"] }, "tools": true } } } } } }GET /api/modelreturns the reasoning controls as generated variants, andcapabilitiesstill has noreasoningfield at all:{ "id": "qwen-flash", "providerID": "acme", "settings": { "reasoningEffort": "xhigh" }, "capabilities": { "tools": true, "input": ["text", "image"], "output": ["text"] }, "variants": [ { "id": "low", "settings": { "reasoningEffort": "low" } }, { "id": "medium", "settings": { "reasoningEffort": "medium" } }, { "id": "xhigh", "settings": { "reasoningEffort": "xhigh" } }, { "id": "none", "settings": { "reasoningEffort": "none" } } ] }So the picker shows
model.tooltip.reasoning.none("No reasoning" / 不支持推理) even though reasoning works end to end for this model — the same session has 38type: "reasoning"parts and usage like"tokens": { "input": 575, "output": 116, "reasoning": 147 }. The TUI is unaffected because it derives support frommodel.variants.length > 0; only the app mapper inpackages/app/src/runtime/server/global-sync/utils.tshard-codesreasoning: falsewhile mappingvariantscorrectly one line below.Two notes for #55100:
reasoningfrom the models.dev catalog fixes the catalog case, but models that only exist via user config (self-hosted / gateway providers, and the new "generate reasoning variants from reasoning options" behavior) have no catalog entry to read from. Would it be worth deriving the flag ascapabilities.reasoning === true || model.variants.length > 0in the app mapper as a belt-and-braces fallback, so config-declared reasoning controls map totrueas well?Model.Capabilities.reasoningis going to be an explicit field, it would be good if the server also reported it for config-declared reasoning controls, not just for catalog flags — that is the difference between "listed in models.dev" and "actually able to reason" for a lot of local / proxied models.cookiecathub commentedon Oct 17, 2026
Confirmed on macOS (desktop app 2.0.31, Darwin 27.0.0 arm64) with the NVIDIA provider (
integrate.api.nvidia.com,@opencode/ai/providers/openai-compatible).GET /api/modelreturns 41 NVIDIA models; 0/41 includecapabilities.reasoning, but they do includevariants(e.g.none/thinking).nvidia/z-ai/glm-5.3→capabilities:{tools,input,output,temperature},variants:[none,thinking]. The tooltip shows "No reasoning".opencode run -m "nvidia/nvidia/nemotron-3.5-lightning-30b-a3b#thinking"returned areasoningblock (reasoningField: reasoning_content), and a session withnvidia/z-ai/glm-5.3stored 61 reasoning tokens.app.asar,out/renderer/assets/main-*.js) buildscapabilities:{temperature:!1, reasoning:!1, ...}hard-coded, so the tooltip (capabilities.reasoning) always reports "No reasoning". Suggested fix: derive reasoning support fromvariants.Environment: opencode 2.0.31, Darwin 27.0.0 (darwin arm64), web/desktop client, shell
/bin/zsh, no relevant local plugins.1 remaining item
yukunmeng commentedon Oct 20, 2026
Same root cause, still reproducible on 2.0.32 (Windows x64, Chinese UI). Additional measurements that may help to close this out:
GET /api/modelon a live 2.0.32 server returns 47 models: 0 / 47 havecapabilities.reasoning, 0 / 47 have a top-levelreasoningfield, and all 47 havecapabilitiespresent — so the tooltip'smodel.reasoningfallback is unreachable.packages/core/src/models-dev.ts,modelInfo()buildscapabilitiesfromtools/input/output/temperatureonly; models.dev's top-levelreasoning: trueandreasoning_optionsare only converted intovariantsbysupports(), never copied intocapabilities.opencode/space-bunny-free#maxandopencode/minimax-m3.1-flash-free#maxreporttokens.reasoningof 71263 and 16437 respectively.One config-related detail, since the issue mentions the config path:
Config.Model.Capabilities.reasoningin the config schema is the v2 object shape (supported/effort/budget/toggle) and is only used to build reasoning variants, while the runtimeModel.Capabilitiestype has noreasoningfield at all. Definingproviders.<id>.models.<model>.capabilities.reasoningtherefore cannot change what the tooltip reads. It also replaces (rather than merges with) the models.dev record: the model name falls back to the raw id,limitfalls back to{context: 200000, output: 32000}, thevideomodality is lost andcompatibility.reasoningFieldis dropped.Full details in the comment on #53520.
zhengkaics commentedon Oct 20, 2026
Thanks for measuring this on 2.0.32 — the
0/47result matches the root cause. #55100 carries the catalog'sreasoningflag through toModel.Capabilities, and the app falls back tocompatibility.reasoningField/ non-nonevariants when the catalog is silent.One correction on the config path: in #55100 the config's rich
capabilities.reasoningis reduced to that runtime boolean inmergeCapabilities, so{ "supported": true }does reach the tooltip. The metadata loss you saw (name / limit / modalities / compatibility falling back to defaults) is separate from the reasoning flag and probably worth its own issue.cripperzprodigy commentedon Oct 23, 2026
Independent repro on v2.1.3 (Linux, self-hosted server) — and a config-path data point
Confirming this is still present on v2.1.3 (latest as of 2026-10-23), with a server where the models are declared in
opencode.json(custom providers) and the config path is exercised:opencode-go: 28 models → 0/28 havecapabilities.reasoning; 27/28 havevariants(e.g.deepseek-v4.1-flash→variants: [low, high, max]).GET /api/modelforopencode-go/deepseek-v4.1-flashreturnscapabilities: {tools,input,output,temperature}— noreasoning, no top-levelreasoning.Config.Model.Capabilitiesschema does havereasoning(config side), andproviders.<id>.models.<id>.capabilities.reasoningvalidates and is retained inopencode debug config— butModel.Capabilities(API response) has no such field, so it is stripped before the client sees it.variantsto models does not change the tooltip (the tooltip readscapabilities.reasoning).So the client has no signal at all, regardless of config. This matches the
modelInfo()/mergeCapabilitiesanalysis above; #55100 ("fix(core): expose model reasoning capability", still OPEN targetingdev) looks like the correct fix. Adding this data point so the config path is covered by the test matrix: after #55100,{"capabilities":{"reasoning":{"supported":true}}}fromopencode.jsonshould reach the tooltip.Repro environment: opencode v2.1.3,
opencode serve --service, 149 models across 10 providers, 116 with variants.MercuryOoO commentedon Oct 25, 2026
Fresh repro on Desktop 2.1.4 (
latestchannel, Windows 11 x64, 2026-10-25) — still broken on the newest build.Measurements (
GET /api/model, live server):capabilities.reasoning, 0/71 have a top-levelreasoning; 71/71 havecapabilitiespresent → the tooltip'smodel.reasoningfallback stays unreachablecompatibility.reasoningField, 28/71 have effort variants (low/medium/high/…) — reasoning works at runtime, only the capability flag is missingConfig-path data point (custom providers declared in
opencode.jsonc):compatibility.reasoningFieldmerges correctly → visible for 14/14 models in/api/modelcapabilities.reasoningnever reaches the store: the config capabilities merge keeps onlytools/input/output/temperature, so the declared effort is dropped andvariantsremain whatever the package fallback generatedBundle check confirms the picker still branches on
model.capabilities.reasoning(app.asar,model.tooltip.reasoning.*).Hoping #55100 lands — the config path above should be covered by it.
fix(core): expose model reasoning capability
fix(app): derive reasoning from variants and reasoningField when the …
maksiorlo commentedon Oct 26, 2026
Still reproducible on OpenCode Desktop 2.1.4 (macOS 26 / Darwin 25.6.0, Apple Silicon), and it affects the free OpenCode Zen model too, not just V2 paid ones.
What the UI shows
Model picker → free model
opencode/deepseek-v4.1-flash-free→ tooltip:Reasoning actually works on the free tier
The catalog says
reasoning: truewithtoggle + effort [low, high, max]:providers/opencode/models/deepseek-v4.1-flash-free.tomlon models.devkv→models-dev:catalog)Empirical check through the bundled CLI (same endpoint,
--format json --thinking), one prompt: "Do not use any tools. Compute 87341 * 92837 in your head and reply with just the number."#low#high#maxThe stream contains a real
reasoningpart withmetadata.reasoningField: "reasoning_content", e.g. on#maxthe model decomposed the multiplication, cross-checked it two ways, and answered8,108,476,417. No "variant unavailable" error for any variant.Same root cause in current
devpackages/app/src/runtime/server/global-sync/utils.tsstill hardcodes:So this is display-only: the request carries
reasoningEffort, the response carriesreasoning_content, only the tooltip lies. Derivingreasoningfrom the catalog flag (or fromvariants.length > 0) would fix the free model as a side effect.opencodejr commentedon Oct 28, 2026
Thanks for the detailed report. I was able to confirm this.
On 2.0.11, I started
opencode serveand queriedGET /api/model?all=true. No model in the catalog hascapabilities.reasoning(or a top-levelreasoning). That includes models that offerreasoningEffortvariants and are markedreasoning: truein models.dev, such as DeepSeek Flash. On top of that, the app's model mapper (packages/app/src/runtime/server/global-sync/utils.ts) hard-codescapabilities.reasoning: false, so the tooltip can only ever show "No reasoning". I didn't open the desktop app itself; the API output and that code already settle it.It still happens on the latest
dev(fa1260d). The script below reproduces the API side.Open PR #55100 addresses this. The later duplicates (#53520, #54229, #56317, #58901) were closed in favour of this issue.
Reproduction script (repro.sh)
Run it:
bash repro.shfix(core): expose model reasoning capability