Symptom
The previous post got the CommandCode GOAT plan hooked up, and the models chat fine, but there was one detail I never looked at closely: deepseek-v4-flash won’t expose thinking levels.
With the opencode go plan, the same batch of models could switch thinking levels just fine; on goat they were all gone. My first reaction was “the goat plan doesn’t support it” — but after digging in, this had nothing to do with the plan at all. It was a missing piece of config.
TL;DR
Tweak the config file once more: for every model that needs selectable thinking depth, just add one line:"reasoning": true
and you’re done.
For example
"deepseek/deepseek-v4-flash": {
"name": "DeepSeek V4 Flash (latest)",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
Oh, and by the way, today I also turned the GOAT plan usage estimator into a live table:

Project is at: https://github.com/mousebomb/opencode-go-limits
Trimmed the config a bit
{
"$schema": "https://opencode.ai/config.json",
"permission": {
},
"mcp": {
},
"provider": {
"goat": {
"npm": "@ai-sdk/openai-compatible",
"name": "goat",
"options": {
"baseURL": "https://api.commandcode.ai/provider/v1",
"headers": {}
},
"models": {
"deepseek/deepseek-v4-flash-vision-exp": {
"name": "DeepSeek V4 Flash Vision (exp)",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"Qwen/Qwen3.8-Flash": {
"name": "Qwen 3.8 Flash",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"stepfun/Step-3.7-Flash": {
"name": "Step 3.7 Flash",
"limit": {
"context": 256000,
"output": 262144
},
"reasoning": true
},
"poolside/laguna-s-2.1-free": {
"name": "Laguna S 2.1",
"limit": {
"context": 256000,
"output": 262144
},
"reasoning": true
},
"meta/muse-spark-1.2": {
"name": "Muse Spark 1.2",
"limit": {
"context": 1048576,
"output": 262144
},
"reasoning": true
},
"meta/muse-spark-1.2-contributor": {
"name": "Muse Spark 1.2 Contributor",
"limit": {
"context": 1048576,
"output": 262144
},
"reasoning": true
},
"xai/grok-4.5": {
"name": "Grok 4.5",
"limit": {
"context": 500000,
"output": 262144
},
"reasoning": true
},
"xai/grok-4.6": {
"name": "Grok 4.6",
"limit": {
"context": 500000,
"output": 262144
},
"reasoning": true
}
}
},
"goat-zdr": {
"npm": "@ai-sdk/openai-compatible",
"name": "goat-zdr",
"options": {
"baseURL": "https://api.commandcode.ai/provider/v1",
"headers": {
"x-cmd-zdr": "1"
}
},
"models": {
"deepseek/deepseek-v4-pro": {
"name": "DeepSeek V4 Pro (latest)",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"deepseek/deepseek-v4-flash": {
"name": "DeepSeek V4 Flash (latest)",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"deepseek/deepseek-v4-flash-fast": {
"name": "DeepSeek V4 Flash Fast",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"moonshotai/Kimi-K3": {
"name": "Kimi K3",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"moonshotai/Kimi-K2.7-Code": {
"name": "Kimi K2.7 Code",
"limit": {
"context": 256000,
"output": 262144
},
"reasoning": true
},
"moonshotai/Kimi-K2.7-Code-Highspeed": {
"name": "Kimi K2.7 Code HighSpeed",
"limit": {
"context": 262000,
"output": 262144
},
"reasoning": true
},
"moonshotai/Kimi-K2.6": {
"name": "Kimi K2.6",
"limit": {
"context": 256000,
"output": 262144
},
"reasoning": true
},
"moonshotai/Kimi-K2.5": {
"name": "Kimi K2.5",
"limit": {
"context": 256000,
"output": 262144
},
"reasoning": true
},
"z-ai/glm-5.3-flash": {
"name": "GLM-5.3 Flash",
"limit": {
"context": 1048576,
"output": 262144
},
"reasoning": true
},
"zai-org/GLM-5.3": {
"name": "GLM-5.3",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"zai-org/GLM-5.2": {
"name": "GLM-5.2",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"zai-org/GLM-5.2-Fast": {
"name": "GLM-5.2 Fast",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"zai-org/GLM-5.1": {
"name": "GLM-5.1",
"limit": {
"context": 200000,
"output": 262144
},
"reasoning": true
},
"zai-org/GLM-5": {
"name": "GLM-5",
"limit": {
"context": 200000,
"output": 262144
},
"reasoning": true
},
"MiniMaxAI/MiniMax-M3": {
"name": "MiniMax M3",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"MiniMaxAI/MiniMax-M2.7": {
"name": "MiniMax M2.7",
"limit": {
"context": 200000,
"output": 262144
},
"reasoning": true
},
"MiniMaxAI/MiniMax-M2.5": {
"name": "MiniMax M2.5",
"limit": {
"context": 200000,
"output": 262144
},
"reasoning": true
},
"xiaomi/mimo-v2.5-pro": {
"name": "MiMo V2.5 Pro",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"xiaomi/mimo-v2.5": {
"name": "MiMo V2.5",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"Qwen/Qwen3.8-Max": {
"name": "Qwen 3.8 Max",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"Qwen/Qwen3.8-27B": {
"name": "Qwen 3.8 27B",
"limit": {
"context": 262144,
"output": 262144
},
"reasoning": true
},
"Qwen/Qwen3.7-Max": {
"name": "Qwen 3.7 Max",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"Qwen/Qwen3.7-Plus": {
"name": "Qwen 3.7 Plus",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"Qwen/Qwen3.7-Flash": {
"name": "Qwen 3.7 Flash",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"Qwen/Qwen3.6-Max-Preview": {
"name": "Qwen 3.6 Max Preview",
"limit": {
"context": 200000,
"output": 262144
},
"reasoning": true
},
"Qwen/Qwen3.6-Plus": {
"name": "Qwen 3.6 Plus",
"limit": {
"context": 200000,
"output": 262144
},
"reasoning": true
},
"stepfun/Step-3.5-Flash": {
"name": "Step 3.5 Flash",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"tencent/hy3-paid": {
"name": "Tencent Hy3",
"limit": {
"context": 262144,
"output": 262144
},
"reasoning": true
},
"tencent/hy4-preview": {
"name": "Tencent Hy4 Preview",
"limit": {
"context": 1048576,
"output": 262144
},
"reasoning": true
},
"nvidia/nemotron-3-ultra-550b-a55b": {
"name": "Nemotron 3 Ultra",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"thinkingmachines/inkling": {
"name": "Inkling",
"limit": {
"context": 256000,
"output": 262144
},
"reasoning": true
},
"thinkingmachines/inkling-small": {
"name": "Inkling Small",
"limit": {
"context": 1000000,
"output": 262144
},
"reasoning": true
},
"gpt-5.6-sol": {
"name": "GPT-5.6 Sol",
"limit": {
"context": 1050000,
"output": 262144
},
"reasoning": true
}
}
}
},
"plugin": [
]
}
Root cause: custom providers lack a reasoning capability declaration
The entry point where opencode generates “thinking levels” (variants) for a model is the variants() function, and the very first gatekeeper is:
if (!model.capabilities.reasoning) return {} // 没声明推理能力 → 直接不给档位
Built-in providers (opencode-go, official deepseek, etc.) get their model metadata from models.dev, which ships with reasoning: true and reasoning_options out of the box, so thinking levels show up.
goat, on the other hand, is a custom provider (@ai-sdk/openai-compatible), and all its models were hand-written by me in opencode.json. At the time I only wrote name and limit, not reasoning: true → the capability declaration defaults to false → variants() returns empty right away, and of course the TUI doesn’t show thinking levels.
It’s crystal clear when you read the code (packages/opencode/src/provider/transform.ts):
case "@ai-sdk/openai-compatible":
const efforts = [...WIDELY_SUPPORTED_EFFORTS] // ["low","medium","high"]
if (model.api.id.toLowerCase().includes("deepseek-v4")) {
efforts.push("max") // deepseek-v4 额外加 max
}
return Object.fromEntries(efforts.map(e => [e, { reasoningEffort: e }]))
In other words: for @ai-sdk/openai-compatible models, as long as reasoning: true is declared, opencode will assign thinking levels automatically based on the model ID — the deepseek-v4 series gets low/medium/high/max.
The GOAT gateway itself does support effort
I verified the gateway layer on the way: the reasoning_effort parameter is genuinely accepted and forwarded, and in practice deepseek-v4-flash really does produce different reasoning token counts at different effort levels. The level enum is low|medium|high|xhigh (none errors out), and max passes too.
Conclusion locked in: the capability is there, opencode just was never told this model can reason.
The fix: add reasoning: true to the models
Just add the declaration to the model entries under the goat and goat-zdr providers:
"deepseek/deepseek-v4-flash": {
"name": "DeepSeek V4 Flash (latest)",
"limit": { "context": 1000000, "output": 262144 },
"reasoning": true
}
Restart after that, and opencode run -m goat/deepseek/deepseek-v4-flash --variant max passes in practice — the level really takes effect.
A follow-up question: is it safe to mark every model reasoning:true?
After fixing deepseek, a thought occurred to me: instead of judging model by model which ones should be marked, why not mark them all true and let opencode’s built-in model logic make the final call (it has its own internal blacklist of “which models should get levels”). But is that safe?
Reading the source: before handing out levels, variants() has a hardcoded blacklist, and any matching model just gets return {}:
if (
id.includes("deepseek-chat") || id.includes("deepseek-r1") ||
id.includes("deepseek-v3") ||
id.includes("minimax") || (id.includes("glm") && !glm52) ||
id.includes("kimi") || id.includes("qwen")
) return {}
That means kimi, qwen, glm (not 5.2), minimax (not M3) and friends won’t get thinking levels even with reasoning:true — the upstream simply doesn’t honor effort control (in practice, sending Kimi-K3 the high level barely changes its reasoning tokens, which confirms the blacklist makes sense).
The remaining question: for models marked true but blocked by the blacklist, will opencode sneak a default effort into the request body? Packet capture put that worry to rest — after marking Qwen3.8-Flash (blacklisted) as reasoning:true, the outgoing request body contains no reasoning_effort field at all, and the conversation behaves exactly as before. Compare that with non-blacklisted deepseek-v4-flash, where --variant high really does put reasoning_effort: high in the request body.
So I confidently marked all 74 models under goat and goat-zdr as reasoning: true, letting opencode’s mechanism handle the rest. After refactoring the config, /models works fine and every model chats normally in practice.
Final results comparison
| Model | Thinking levels that appear in the TUI after marking reasoning:true |
|---|---|
| deepseek-v4 series (pro/flash/fast/vision) | low / medium / high / max |
| mimo-v2.5, stepfun, tencent, nemotron, inkling, gpt-5.6-sol, muse, grok, etc. | low / medium / high |
| GLM-5.2 | high / max (special-cased) |
| MiniMax-M3 | thinking on / off (toggle special case) |
| Kimi series, Qwen series, GLM-5/5.1/5.3, MiniMax-M2.x | no levels (hardcoded blacklist, upstream doesn’t support effort) |
Takeaways
- For custom providers (openai-compatible), model levels are entirely up to your own declarations.
reasoning: trueis a necessary but not sufficient condition for getting levels — whether levels appear at all, and which ones, is decided a second time by opencode’s built-in model logic (blacklist included). - Marking blacklisted models as reasoning:true has no side effects: it only affects the “can thinking levels appear” UI switch, and no default effort parameter gets injected into the request body. Mark them all with confidence.
- After fixing the config, remember to restart opencode (config is only loaded at startup, no hot reload), then select a model in
/modelsand press Tab to cycle through the levels.