Skip to content

Free Zen models with reasoning=true show zero thinking variants (reasoning_options=[] blocks fallback) #49013

Open

Description

@Gopendranath

Summary

Several free OpenCode Zen models advertise reasoning = true in models.dev but expose zero thinking variants in OpenCode (/models picker + variant_cycle). Users cannot select low / medium / high thinking effort for these models.

Affected models (provider = opencode)

Verified against https://models.dev/api.json (opencode provider) and Zen docs (2026-09-13):

Model ID reasoning reasoning_options Variants shown
big-pickle true [] none (bug)
mimo-v2.5-free true [] none (bug)
nemotron-3-ultra-free true [] none (bug)
nemotron-3.5-lightning-free true [] none (bug)
minimax-m2.5-free true [] none (bug)
ling-3.0-flash-fin-free true [{type="toggle"}] low/medium/high via fallback (OK)
muse-spark-1.3-contributor-free true [{type="effort", values=[minimal,low,medium,high,xhigh]}] 5 variants (OK)
deepseek-v4-flash-free true [{type="effort", values=[low,high,max]}] OK
kimi-k2.5-free / glm-5-free / qwen3.6-plus-free true [{type="toggle"}] (+budget for qwen) none — fallback denylisted (see below)

Zen endpoint table serves 5/6 advertised free models via https://opencode.ai/zen/v1/chat/completions (@ai-sdk/openai-compatible); only muse-spark-1.3-contributor-free uses .../v1/responses (@ai-sdk/openai).

Expected vs actual

  • Expected: models with reasoning = true served over OpenAI-compatible API expose generic low / medium / high variants (same as ling-3.0-flash-fin-free does today).
  • Actual: models with reasoning_options = [] show no variants at all.

Root cause (two layers)

1. reasoningVariants() returns {} instead of undefined for []

packages/opencode/src/provider/transform.ts:1704:

export function reasoningVariants(model: ModelsDev.Model, target: Provider.Model) {
  const options = model.reasoning_options
  if (options === undefined) return
  if (options.length === 0) return {}
  // ...
}

2. ?? fallback never runs on {}

packages/opencode/src/provider/provider.ts:1314:

const variants = ProviderTransform.reasoningVariants(model, base) ?? ProviderTransform.variants(base)

{} is not nullish, so generic variants(base) never runs for the [] models. Result: zero variants despite reasoning=true.

3. Hardcoded denylist in variants() also returns {}

packages/opencode/src/provider/transform.ts:777:

if (
  id.includes("deepseek-chat") ||
  id.includes("deepseek-reasoner") ||
  id.includes("deepseek-r1") ||
  id.includes("deepseek-v3") ||
  id.includes("minimax") ||
  (id.includes("glm") && !glm52) ||
  id.includes("kimi") ||
  id.includes("k2p") ||
  id.includes("qwen") ||
  id.includes("big-pickle")
)
  return {}

This blocks fallback for big-pickle, kimi-*, qwen-*, glm-* (non-5.2), minimax-* even when reasoning=true. For toggle-only models on @ai-sdk/openai-compatible, reasoningToggle() returns {} -> nonEmptyVariants({}) -> undefined -> fallback -> denylist {}. Net: no variants.

Models.dev evidence

Raw TOMLs (anomalyco/models.dev@dev:providers/opencode/models/):

  • mimo-v2.5-free.toml: reasoning = true, reasoning_options = []
  • nemotron-3-ultra-free.toml: reasoning = true, reasoning_options = []
  • nemotron-3.5-lightning-free.toml: reasoning_options = []
  • big-pickle.toml: reasoning = true, reasoning_options = []
  • minimax-m2.5-free.toml: reasoning = true, reasoning_options = []
  • ling-3.0-flash-fin-free.toml: reasoning_options = [{ type = "toggle" }] (works via fallback)
  • muse-spark-1.3-contributor-free.toml: reasoning_options = [{ type = "effort", values = [...] }] + [provider] npm = "@ai-sdk/openai" (works)

Suggested fix (either side, ideally both)

  • Option A (code, anomalyco/opencode): treat empty variants as missing. E.g. reasoningVariants() returns undefined for [], or change call site to fall back when Object.keys(variants).length === 0.
  • Option B (data, anomalyco/models.dev): give free OpenAI-compatible reasoning models real effort options, e.g. reasoning_options = [{ type = "effort", values = ["low", "medium", "high"] }], or document that [] means fixed always-on reasoning with no knob.
  • Re-evaluate denylist entry for big-pickle if reasoningEffort passthrough works on Zen chat-completions endpoint.

Workaround

Custom variants in opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "opencode": {
      "models": {
        "mimo-v2.5-free": {
          "variants": {
            "low": { "reasoningEffort": "low" },
            "medium": { "reasoningEffort": "medium" },
            "high": { "reasoningEffort": "high" }
          }
        }
      }
    }
  }
}

Environment

  • opencode v2.0.3 (Windows, C:\Users\baban\.bun\bin\opencode.exe)
  • Zen docs last updated 2026-09-13, /zen/v1/models checked 2026-09-14
  • models.dev opencode provider, 102 models

Activity

  1. github-actions commented on Sep 14, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    If your report adds new findings (the denylist layer detail, nemotron models, and the specific free-tier scope), it may still be worth keeping open as an elaboration — but please coordinate with those issues to avoid duplicate fixes.

  2. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the detailed write-up!

    To restate the request: OpenCode Zen models that models.dev marks as reasoning-capable but that declare no reasoning controls (big-pickle, mimo-v2.5-free, nemotron-*-free, minimax-m2.5-free) should still offer low / medium / high variants.

    Some notes from the current v2 code:

    • The code you quote (packages/opencode/src/provider/transform.ts, the ?? ProviderTransform.variants(base) fallback, the big-pickle/kimi/qwen denylist) is from v1. It doesn't exist in v2 (2.0.3 or later). In v2, variants are built in packages/core/src/variant.ts, and only from the controls a model declares in models.dev.
    • A model with no declared control gets no variants on purpose. There is no generic fallback in v2 anymore, because it offered effort levels that some models reject (see opencode-go: spurious low/medium/high reasoning variants for MiMo V2.5 / Hy3; inconsistent with Kimi/Grok #43543).
    • So for these models, the most likely route is declaring real effort or toggle options in models.dev, where the Zen endpoint actually accepts them.

    The maintainers will consider this. Related issues:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions