Skip to content

opencode-go shows no thinking-strength control for deepseek-v4-flash despite models.dev declaring reasoning_options #1858

Description

@Astro-Han

What happened

On the opencode-go provider, deepseek-v4-flash has no thinking-strength control in the model picker. The model is reasoning-capable in Maka's own metadata (capabilities.reasoning === true), and models.dev declares reasoning_options for it: [{ "type": "effort", "values": ["high", "max"] }] (api.json fetched 2026-08-02).

The same model on the native deepseek provider does show the control with high/max. Expected behavior is that opencode-go exposes the upstream-declared effort options, or states explicitly that this path does not support a knob.

How to reproduce

  1. Add an opencode-go connection and start a new chat with it.
  2. Select deepseek-v4-flash. No thinking-strength control appears.
  3. Repeat with the native deepseek provider and the same model. The control appears with high / max.

No configuration or code change is needed; this is the shipped behavior on main @ 92cde3d.

Environment

  • Maka commit: 92cde3d (main)
  • Surface: Desktop (renderer model picker + runtime)
  • models.dev api.json fetched 2026-08-02

Root cause

  1. scripts/sync-model-metadata.mjs toMetadata (line 117) reads model.reasoning (lines 124/137) but never reads model.reasoning_options, so packages/core/src/model-metadata.generated.ts can never carry thinking options. Running the script against today's api.json produces 0 thinkingOptions in the output while the input has 10 opencode-go models with non-empty reasoning_options.

  2. Thinking options are declared by hand per provider path in STATIC_MODEL_METADATA (packages/core/src/model-metadata.ts), and only a few paths are covered (native deepseek at line 434). There are no entries for opencode, opencode-go, or opencode-free, so thinkingOptionsForModel('opencode-go', 'deepseek-v4-flash') is undefined, thinkingVariantsForModel(...) returns [], and the UI hides the knob (apps/desktop/src/renderer/use-shell-chat-model.ts:125,141).

  3. The runtime wire is a separate manual switch. buildProviderOptions (packages/runtime/src/model-factory.ts) has no case for the opencode paths and falls through to default: return {}. Verified: buildProviderOptions({providerType:'opencode-go'}, 'deepseek-v4-flash', 'high') returns {}. Even a forced knob would send no parameter.

The problem is systemic, not an opencode-go typo: 41 of 44 providers with reasoning-capable models have at least one reasoning model without a thinking declaration (opencode-go 13/13, opencode 55/55, openrouter 201/205, vercel 142/143, ...). In the current data model, "model has no knob" and "we forgot to declare it" are indistinguishable, and both fail silently.

Suggested direction

  • Pass reasoning_options through scripts/sync-model-metadata.mjs (normalize effort / toggle / budget_tokens), so every plan inherits upstream-declared effort options on sync.
  • Replace the manual metadata plus runtime-switch pairing with a single per-path thinking-wire declaration that drives both thinkingVariantsForModel and buildProviderOptions.
  • Classify every reasoning model as exactly one of: declared and wired, explicitly opaque (with reason), or non-reasoning. No "forgot" state.
  • Enforce the above in packages/core/src/__tests__/provider-catalog-contract.test.ts across PROVIDER_REGISTRY, so a new plan cannot regress silently.

Logs, screenshots, or additional context

  • Verification against today's api.json: deepseek-v4-flash has reasoning_options: [{"type":"effort","values":["high","max"]}] on opencode-go (no toggle), on opencode, and on native deepseek (which adds toggle).
  • opencode-go has 23 models, 13 reasoning-capable, 10 with non-empty reasoning_options (e.g. glm-5.2, hy3, grok-4.5, kimi-k3, gpt-5.6-luna, qwen3.x toggle + budget_tokens).
  • OpenCode Zen docs serve deepseek-v4-flash over OpenAI-compatible chat/completions. Actual reasoning_effort passthrough on https://opencode.ai/zen/go/v1 is not yet verified against the live endpoint (needs a key); this decides whether opencode-go is wired or first declared opaque with a documented reason.
  • Side observation: today's api.json names the model "DeepSeek V4 Flash (New)" while the repo snapshot says "DeepSeek V4 Flash". The snapshot is stale relative to upstream, which is expected since sync is periodic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions