anomalyco / anomalyco/opencode
core: flatten reasoningEffort settings for custom OpenAI-compatible providers
@nexxeln is already working on this.
Since Sep 9, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
With a custom OpenAI-compatible provider (local mlx-serve) and a user-defined variants entry that sets reasoningEffort, the variant is selected and sent (variant:"high" appears on every assistant message, reasoning_effort is in the request body), and the server returns reasoning — but no reasoning part ever reaches the session/TUI. Model: compatibility.reasoningField: "reasoning_content" from opencode.json appears not to be applied to the model definition, so reasoning streamed by the provider is silently dropped.
Environment
- opencode version: 0.0.0-beta-19378 (
opencode2 --version) - OS: Darwin 25.6.0 (macOS, arm64)
- Terminal: iTerm.app (TERM=xterm-256color, COLORTERM=truecolor)
- Shell: /bin/zsh
- Install/channel: beta (
@opencode/clinpm bundle,opencode2) - Active plugins: none found in config
- Provider: custom
mlx-serve(package: "@opencode/ai/providers/openai-compatible", baseURLhttp://127.0.0.1:11234/v1), model Qwen reasoning hybrid model, server exposescapabilities: ["chat","tool_use","streaming","vision","reasoning","json_schema"]
Reproduction
- Configure a custom OpenAI-compatible provider in
~/.config/opencode/opencode.jsonwith a model that definescompatibility: { "reasoningField": "reasoning_content" }andvariants: [{ "id": "high", "settings": { "reasoningEffort": "high" } }]. - Select the model in the TUI and set the variant to
high(Ctrl+T). - Ask a question that triggers reasoning, e.g. a simple multi-step arithmetic word problem.
- Observe: no thinking block is rendered in the TUI.
- Inspect the session DB (
session_messagetable): every assistant message hasmodel.variant: "high"but contains onlytext/toolcontent parts — zeroreasoningparts (0 occurrences across 538 assistant messages in the session). - Directly against the same server with the equivalent request (
{"reasoning_effort":"high","stream":true}): SSE deltas containdelta.reasoning_content(≈500 chars streamed), and non-streaming responses returnmessage.reasoning_contentwithusage.completion_tokens_details.reasoning_tokenspopulated (203/293/223 for low/medium/high; 0 for none/absent).
Expected Behavior
When the variant is applied, the request sends reasoning_effort, and the server streams reasoning_content deltas, OpenCode should render/emit a reasoning part on the assistant message (as it does for built-in providers).
Actual Behavior
Reasoning output is silently dropped: no thinking appears in the TUI and no reasoning parts are stored in the session. No error is logged.
Additional Context
Config snippet (redacted, structurally verbatim):
{
"model": "mlx-serve/Qwen3.8-Flash-Next-Uncensored-MLX-Serve-4bit",
"providers": {
"mlx-serve": {
"package": "@opencode/ai/providers/openai-compatible",
"settings": { "baseURL": "http://127.0.0.1:11234/v1" },
"models": {
"Qwen3.8-Flash-Next-Uncensored-MLX-Serve-4bit": {
"compatibility": { "reasoningField": "reasoning_content" },
"variants": [
{ "id": "none", "settings": { "reasoningEffort": "none" } },
{ "id": "low", "settings": { "reasoningEffort": "low" } },
{ "id": "medium", "settings": { "reasoningEffort": "medium" } },
{ "id": "high", "settings": { "reasoningEffort": "high" } }
]
}
}
}
}
}
Investigation notes from the bundle (opencode2.exe strings, openai-chat protocol): the stream step reads reasoning via a helper that checks a set of fields including the configured reasoningField ({[key]: val} — an object literal, so the value reasoning_content is the checked key, undefined is never in the set, and the loop takes the first match). The non-streaming path likewise falls back to message.reasoning_content ?? message.reasoning. That implies dropping compatibility from the config would not break the reasoning_content case, so something else in the beta-19378 pipeline loses either model.compatibility from user config or the reasoning events between provider stream and session storage — I could not pinpoint the exact line statically.
Related issues (same family, not identical): #35283 (delta schema strips unknown reasoning fields — but that is about the field name reasoning, not user config), #41294/#20815 (variant reasoningEffort dropped from request body — closed; here the request body is correct), #42876 (variant body-level fields dropped before the request).
Frequency: consistent, every message, across all sessions using this custom provider.
Workarounds tried: explicitly setting compatibility.reasoningField: "reasoning_content" (no effect observed); toggling variant via Ctrl+T (variant metadata confirms high on every message). Untested lead: the model card advertises "capabilities": { "tools": true, "input": [...], "output": [...] } without a reasoning capability — the built-in catalog's variant generator (DQ) keys off capabilities.supports.reasoning_effort, so it is plausible that user-config models with variants are handled by a different code path than catalog models.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.