Skip to content

[Bug] v1 path drops variant body fields (e.g. \reasoning_split) for @ai-sdk/openai-compatible provider #42300

Closed as not planned

Description

@josedeng-aaxis

Description

Summary

When using a custom @ai-sdk/openai-compatible provider with a model that has body-level options in its variant (e.g. reasoning_split), opencode's v1 path silently drops those fields from the actual HTTP
request. The body schema constructed by streamText is missing fields that the user's variant config clearly defines.

Verified by reverse engineering the 1.18.16 binary and by direct comparison with the AI SDK working correctly when given the same providerOptions shape.

Environment

  • opencode version: 1.18.16 (binary)
  • opencode path: v1 (default; not the opt-in OPENCODE_EXPERIMENTAL_NATIVE_LLM native runtime)
  • Provider: custom @ai-sdk/openai-compatible named minimax-openai pointing at https://api.minimaxi.com/v1
  • Model: MiniMax-M3 (used as a representative; the bug is provider-agnostic, not MiniMax-specific)
  • Endpoint: POST /v1/chat/completions

Config that triggers the bug

{
  "provider": {
    "minimax-openai": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "MiniMax",
      "api": "https://api.minimaxi.com/v1",
      "options": {
        "baseURL": "https://api.minimaxi.com/v1",
        "apiKey": "{env:MiniMax_API_KEY}"
      },
      "models": {
        "MiniMax-M3": {
          "name": "MiniMax-M3",
          "reasoning": true,
          "request": { "variant": "thinking" },
          "variants": {
            "thinking": {
              "thinking": { "type": "adaptive" },
              "reasoning_split": true
            }
          }
        }
      }
    }
  }
}

Expected behavior

The HTTP body sent to https://api.minimaxi.com/v1/chat/completions should include both thinking: { type: "adaptive" } and reasoning_split: true because both come from the active thinking variant.

With reasoning_split: true, MiniMax's OpenAI-compatible API returns:

  • delta.reasoning_content (clean thinking text)
  • delta.content (only the actual response, no ... tags)

Actual behavior

The request body only contains thinking: { type: "adaptive" }. reasoning_split is missing.

Because reasoning_split: true is not sent, MiniMax returns the model output with ... blocks embedded directly in delta.content, while the parallel delta.reasoning / delta.reasoning_content
fields stay empty or duplicated. End result: opencode shows the thinking text in the visible content of the assistant message instead of as a separate reasoning block.

How I confirmed this is an opencode bug, not MiniMax / AI SDK

  1. Direct AI SDK test (works correctly):

    import { createOpenAICompatible } from '@ai-sdk/openai-compatible'
    const oai = createOpenAICompatible({ name: 'minimax', baseURL: 'https://api.minimaxi.com/v1', apiKey: '...' })
    const result = await oai('MiniMax-M3').doGenerate({
    prompt: [{ role: 'user', content: [{ type: 'text', text: '一句话hi' }] }],
    maxOutputTokens: 100,
    temperature: 1,
    providerOptions: {
    minimax: { thinking: { type: 'adaptive' }, reasoning_split: true }
    }
    })
    console.log(result.request.body)
    // → {"model":"MiniMax-M3","max_tokens":100,"temperature":1,
    // "thinking":{"type":"adaptive"},"reasoning_split":true,"messages":[...]}

    The SDK puts both fields in the body and the response is split cleanly.

  2. Reverse-engineering the 1.18.16 binary shows ProviderTransform.providerOptions produces { [X]: Y } where X is the provider slug and Y is the merged options. For an openai-compatible provider named
    minimax-openai, this should resolve to { minimax: { …, thinking, reasoning_split } }. I could not locate an exact point in the v1 path that strips the variant body, but the body that opencode ultimately
    sends to MiniMax is provably missing reasoning_split.

  3. Same v1 path correctly emits other variant fields like thinking: { type: "adaptive" } — so the variant is being read; something downstream selectively drops reasoning_split.

Likely root cause (to investigate)

packages/opencode/src/provider/transform.ts → providerOptions returns the merged options, which is then handed to streamText as providerOptions.minimax = { thinking, reasoning_split, … }.

The AI SDK's getArgs for @ai-sdk/openai-compatible (in the bundled binary) builds the body with:

...Object.fromEntries(
Object.entries({
...B[this.providerOptionsName],
...B[h(this.providerOptionsName)]
}).filter(([d]) => !Object.keys(g.shape).includes(d))
)

with g.shape = { user, reasoningEffort, textVerbosity, strictJsonSchema }. So reasoning_split should pass through, and my standalone test confirms it does.

The discrepancy suggests one of:

  • The providerOptions actually being passed to streamText from the v1 path does not contain reasoning_split (lost earlier in the request.ts → ProviderTransform.providerOptions chain).
  • Or the AI SDK bundle version inside 1.18.16 differs from the one I tested and has a stricter filter.

I'd appreciate maintainer eyes on the v1 LLMRequestPrep.prepare → ProviderTransform.providerOptions chain (packages/opencode/src/session/llm/request.ts line 80–91, packages/opencode/src/provider/transform.ts
line 1360).

Workaround (until fixed)

  1. Switch the model to a provider that natively understands the model's thinking protocol (e.g. @ai-sdk/anthropic against MiniMax's Anthropic-compatible endpoint https://api.minimaxi.com/anthropic). That
    endpoint returns structured thinking blocks and does not require reasoning_split.
  2. Or opt into the v2 native runtime with OPENCODE_EXPERIMENTAL_NATIVE_LLM=1 and put body fields directly under request.body (the v2 path's withDefaults in packages/core/src/session/runner/model.ts propagates
    model.request.body to http.body).

Reproduction

Use the config above, then:

opencode run --model "minimax-openai/MiniMax-M3" --variant thinking "一句话hi"

In the response you will see ... in the assistant content,

which should not happen when reasoning_split: true is honored.

Impact

  • Affects all openai-compatible providers where the upstream model needs extra body fields (e.g. reasoning_split, enable_thinking, chat_template_kwargs, custom vendor flags) configured via variants in
    opencode.
  • The fields silently disappear — no warning, no error in the log.
  • The model still works (it just returns thinking in a different shape), so users may not notice unless they inspect raw response content.

Plugins

No response

OpenCode version

No response

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 Aug 13, 2026

    @github-actions
    Contributor

    This issue does not fully meet our contributing guidelines.

    What needs to be fixed:

    • OpenCode version field is empty (_No response_). Please fill in your exact version (e.g. 1.18.16 — you mentioned it in the description, please also put it in the template field).
    • Steps to reproduce field is empty. Please fill in the structured reproduction steps (you have a repro command in the description — move or copy it here).
    • Operating System and Terminal fields are empty. We need system information to help diagnose and reproduce the issue.

    Please edit this issue to address the above within 2 hours, or it will be automatically closed.


    This issue might be a duplicate of or closely related to existing issues. Please check:

    If you believe this was flagged incorrectly, please let a maintainer know.

  2. github-actions commented on Aug 13, 2026

    @github-actions
    Contributor

    This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.

    Feel free to open a new issue that follows our issue templates.

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