Skip to content

bug(provider): thinking/reasoning options not gated by model.capabilities.reasoning #34282

Closed as not planned

Description

@licat2023

Description

Thinking/reasoning provider options aren't gated by model.capabilities.reasoning. This breaks non-reasoning models, most visibly when a subagent uses one:

  1. A non-thinking model's normal output gets stored as reasoning ("thinking") instead of normal assistant text.
  2. Trying to opt out via agent options: { thinking: { type: "disable" } } passes the field straight through to the provider, and the non-thinking model's API rejects the unknown thinking parameter with an error.

Root cause: ProviderTransform.options() in packages/opencode/src/provider/transform.ts force-injects thinking/reasoning params from model id/provider substrings without checking capabilities.reasoning, e.g.:

  • ~L1112 zai/zhipuai + @ai-sdk/openai-compatible → thinking: { type: "enabled" }
  • ~L1144 kimi-k2* + anthropic → thinking: { type: "enabled", budgetTokens }
  • ~L1174 id contains gpt-5 → reasoningEffort: "medium"

Compare the google (~L1127) and alibaba-cn (~L1162) branches, which do gate on capabilities.reasoning. The stream side (session/llm/ai-sdk.ts, session/processor.ts) and the option merge (session/llm/request.ts ~L91 → providerOptions()) also don't filter by capability, so the disable workaround in (2) leaks straight to the provider.

Suggested fix: gate every thinking/reasoning branch in options() on model.capabilities.reasoning (like the google/alibaba branches), and/or strip thinking/reasoning/reasoningEffort/thinkingConfig/enable_thinking in providerOptions() when !capabilities.reasoning.

OpenCode version

1.17.11 (dev branch @ dfeb1b5, built from source)

Steps to reproduce

  1. Configure a subagent using a non-reasoning model whose id/provider matches one of the ungated branches (e.g. a non-thinking zai/zhipuai model via @ai-sdk/openai-compatible).
  2. Invoke it via the task tool — its normal output is recorded as thinking.
  3. Add options: { thinking: { type: "disable" } } to that agent to try to disable it.
  4. The provider rejects the request because the non-thinking model has no thinking parameter.

Operating System

Windows 11 (10.0.26200)

Activity

  1. github-actions commented on Jun 28, 2026

    @github-actions
    Contributor

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

  2. a-yeyang commented on Aug 1, 2026

    @a-yeyang

    Still reproduces on current dev (v1.18.11, 0eac096). Confirming the gpt-5 branch from bullet 3 with a minimal runnable case, and refreshing the line references since they've drifted from the ~L1174 in the report.

    The branch is now at packages/opencode/src/provider/transform.ts:1278:

    if (input.model.api.id.includes("gpt-5") && !input.model.api.id.includes("gpt-5-chat")) {
      if (!input.model.api.id.includes("gpt-5-pro")) {
        result["reasoningEffort"] = "medium"        // :1280 — never checks capabilities.reasoning
        // reasoningSummary / include below ARE npm-gated, but reasoningEffort is not
      }
      if (
        input.model.api.id.includes("gpt-5.") &&
        !input.model.api.id.includes("codex") &&
        !input.model.api.id.includes("-chat") &&
        input.model.providerID !== "azure"
      ) {
        result["textVerbosity"] = "low"             // :1302 — same, no capability check
      }
    }

    Repro — a self-hosted OpenAI-compatible gateway serving a model whose id contains gpt-5, with capabilities.reasoning: false:

    capabilities.reasoning === false for every model below
    api.id="gpt-5.1"   => options() => {"reasoningEffort":"medium","textVerbosity":"low"}
    api.id="gpt-5.2"   => options() => {"reasoningEffort":"medium","textVerbosity":"low"}
    api.id="gpt-5"     => options() => {"reasoningEffort":"medium"}
    

    Downstream this isn't inert: @ai-sdk/openai-compatible's getArgs() maps reasoningEffort → reasoning_effort and textVerbosity → verbosity straight onto the /chat/completions body, so a non-reasoning model on a custom gateway ends up sending params it never advertised.

    One case-sensitivity note for whoever picks this up: the match is includes("gpt-5") on the raw api.id (never lowercased, unlike most other id checks in this file), so lowercase LiteLLM-style aliases like gpt-5.1 trigger it while an uppercase alias like GPT-5.5 does not — worth normalizing alongside the capability gate.

    Not touching the fix since this is assigned — just adding a reproducible anchor at current HEAD. Happy to help test a gate patch.

  3. github-actions commented on Oct 1, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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