Skip to content

[BUG] Model options (reasoning/thinking) not forwarded to API for @ai-sdk/openai-compatible providers in headless mode #27361

Closed

Description

@R8CEH

Description

When using a custom @ai-sdk/openai-compatible provider with model-specific options (e.g. reasoning.effort for OpenRouter), the options are silently ignored and never sent to the API. This affects all headless usage (opencode run --format json) and makes it impossible to enable extended thinking/reasoning for any custom provider model.

Environment

  • OpenCode version: 1.14.48
  • OS: Ubuntu 24.04 LTS
  • Mode: headless (opencode run --format json)
  • Provider: OpenRouter via @ai-sdk/openai-compatible
  • Model: qwen/qwen3.6-plus

Steps to Reproduce

1. Create config with reasoning options:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "openrouter": {
      "npm": "@ai-sdk/openai-compatible",
      "options": {
        "baseURL": "https://openrouter.ai/api/v1",
        "name": "Openrouter"
      },
      "models": {
        "qwen/qwen3.6-plus": {
          "options": {
            "reasoning": { "effort": "high" }
          }
        }
      }
    }
  }
}

2. Run in headless mode:

OPENCODE_CONFIG=/tmp/test_thinking.json \
OPENROUTER_API_KEY=sk-or-v1-... \
opencode run \
  --model openrouter/qwen/qwen3.6-plus \
  --format json \
  --dangerously-skip-permissions \
  "explain recursion briefly"

3. Observe output:

{"type":"step_start",...}
{"type":"text","part":{"text":"Recursion is..."}}

Model responds instantly with no reasoning — reasoning.effort: high was not sent to the API.

Expected Behavior

OpenCode should forward model-level options to the API call, resulting in the model performing extended thinking before responding. OpenRouter confirms qwen/qwen3.6-plus supports reasoning parameter:

curl https://openrouter.ai/api/v1/models | jq '.data[] | select(.id=="qwen/qwen3.6-plus") | .supported_parameters'
# ["include_reasoning", "max_tokens", "presence_penalty", "reasoning", ...]

When called directly with reasoning parameter, the model thinks before responding (verified via direct API call).

Actual Behavior

Model options are silently ignored. The reasoning parameter is never sent to OpenRouter. Model responds without any thinking regardless of config.

Additional Investigation

We also tested:

Attempt 1 — options at provider level via defaultQuery:

"options": {
  "baseURL": "...",
  "name": "Openrouter",
  "defaultQuery": { "reasoning": {"effort": "high"} }
}

→ Same result. Options ignored.

Attempt 2 — using built-in OpenRouter provider (no npm field):

{
  "provider": {
    "openrouter": {
      "models": {
        "qwen/qwen3.6-plus": {
          "options": { "reasoning": {"effort": "high"} }
        }
      }
    }
  }
}

→ Same result. Options ignored.

Root Cause (suspected)

Related to issue #971 — model options are not forwarded for openai-compatible providers. The options object appears to be parsed from config but never merged into the actual AI SDK provider call parameters.

The name field in provider options IS applied correctly (baseURL routing works), suggesting provider-level options are forwarded but model-level options are dropped somewhere between config parsing and the API call.

Use Case / Impact

This blocks implementing dynamic thinking/reasoning toggle for any custom or OpenRouter-hosted model in headless/programmatic OpenCode usage. Specifically, it prevents building OpenWebUI pipe integrations that toggle reasoning per-request via OPENCODE_CONFIG environment variable.

Workaround

None found. The only alternative is to use models with thinking built-in at the model level (e.g. qwen/qwen3-235b-a22b-thinking), but this removes the ability to toggle thinking on/off dynamically.

References

Plugins

No response

OpenCode version

1.14.48

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. github-actions commented on May 13, 2026

    @github-actions
    Contributor

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

    If your issue is distinct (e.g., specific to model-level options like reasoning.effort vs. provider-level options like baseURL), please clarify the difference so it can be tracked separately.

  2. R8CEH commented on May 13, 2026

    @R8CEH
    Author

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

    If your issue is distinct (e.g., specific to model-level options like reasoning.effort vs. provider-level options like baseURL), please clarify the difference so it can be tracked separately.

    Yes, this is distinct from #5674 and #971. To clarify:

    • Provider-level options (baseURL, name) — work correctly. Requests are routed to the right endpoint.

    • Model-level options (reasoning.effort, thinking) — silently dropped. Never appear in the actual API call.

    Tested with three different configs (model options, provider defaultQuery, built-in openrouter provider without npm) — all produce identical results with no reasoning parameter sent to the API.
    The distinction matters because provider-level fix (#971) did not resolve model-level forwarding.

  3. cgaravitoq commented on May 19, 2026

    @cgaravitoq

    Hitting the same root cause from a slightly different angle — direct OpenAI provider (@ai-sdk/openai), via the JS SDK (@opencode-ai/sdk 1.15.5, createOpencode()).

    Two extra symptoms worth capturing:

    1. Default injection, not just drop: For GPT-5 models with reasoning: true, opencode 1.15.5 doesn't merely drop the user-supplied options.reasoning.effort — it injects "max" from an internal default. The OpenAI Responses API rejects this:

      {
        "error": {
          "message": "Invalid value: 'max'. Supported values are: 'none', 'minimal', 'low', 'medium', 'high', and 'xhigh'.",
          "param": "reasoning.effort",
          "code": "invalid_value"
        }
      }

      Result: 100% of GPT-5 reasoning model calls fail with HTTP 400.

    2. Confirms the parse/forward split noted in this issue's root-cause analysis: changing provider.openai.models["gpt-5.5"].name via Config is reflected in client.config.providers(), while provider.openai.models["gpt-5.5"].options.reasoning.effort = "xhigh" is silently discarded.

    Tried at provider level, model level, and with reasoning: false — all ignored.

  4. dzuff-sfdc commented on Jun 8, 2026

    @dzuff-sfdc

    May also affect @ai-sdk/amazon-bedrock providers

    I believe I'm hitting the same issue with a custom provider using npm: "@ai-sdk/amazon-bedrock". Specifically tried us.anthropic.claude-opus-4-8 with reasoningConfig, but model-level options appear to be dropped — additionalModelRequestFields looks absent in the outbound Converse request based on my testing.

    Haven't fully ruled out other causes, but the gateway and model seem fine when called directly, which suggests the drop may be happening in opencode's option-forwarding layer.

  5. psemeniuk commented on Jun 18, 2026

    @psemeniuk

    Hi,

    probably related - i have the same issues with new bedrock mantle (openai-compatible) endpoint for closed GPT models.

    Payload intercepted by mitmproxy:

    {
      "model": "openai.gpt-5.4",
      "input": [
        {
          "role": "system",
          "content": "You are OpenCode,...IRRELEVANT"
        },
        {
          "role": "user",
          "content": [
            {
              "type": "input_text",
              "text": "hi"
            }
          ]
        }
      ],
      "max_output_tokens": 32000,
      "text": {
        "verbosity": "low"
      },
      "store": false,
      "include": [
        "reasoning.encrypted_content"
      ],
      "tools": [
        ...IRRELEVANT
      ],
      "tool_choice": "auto",
      "stream": true
    }

    Effectively GPT 5.4 works with NO reasoning, GPT 5.5 medium effort (because of their at-provider-level defaults). Tried set via toggling effort and via opencode.json with

              "options": {
                "reasoning": { "effort": "high" }
              }

    on model settings.

  6. opencode-agent commented on Aug 9, 2026

    @opencode-agent
    Contributor

    Thanks everyone for the reports. There are a few different cases collected in this issue, and they are covered by the current dev branch:

    • Model options are merged and forwarded through the same request path for interactive and headless usage.
    • For a custom @ai-sdk/openai-compatible provider, do not retain the old options.name workaround with different casing. In the original config, name: "Openrouter" makes the SDK read providerOptions.Openrouter while OpenCode writes providerOptions.openrouter, so the nested reasoning option is skipped. Remove options.name (allowing OpenCode to use the provider ID openrouter) or keep its casing identical to the provider ID.
    • The later @ai-sdk/openai and Bedrock Mantle comments describe a separate OpenAI reasoning gate. That was fixed in fix(provider): force openai reasoning variants #34702 by forcing the reasoning request path when reasoning options or capabilities are present; the fix first shipped in v1.17.13.
    • The reported invalid max default and reasoning variant behavior have also since been corrected on dev.
    • Current dev routes Amazon Bedrock reasoningConfig under the bedrock SDK namespace and has coverage for that mapping.

    Please upgrade to the latest release and use the provider-specific option shape (reasoning for the OpenRouter SDK, reasoningEffort for OpenAI-family SDKs). Closing this as fixed.

  7. adamkes commented on Aug 15, 2026

    @adamkes

    Still reproducible on the latest release (1.18.18), headless and interactive. Verified against @ai-sdk/openai-compatible with an Ollama 0.32.9 backend. Captured request bodies via a stub at baseURL:

    1. Model-level options — silently dropped:

    // config: models["qwen3.5:9b"].options = { "reasoning_effort": "none" }
    {"model":"qwen3.5:9b","max_tokens":32000,"top_p":1,"messages":[...]}

    2. variants — forwarded, but nested under options, which OpenAI-compatible backends ignore:

    // config: models["qwen3.5:9b"].variants.fast.options = { "reasoning_effort": "none" }
    // (snake_case and camelCase behave the same)
    {"model":"qwen3.5:9b","max_tokens":32000,"top_p":1,"options":{"reasoning_effort":"none"},"messages":[...]}

    Ollama's /v1 handler unmarshals into a struct with no options field, so this is silently discarded: the model still thinks (60 reasoning tokens, empty content with max_tokens:60). The same server honors a top-level reasoning_effort:"none" (17 tokens, instant answer).

    3. v2 config keys settings / body — not honored by 1.18.18 at all (nothing appears in the request body).

    Net result: there is no config-only way to inject a top-level provider field like reasoning_effort for @ai-sdk/openai-compatible models, so users of Ollama thinking models (qwen3.5, gemma4, DeepSeek-R1 style) cannot disable reasoning without running a local proxy.

    Expected: model options forwarded into the request, variants/options merged at the top level, or the documented v2 settings/body keys working in the openai-compatible adapter — any of these would fix it.

    Related: #41294, #5674, #971.

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