Thinking / Reasoning Compatibility

May 14, 2026 ยท View on GitHub

This document describes the current thinking / reasoning compatibility layer in aiproxy, including:

  • how different request protocols express reasoning parameters
  • how the proxy normalizes those parameters internally
  • how they are converted when forwarding to different upstream vendors
  • known vendor- and model-specific limitations, fallbacks, and downgrade behavior

This document focuses on parameter compatibility and conversion behavior, not the full upstream API surface of each vendor.

1. Goals

The purpose of this feature is to:

  1. let callers use the native reasoning format of the current request mode whenever possible
  2. automatically convert reasoning parameters when transforming from request format A to upstream format B
  3. reduce upstream validation errors caused by model capability differences or schema mismatches

The implementation follows these rules:

  • it only parses the native thinking / reasoning fields of the current request mode
    • OpenAI Chat / Completions only parse reasoning_effort
    • OpenAI Responses as a target format only writes reasoning.effort
    • Gemini only parses generationConfig.thinkingConfig
    • Claude / Anthropic only parse thinking and output_config
  • it no longer supports reverse compatibility for the old generic thinking structure
  • only converted request bodies are normalized or constrained for upstream validity
  • native requests are not automatically migrated into another thinking dialect
    • for example, a native Claude request is not rewritten into OpenAI reasoning_effort
    • existing protocol-level cleanup may still apply, for example when an upstream forbids temperature together with thinking
  • adaptor-specific native fields may still be preserved when the upstream itself accepts them, for example Qianfan native thinking
  • every model-name-based capability branch uses:
    1. OriginModel first
    2. ActualModel as fallback when origin does not match

2. Internal normalization model

Internally, the proxy first normalizes different protocol-specific reasoning parameters into one unified structure. Conceptually it contains:

  • Specified: whether reasoning was explicitly configured
  • Disabled: whether reasoning was explicitly turned off
  • Effort: normalized effort level
  • BudgetTokens: token budget if the original protocol provided one

2.1 Supported normalized effort values

The normalized effort enum is:

  • none
  • minimal
  • low
  • medium
  • high
  • xhigh

The parser also accepts several aliases:

  • off / disabled -> none
  • med -> medium
  • max / maximum -> xhigh

2.2 Default effort <-> budget mapping

When an upstream only supports token budgets instead of discrete labels such as high or medium, the proxy uses this mapping:

effortbudget
none0
minimal1024
low2048
medium8192
high16384
xhigh32768

When converting budget back into effort, the proxy uses these ranges:

budget rangenormalized effort
<= 0none
1 ~ 1024minimal
1025 ~ 4096low
4097 ~ 12288medium
12289 ~ 24576high
> 24576xhigh

3. Supported input formats by request mode

3.1 OpenAI Chat / Completions

The compatibility layer currently only parses:

{
  "reasoning_effort": "none|minimal|low|medium|high|xhigh"
}

Notes:

  • this is the only reasoning field currently consumed in OpenAI Chat / Completions mode
  • the old generic thinking structure is no longer parsed here

3.2 OpenAI Responses

When the proxy needs to build an OpenAI Responses request body, reasoning is written as:

{
  "reasoning": {
    "effort": "none|minimal|low|medium|high|xhigh"
  }
}

Notes:

  • in the current implementation, Responses is mostly used as a target format
  • for example, Chat / Claude / Gemini requests converted into Responses will write reasoning.effort

3.3 Gemini

The proxy currently parses:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 2048,
      "includeThoughts": true,
      "thinkingLevel": "minimal|low|medium|high"
    }
  }
}

Parsing priority:

  1. thinkingLevel
  2. thinkingBudget
  3. includeThoughts

Interpretation:

  • thinkingLevel maps directly to normalized effort
  • thinkingBudget is converted into effort through the budget ranges
  • includeThoughts=true with no other recognized fields is treated as medium
  • thinkingBudget<=0 is treated as none
  • if thinkingConfig is explicitly present but contains no recognized reasoning fields, it is treated as disabled

3.4 Claude / Anthropic

The proxy currently parses:

{
  "thinking": {
    "type": "disabled|enabled|adaptive",
    "budget_tokens": 2048
  },
  "output_config": {
    "effort": "low|medium|high|max"
  }
}

Rules:

  • thinking.type=disabled -> none
  • thinking.type=enabled or adaptive -> reasoning enabled
  • if budget_tokens is provided, the budget is preserved in normalized form
  • if output_config.effort is provided, it takes precedence for effort selection
  • if only thinking.type=enabled is present without budget or effort, the normalized default is medium

4. How normalized reasoning is written to each target format

4.1 OpenAI Chat / Completions output

Output field:

{
  "reasoning_effort": "..."
}

Typical use cases:

  • Gemini -> OpenAI
  • Claude -> OpenAI
  • any other request normalized first, then emitted as OpenAI-compatible reasoning

Effort mapping:

normalized effortOpenAI Chat / Completions field
nonereasoning_effort: "none"
minimalreasoning_effort: "minimal"
lowreasoning_effort: "low"
mediumreasoning_effort: "medium"
highreasoning_effort: "high"
xhighreasoning_effort: "xhigh"

4.2 OpenAI Responses output

Output field:

{
  "reasoning": {
    "effort": "..."
  }
}

Typical use cases:

  • Chat -> Responses
  • Claude -> Responses
  • Gemini -> Responses

Effort mapping:

normalized effortOpenAI Responses field
nonereasoning.effort: "none"
minimalreasoning.effort: "minimal"
lowreasoning.effort: "low"
mediumreasoning.effort: "medium"
highreasoning.effort: "high"
xhighreasoning.effort: "xhigh"

4.3 Gemini output

Output location:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 2048,
      "includeThoughts": true,
      "thinkingLevel": "low|medium|high"
    }
  }
}

There are two main branches.

A. Gemini 3 / 4 / 5 families: use thinkingLevel

If the model name matches one of these families, the proxy prefers thinkingLevel:

  • gemini-3*
  • gemini-4*
  • gemini-5*

Mapping rules:

  • Pro models:
    • high / xhigh -> high
    • all other enabled states -> low
  • non-Pro models:
    • none -> minimal
    • low -> low
    • medium -> medium
    • high / xhigh -> high
    • everything else -> minimal

Disable behavior:

  • these models generally do not use thinkingBudget=0 as the disable path
  • when the caller explicitly sends none, the proxy degrades to the minimum valid level for that model instead of forcing an invalid disable payload

Exact level mapping:

normalized effortGemini 3+ Pro thinkingLevelGemini 3+ non-Pro thinkingLevel
nonelowminimal
minimallowminimal
lowlowlow
mediumlowmedium
highhighhigh
xhighhighhigh

B. Gemini 2.5 family: use thinkingBudget

Model limits:

modelbudget rangedisable supported
gemini-2.5-pro128 ~ 32768no
gemini-2.5-flash1 ~ 24576yes
gemini-2.5-flash-lite512 ~ 24576yes

Write rules:

  • when reasoning is enabled:
    • first derive a default budget from effort
    • then clamp it to the model-specific allowed range
  • when reasoning is disabled:
    • models that support disabling receive thinkingBudget=0
    • models that do not support disabling receive the minimum allowed budget
  • includeThoughts:
    • true when reasoning is enabled
    • false when reasoning is disabled

Important note:

  • Gemini thinking budgets are not additionally clamped by max_tokens / maxOutputTokens
  • this is intentional, to avoid incorrectly shrinking an otherwise valid Gemini reasoning configuration

Exact budget mapping after Gemini model-range clamping:

normalized effortgemini-2.5-progemini-2.5-flashgemini-2.5-flash-lite
none12800
minimal102410241024
low204820482048
medium819281928192
high163841638416384
xhigh327682457624576

includeThoughts is true for enabled rows and false for the none row.

4.4 Claude / Anthropic output

The proxy may emit two shapes.

A. Legacy / budget mode

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  }
}

B. Adaptive mode

{
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "low|medium|high|max"
  }
}

Mapping details:

  • xhigh -> Claude output_config.effort=max
  • high -> high
  • medium -> medium
  • low, minimal, and none map to low when adaptive output is used

Exact output mapping:

normalized effortlegacy / budget Claude outputadaptive Claude output
nonethinking.type=disabledthinking.type=disabled; may be removed for adaptive-only / Mythos models
minimalthinking.type=enabled, budget_tokens=1024thinking.type=adaptive, output_config.effort=low
lowthinking.type=enabled, budget_tokens=2048thinking.type=adaptive, output_config.effort=low
mediumthinking.type=enabled, budget_tokens=8192thinking.type=adaptive, output_config.effort=medium
highthinking.type=enabled, budget_tokens=16384thinking.type=adaptive, output_config.effort=high
xhighthinking.type=enabled, budget_tokens=32768thinking.type=adaptive, output_config.effort=max

Budget-mode constraints:

  • minimum budget_tokens=1024
  • any explicit budget below 1024 is raised to 1024
  • when max_tokens exists, the proxy guarantees:
    • max_tokens >= max(budget_tokens + 1, 2048)
    • budget_tokens < max_tokens
  • invalid budgets are adjusted into an upstream-valid value

Adaptive capability behavior:

  • older models continue to use enabled + budget_tokens
  • models that support adaptive thinking are emitted as thinking.type=adaptive + output_config.effort
  • Claude capability detection uses OriginModel first, then ActualModel

4.5 Ali DashScope-compatible output

Output fields:

{
  "enable_thinking": true,
  "thinking_budget": 2048
}

Rules:

  • none -> enable_thinking=false, and thinking_budget is removed
  • enabled reasoning -> enable_thinking=true
  • if the model supports budgets, thinking_budget is written and clamped to the Qianfan documented range [100, 16384]

Exact mapping:

normalized effortAli output for models supporting thinking_budgetAli output for models without budget support
noneenable_thinking=false; no thinking_budgetenable_thinking=false; no thinking_budget
minimalenable_thinking=true, thinking_budget=1024enable_thinking=true; no thinking_budget
lowenable_thinking=true, thinking_budget=2048enable_thinking=true; no thinking_budget
mediumenable_thinking=true, thinking_budget=8192enable_thinking=true; no thinking_budget
highenable_thinking=true, thinking_budget=16384enable_thinking=true; no thinking_budget
xhighenable_thinking=true, thinking_budget=16384enable_thinking=true; no thinking_budget

Models currently considered to support thinking_budget include:

  • qwen3-*
  • qwq-*
  • models containing glm
  • models containing kimi

Ali-specific behavior:

  • thinking_budget is not clamped by max_tokens
  • qwen3-*: non-streaming requests are forced to enable_thinking=false
  • qwq-*: requests are forced to stream=true

4.6 Zhipu / DeepSeek / Doubao thinking output

These vendors currently use a simplified thinking object:

{
  "thinking": {
    "type": "enabled|disabled"
  }
}

Rules:

  • none -> thinking.type=disabled
  • every other enabled state -> thinking.type=enabled
  • these vendors currently preserve only the on/off meaning, not detailed budget information

That means:

  • minimal, low, medium, high, and xhigh
  • all collapse into the same upstream enabled state

Exact mapping:

normalized effortZhipu / DeepSeek / Doubao output
nonethinking.type=disabled
minimalthinking.type=enabled
lowthinking.type=enabled
mediumthinking.type=enabled
highthinking.type=enabled
xhighthinking.type=enabled

4.7 Qianfan output

Qianfan supports multiple upstream reasoning shapes, and different models accept different fields:

{
  "thinking": {
    "type": "enabled|disabled"
  },
  "enable_thinking": true,
  "thinking_budget": 2048,
  "reasoning_effort": "high|max"
}

Rules:

  • native thinking has priority and is preserved as provided
  • when thinking is present, conflicting reasoning_effort, enable_thinking, and thinking_budget are removed
  • when native thinking is absent, the adaptor selects a field family based on model capability:
    • models that support reasoning_effort: enabled states emit reasoning_effort=high|max; disabled states emit no reasoning field
    • models that support enable_thinking: emit enable_thinking=true|false; enabled states also emit thinking_budget when supported
    • models that support thinking: emit thinking.type=enabled|disabled; enabled states also emit thinking_budget when supported
    • models that only support thinking_budget: enabled states emit only thinking_budget; disabled states emit no reasoning field
  • model capability detection first checks exact documented model names, then falls back to family / keyword matches such as qwen3-*, deepseek-v4-*, *think* / *thinking*, and *vl*
  • models that do not match any Qianfan reasoning capability have normalized reasoning controls removed to avoid sending unsupported fields

Exact mapping by field family when the input does not already contain native thinking:

normalized effortreasoning_effort modelsenable_thinking modelsthinking modelsbudget-only models
noneno reasoning fieldenable_thinking=falsethinking.type=disabledno reasoning field
minimalreasoning_effort=highenable_thinking=true; thinking_budget=1024 when supportedthinking.type=enabled; thinking_budget=1024 when supportedthinking_budget=1024
lowreasoning_effort=highenable_thinking=true; thinking_budget=2048 when supportedthinking.type=enabled; thinking_budget=2048 when supportedthinking_budget=2048
mediumreasoning_effort=highenable_thinking=true; thinking_budget=8192 when supportedthinking.type=enabled; thinking_budget=8192 when supportedthinking_budget=8192
highreasoning_effort=highenable_thinking=true; thinking_budget=16384 when supportedthinking.type=enabled; thinking_budget=16384 when supportedthinking_budget=16384
xhighreasoning_effort=maxenable_thinking=true; thinking_budget=16384 when supportedthinking.type=enabled; thinking_budget=16384 when supportedthinking_budget=16384

For OpenAI Responses input, Qianfan also normalizes reasoning.effort into the same upstream Qianfan fields.

4.8 Moonshot / Kimi output

Moonshot / Kimi uses Kimi's thinking object only for upstream models that support thinking toggling:

{
  "thinking": {
    "type": "enabled|disabled"
  }
}

Exact mapping for toggle-capable Kimi models:

normalized effortKimi output
nonethinking.type=disabled
minimalthinking.type=enabled
lowthinking.type=enabled
mediumthinking.type=enabled
highthinking.type=enabled
xhighthinking.type=enabled

For non-toggle Kimi models, the adaptor removes reasoning_effort and does not send thinking. Kimi output currently preserves only on/off semantics, not budget or fine-grained effort.


5. Vendor / adaptor support matrix

This section focuses on the major adaptors that currently participate in the reasoning compatibility layer. The tables describe what happens after the source request has been parsed into the normalized reasoning model.

5.1 OpenAI / Azure / OpenAI-compatible upstreams

Native fields:

modenative source fieldemitted upstream fieldexact effort mapping
Chat Completionsreasoning_effortreasoning_effortunchanged unless a known GPT model family does not support the requested value
Completionsreasoning_effortreasoning_effortunchanged unless a known GPT model family does not support the requested value
Responsesreasoning.effortreasoning.effortunchanged unless a known GPT model family does not support the requested value

Cross-protocol conversions:

source request modetarget OpenAI modeconversion
Gemini -> Chat / CompletionsOpenAI-compatible chat payloadgenerationConfig.thinkingConfig -> normalized effort -> reasoning_effort
Claude / Anthropic -> Chat / CompletionsOpenAI-compatible chat payloadthinking / output_config -> normalized effort -> reasoning_effort
OpenAI Chat -> ResponsesOpenAI Responses payloadreasoning_effort -> normalized effort -> reasoning.effort
Gemini -> ResponsesOpenAI Responses payloadthinkingConfig -> normalized effort -> reasoning.effort
Claude / Anthropic -> ResponsesOpenAI Responses payloadthinking / output_config -> OpenAI chat request -> reasoning.effort

Notes:

  • OpenAI Chat / Completions only parse reasoning_effort.
  • OpenAI Chat / Completions do not parse Gemini-style thinkingConfig, Claude-style thinking, Ali enable_thinking, or Ali thinking_budget in this mode.
  • GPT reasoning effort compatibility is an OpenAI-adaptor-specific rule. It applies to native OpenAI Chat / Completions / Responses payloads and to Gemini / Claude / Chat requests converted into OpenAI Chat or Responses payloads.
  • The compatibility check uses OriginModel first and falls back to ActualModel. Matching recognizes only explicit known GPT model IDs / families, while allowing provider prefixes and official-style suffixes such as dated snapshots.
  • If neither model name matches a known GPT reasoning-effort family, including unknown GPT-like model names, the adaptor leaves the requested effort unchanged.
  • For known GPT families, unsupported efforts are migrated to the nearest supported effort. Ties prefer the higher enabled effort, so minimal on a model that supports none and low becomes low.

Known GPT effort support used by the adaptor:

model matchsupported reasoning_effort / reasoning.effort valuesmigration examples
gpt-5.5*none, low, medium, high, xhighminimal -> low
gpt-5.4*, gpt-5.2*none, low, medium, high, xhighminimal -> low
gpt-5.4-pro*, gpt-5.2-pro*medium, high, xhighnone / minimal / low -> medium
gpt-5.1*none, low, medium, highminimal -> low; xhigh -> high
gpt-5-pro*highany unsupported value -> high
gpt-5*minimal, low, medium, highnone -> minimal; xhigh -> high

5.2 Google Gemini

Supported reasoning conversion modes:

source request modeGemini adaptor behavioremitted upstream field
OpenAI Chatparses reasoning_effortgenerationConfig.thinkingConfig
Claude / Anthropicparses thinking / output_config through the Claude convertergenerationConfig.thinkingConfig
Gemini nativeuses generationConfig.thinkingConfig as the native request fieldgenerationConfig.thinkingConfig
OpenAI Responsesnot a Gemini adaptor input modeN/A
OpenAI Completionsnot a Gemini adaptor input modeN/A

Exact output depends on the target Gemini model family:

normalized effortGemini 3+ ProGemini 3+ non-Progemini-2.5-progemini-2.5-flashgemini-2.5-flash-lite
nonethinkingLevel=lowthinkingLevel=minimalthinkingBudget=128, includeThoughts=falsethinkingBudget=0, includeThoughts=falsethinkingBudget=0, includeThoughts=false
minimalthinkingLevel=lowthinkingLevel=minimalthinkingBudget=1024, includeThoughts=truethinkingBudget=1024, includeThoughts=truethinkingBudget=1024, includeThoughts=true
lowthinkingLevel=lowthinkingLevel=lowthinkingBudget=2048, includeThoughts=truethinkingBudget=2048, includeThoughts=truethinkingBudget=2048, includeThoughts=true
mediumthinkingLevel=lowthinkingLevel=mediumthinkingBudget=8192, includeThoughts=truethinkingBudget=8192, includeThoughts=truethinkingBudget=8192, includeThoughts=true
highthinkingLevel=highthinkingLevel=highthinkingBudget=16384, includeThoughts=truethinkingBudget=16384, includeThoughts=truethinkingBudget=16384, includeThoughts=true
xhighthinkingLevel=highthinkingLevel=highthinkingBudget=32768, includeThoughts=truethinkingBudget=24576, includeThoughts=truethinkingBudget=24576, includeThoughts=true

Notes:

  • The exact mapping table applies when the adaptor is converting from OpenAI Chat or Claude / Anthropic into Gemini.
  • Native Gemini requests keep their own generationConfig.thinkingConfig; the adaptor does not rewrite a native Gemini thinking dialect into another Gemini thinking dialect.
  • Gemini 2.5 uses budget-based output with model-specific range enforcement.
  • Gemini 3 / 4 / 5 use thinkingLevel.
  • Some models cannot truly disable thinking, so none degrades to the minimum valid level or budget.

5.3 Anthropic official

Supported reasoning conversion modes:

source request modeAnthropic adaptor behavioremitted upstream field
OpenAI Chatparses reasoning_effortthinking and optionally output_config
Gemini nativeparses generationConfig.thinkingConfigthinking and optionally output_config
Anthropic nativepreserves native Anthropic fieldsthinking and output_config as provided, with native cleanup
OpenAI Responsesnot an Anthropic adaptor input modeN/A
OpenAI Completionsnot an Anthropic adaptor input modeN/A

Exact output by Claude capability:

normalized effortlegacy / budget Claude outputadaptive Claude output
nonethinking.type=disabledthinking.type=disabled; may be removed for adaptive-only / Mythos models
minimalthinking.type=enabled, budget_tokens=1024thinking.type=adaptive, output_config.effort=low
lowthinking.type=enabled, budget_tokens=2048thinking.type=adaptive, output_config.effort=low
mediumthinking.type=enabled, budget_tokens=8192thinking.type=adaptive, output_config.effort=medium
highthinking.type=enabled, budget_tokens=16384thinking.type=adaptive, output_config.effort=high
xhighthinking.type=enabled, budget_tokens=32768thinking.type=adaptive, output_config.effort=max

Notes:

  • The exact mapping table applies when the adaptor is converting from OpenAI Chat or Gemini into Claude.
  • Native Anthropic requests keep native thinking / output_config fields, apart from native cleanup required by the adaptor.
  • Budget mode enforces budget_tokens < max_tokens.
  • Older models use enabled + budget_tokens.
  • Adaptive-capable models use adaptive + output_config.effort.

5.4 AWS Bedrock Claude

Supported reasoning conversion modes:

source request modeBedrock Claude behavioremitted upstream field
OpenAI Chatparses reasoning_effortClaude thinking after Bedrock wrapping
Gemini nativeparses generationConfig.thinkingConfigClaude thinking after Bedrock wrapping
Anthropic nativepreserves native Anthropic fieldsClaude thinking after Bedrock wrapping
OpenAI Responsesnot a Bedrock Claude input modeN/A
OpenAI Completionsnot a Bedrock Claude input modeN/A

The effort mapping is the same as Anthropic official: legacy models use enabled + budget_tokens; adaptive-capable models use adaptive + output_config.effort. Bedrock then wraps the Claude request for the Bedrock runtime.

5.5 Vertex AI Claude

Supported reasoning conversion modes:

source request modeVertex Claude behavioremitted upstream field
OpenAI Chatparses reasoning_effortClaude thinking after Vertex wrapping
Gemini nativeparses generationConfig.thinkingConfigClaude thinking after Vertex wrapping
Anthropic nativepreserves native Anthropic fieldsClaude thinking after Vertex wrapping
OpenAI Responsesnot a Vertex Claude input modeN/A
OpenAI Completionsnot a Vertex Claude input modeN/A

The effort mapping is the same as Anthropic official: legacy models use enabled + budget_tokens; adaptive-capable models use adaptive + output_config.effort. Vertex then wraps the Claude request for Vertex AI.

5.6 Ali DashScope

Supported reasoning conversion modes:

source request modeAli adaptor behavioremitted upstream field
OpenAI Chatparses reasoning_effortenable_thinking; optional thinking_budget
OpenAI Completionsparses reasoning_effortenable_thinking; optional thinking_budget
Gemini nativeparses generationConfig.thinkingConfig through OpenAI-compatible conversionenable_thinking; optional thinking_budget
Anthropic nativeuses Ali Claude Code Proxy native request formatno cross-dialect reasoning migration
OpenAI Responsespassed through OpenAI-compatible Responses conversionno Ali-specific reasoning hook

Ali exact effort mapping:

normalized effortoutput on budget-capable Ali modelsoutput on non-budget Ali models
noneenable_thinking=false; thinking_budget removedenable_thinking=false; thinking_budget removed
minimalenable_thinking=true; thinking_budget=1024enable_thinking=true; no thinking_budget
lowenable_thinking=true; thinking_budget=2048enable_thinking=true; no thinking_budget
mediumenable_thinking=true; thinking_budget=8192enable_thinking=true; no thinking_budget
highenable_thinking=true; thinking_budget=16384enable_thinking=true; no thinking_budget
xhighenable_thinking=true; thinking_budget=16384enable_thinking=true; no thinking_budget

Ali budget-capable model detection:

model rulewrites thinking_budget
model starts with qwen3-yes
model starts with qwq-yes
model name contains glmyes
model name contains kimiyes
other Ali-compatible modelsno; only enable_thinking is written

Ali-specific behavior:

  • thinking_budget is not clamped by max_tokens.
  • qwen3-* non-streaming Chat / Completions requests force enable_thinking=false after the reasoning hook.
  • qwen3-* non-streaming Gemini-mode requests force enable_thinking=false and remove thinking_budget.
  • qwq-* requests force stream=true.

5.7 Doubao

Supported reasoning conversion modes:

source request modeDoubao adaptor behavioremitted upstream field
OpenAI Chatparses reasoning_effortthinking.type
Gemini nativeparses generationConfig.thinkingConfig through OpenAI-compatible conversionthinking.type
Anthropic nativeparses thinking / output_config through OpenAI-compatible conversionthinking.type
OpenAI Responsespassed through native OpenAI-compatible Responses conversionno Doubao-specific reasoning hook
OpenAI Completionsnot supported by Doubao adaptorN/A

Exact effort mapping:

normalized effortDoubao output
nonethinking.type=disabled
minimalthinking.type=enabled
lowthinking.type=enabled
mediumthinking.type=enabled
highthinking.type=enabled
xhighthinking.type=enabled

Notes:

  • Only on/off semantics are preserved; budget and fine-grained effort are dropped.
  • deepseek-reasoner also injects a system prompt, using the same origin-first, actual-fallback model match strategy.

5.8 DeepSeek

Supported reasoning conversion modes:

source request modeDeepSeek adaptor behavioremitted upstream field
OpenAI Chatparses reasoning_effortthinking.type
Gemini nativeparses generationConfig.thinkingConfig through OpenAI-compatible conversionthinking.type
Anthropic nativeuses DeepSeek /anthropic/v1/messagesno cross-dialect reasoning migration
OpenAI Responsesunsupported by the DeepSeek adaptorN/A
OpenAI CompletionsOpenAI-compatible pass-throughno DeepSeek-specific reasoning hook

Exact effort mapping for the hooked Chat / Gemini paths:

normalized effortDeepSeek output
nonethinking.type=disabled
minimalthinking.type=enabled
lowthinking.type=enabled
mediumthinking.type=enabled
highthinking.type=enabled
xhighthinking.type=enabled

DeepSeek currently preserves only enabled / disabled for the hooked OpenAI Chat and Gemini paths.

5.9 Zhipu

Supported reasoning conversion modes:

source request modeZhipu adaptor behavioremitted upstream field
OpenAI Chatparses reasoning_effortthinking.type
Gemini nativeparses generationConfig.thinkingConfig through OpenAI-compatible conversionthinking.type
Anthropic nativeparses thinking / output_config through OpenAI-compatible conversionthinking.type
OpenAI Responsesunsupported by this adaptorN/A
OpenAI CompletionsOpenAI-compatible pass-throughno Zhipu-specific reasoning hook

Exact effort mapping for the hooked Chat / Gemini / Anthropic paths:

normalized effortZhipu output
nonethinking.type=disabled
minimalthinking.type=enabled
lowthinking.type=enabled
mediumthinking.type=enabled
highthinking.type=enabled
xhighthinking.type=enabled

Zhipu currently preserves only enabled / disabled for the hooked paths.

5.10 Qianfan

Supported reasoning conversion modes:

source request modeQianfan adaptor behavioremitted upstream field
OpenAI Chatpreserves native thinking; otherwise parses reasoning_effortmodel-dependent thinking.type / enable_thinking / thinking_budget / reasoning_effort
OpenAI Completionspreserves native thinking; otherwise parses reasoning_effortmodel-dependent thinking.type / enable_thinking / thinking_budget / reasoning_effort
Gemini nativeparses generationConfig.thinkingConfig through OpenAI-compatible conversionmodel-dependent thinking.type / enable_thinking / thinking_budget / reasoning_effort
Anthropic nativeparses thinking / output_config through OpenAI-compatible conversionmodel-dependent thinking.type / enable_thinking / thinking_budget / reasoning_effort
OpenAI Responsespreserves native thinking; otherwise parses reasoning.effortmodel-dependent thinking.type / enable_thinking / thinking_budget / reasoning_effort

Exact effort mapping by field family when native thinking is not already present:

normalized effortreasoning_effort modelsenable_thinking modelsthinking modelsbudget-only models
noneno reasoning fieldenable_thinking=falsethinking.type=disabledno reasoning field
minimalreasoning_effort=highenable_thinking=true; thinking_budget=1024 when supportedthinking.type=enabled; thinking_budget=1024 when supportedthinking_budget=1024
lowreasoning_effort=highenable_thinking=true; thinking_budget=2048 when supportedthinking.type=enabled; thinking_budget=2048 when supportedthinking_budget=2048
mediumreasoning_effort=highenable_thinking=true; thinking_budget=8192 when supportedthinking.type=enabled; thinking_budget=8192 when supportedthinking_budget=8192
highreasoning_effort=highenable_thinking=true; thinking_budget=16384 when supportedthinking.type=enabled; thinking_budget=16384 when supportedthinking_budget=16384
xhighreasoning_effort=maxenable_thinking=true; thinking_budget=16384 when supportedthinking.type=enabled; thinking_budget=16384 when supportedthinking_budget=16384

Notes:

  • Qianfan native thinking wins over reasoning_effort / reasoning.effort.
  • When native thinking is present in Chat / Completions, the adaptor removes reasoning_effort, enable_thinking, and thinking_budget.
  • When native thinking is present in Responses, the adaptor removes reasoning.
  • Qianfan accepts only high and max for reasoning_effort, so lower enabled efforts are upgraded to high.
  • disabled states are expressed according to the model's field family; models that cannot disable thinking or do not match a reasoning capability are not forced to receive thinking.type=disabled.
  • model capability detection is origin-first, actual-fallback, and falls back to family / keyword matching when exact model names do not match.

5.11 Moonshot / Kimi

Supported reasoning conversion modes:

source request modeMoonshot adaptor behavioremitted upstream field
OpenAI Chatparses reasoning_effortKimi thinking.type for toggle-capable models
Gemini nativeparses generationConfig.thinkingConfig through OpenAI-compatible conversionKimi thinking.type for toggle-capable models
Anthropic nativeparses thinking / output_config through OpenAI-compatible conversionKimi thinking.type for toggle-capable models
OpenAI CompletionsOpenAI-compatible payload is passed throughno Moonshot-specific reasoning hook
OpenAI Responsesunsupported by the Moonshot adaptorN/A

The Moonshot adaptor currently treats these actual upstream model names as thinking-toggle capable:

actual upstream model rulesupports emitted Kimi thinking.type
kimi-k2.5*yes
kimi-k2.6*yes
other Kimi model namesno; reasoning_effort is removed and thinking is omitted

Exact effort mapping for toggle-capable Kimi models:

normalized effortKimi output
nonethinking.type=disabled; reasoning_effort removed
minimalthinking.type=enabled; reasoning_effort removed
lowthinking.type=enabled; reasoning_effort removed
mediumthinking.type=enabled; reasoning_effort removed
highthinking.type=enabled; reasoning_effort removed
xhighthinking.type=enabled; reasoning_effort removed

For non-toggle Kimi models, such as dedicated thinking models, the adaptor removes reasoning_effort and omits thinking. It does not send thinking.type=disabled to a model that cannot be disabled.

Notes:

  • Only on/off semantics are preserved; budget and fine-grained effort are dropped.
  • Model support is checked against ActualModel, because the upstream Kimi model name after channel mapping determines whether thinking is valid.

6. Model matching strategy

Most model-capability branches follow the same rule:

  1. use OriginModel first
  2. if OriginModel does not match, fall back to ActualModel

Why this exists:

  • callers may use a business-facing original model name
  • channel mapping may rewrite to the real upstream model name in ActualModel
  • some capability checks only match one of those names

This strategy is already used in:

  • Claude adaptive capability detection
  • Gemini thinking-level vs thinking-budget path selection
  • Ali budget support detection
  • Doubao bot / vision / deepseek-reasoner special routing
  • other model-name-based reasoning capability branches

Exception:

  • Moonshot / Kimi thinking-toggle detection uses ActualModel first, because thinking validity depends on the final upstream Kimi model after channel mapping.

7. Complete conversion examples

This section is intentionally broad. It tries to cover every reasoning-related conversion path currently implemented in code.

7.1 OpenAI Chat / Completions as the source format

7.1.1 OpenAI Chat -> OpenAI Responses

Input:

{
  "model": "gpt-4o",
  "reasoning_effort": "high",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "model": "gpt-4o",
  "input": [{"type": "message", "role": "user", "content": [{"type": "input_text", "text": "hello"}]}],
  "reasoning": {
    "effort": "high"
  }
}

Notes:

  • the same reasoning payload shape is used when Chat is converted to Responses for OpenAI-compatible upstreams
  • the same principle also applies to Azure when the request is routed to Responses

7.1.2 OpenAI Chat -> Gemini 2.5 Pro

Input:

{
  "model": "gemini-2.5-pro",
  "reasoning_effort": "high",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 16384,
      "includeThoughts": true
    }
  }
}

7.1.3 OpenAI Chat -> Gemini 2.5 Flash with explicit disable

Input:

{
  "model": "gemini-2.5-flash",
  "reasoning_effort": "none",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 0,
      "includeThoughts": false
    }
  }
}

7.1.4 OpenAI Chat -> Gemini 3 Pro with explicit disable

Input:

{
  "model": "gemini-3-pro",
  "reasoning_effort": "none",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingLevel": "low",
      "includeThoughts": false
    }
  }
}

Notes:

  • the proxy does not force an invalid disable payload here
  • it degrades to the minimum valid level for the model

7.1.5 OpenAI Chat -> Anthropic Claude Sonnet 4.5

Input:

{
  "model": "claude-sonnet-4-5",
  "reasoning_effort": "low",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  }
}

7.1.6 OpenAI Chat -> Anthropic Claude 3.7 Sonnet

Input:

{
  "model": "claude-3-7-sonnet-20250219",
  "reasoning_effort": "medium",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 8192
  }
}

7.1.7 OpenAI Chat -> Anthropic Claude Opus 4.7

Input:

{
  "model": "claude-opus-4-7",
  "reasoning_effort": "high",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "high"
  }
}

7.1.8 OpenAI Chat -> AWS Bedrock Claude

Input:

{
  "model": "claude-opus-4-7",
  "reasoning_effort": "high",
  "messages": [{"role": "user", "content": "hello"}]
}

Representative output body:

{
  "anthropic_version": "bedrock-2023-05-31",
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "high"
  }
}

Notes:

  • AWS wraps the Claude request with Bedrock-specific fields
  • the inner Claude thinking shape still follows the Anthropic conversion rules

7.1.9 OpenAI Chat -> Vertex AI Claude

Input:

{
  "model": "claude-sonnet-4-5",
  "reasoning_effort": "low",
  "messages": [{"role": "user", "content": "hello"}]
}

Representative output body:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  }
}

Notes:

  • Vertex AI uses a different transport path such as rawPredict / streamRawPredict
  • the request body still follows the Claude reasoning schema

7.1.10 OpenAI Chat -> Ali compatible chat

Input:

{
  "model": "glm-4.5",
  "reasoning_effort": "high",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "reasoning_effort": "high"
}

7.1.11 OpenAI Chat -> Ali qwen3-* non-stream request

Input:

{
  "model": "qwen3-32b",
  "reasoning_effort": "high",
  "stream": false,
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "enable_thinking": false,
  "thinking_budget": 16384
}

Notes:

  • the model-specific qwen3 patch forces enable_thinking=false for non-streaming requests
  • the budget field may still remain in the converted body because the override only changes the enable flag

7.1.12 OpenAI Chat -> Ali qwq-*

Input:

{
  "model": "qwq-plus",
  "reasoning_effort": "low",
  "stream": false,
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "enable_thinking": true,
  "thinking_budget": 2048,
  "stream": true
}

7.1.13 OpenAI Chat -> Zhipu

Input:

{
  "model": "glm-5.1",
  "reasoning_effort": "none",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "disabled"
  }
}

7.1.14 OpenAI Chat -> DeepSeek

Input:

{
  "model": "deepseek-chat",
  "reasoning_effort": "high",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "enabled"
  }
}

7.1.15 OpenAI Chat -> Doubao

Input:

{
  "model": "doubao-seed-1-6",
  "reasoning_effort": "none",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "disabled"
  }
}

7.1.16 OpenAI Chat -> Doubao with deepseek-reasoner

Input:

{
  "model": "deepseek-reasoner",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "messages": [
    {
      "role": "system",
      "content": "ๅ›ž็ญ”ๅ‰๏ผŒ้ƒฝๅ…ˆ็”จ <think></think> ่พ“ๅ‡บไฝ ็š„ๆ€่€ƒ่ฟ‡็จ‹ใ€‚"
    },
    {
      "role": "user",
      "content": "hello"
    }
  ]
}

Notes:

  • this is not an effort conversion example
  • it is still part of the implemented reasoning-related behavior in the Doubao adaptor

7.1.17 OpenAI Chat -> Moonshot / Kimi K2.6

Input:

{
  "model": "kimi-k2.6",
  "reasoning_effort": "none",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "disabled"
  }
}

Notes:

  • reasoning_effort is removed
  • enabled efforts become thinking.type=enabled
  • budget and fine-grained effort are not preserved

7.1.18 OpenAI Chat -> Moonshot / Kimi non-toggle model

Input:

{
  "model": "kimi-k2-thinking",
  "reasoning_effort": "none",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "messages": [{"role": "user", "content": "hello"}]
}

Notes:

  • reasoning_effort is removed
  • thinking is omitted because this model family does not support toggling via request parameter

7.1.19 OpenAI Completions -> Ali

Input:

{
  "model": "glm-4.5",
  "reasoning_effort": "low",
  "prompt": "hello"
}

Output:

{
  "enable_thinking": true,
  "thinking_budget": 2048
}

7.1.20 OpenAI Chat -> Qianfan, disabling reasoning on an enable_thinking model

Input:

{
  "model": "qwen3-14b",
  "reasoning_effort": "none",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "enable_thinking": false
}

Notes:

  • reasoning_effort:none is removed
  • the Qianfan adaptor expresses disabled reasoning according to the target model's field family; qwen3-* uses enable_thinking=false

7.1.21 OpenAI Chat -> Qianfan DeepSeek v4 with enabled reasoning

Input:

{
  "model": "deepseek-v4-pro",
  "reasoning_effort": "xhigh",
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "enabled"
  },
  "thinking_budget": 16384
}

Notes:

  • DeepSeek models use Qianfan thinking.type.
  • DeepSeek v4 also supports thinking_budget, so enabled reasoning includes the budget derived from effort and clamped to [100, 16384].
  • reasoning_effort is removed after conversion.

7.1.22 OpenAI Chat -> Qianfan with native thinking

Input:

{
  "model": "deepseek-v3.2",
  "reasoning_effort": "none",
  "thinking": {
    "type": "enabled"
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "enabled"
  }
}

Notes:

  • native thinking wins
  • conflicting reasoning_effort, enable_thinking, and thinking_budget are removed

7.1.23 OpenAI Completions -> Qianfan

Input:

{
  "model": "qwen3-14b",
  "reasoning_effort": "low",
  "prompt": "hello"
}

Output:

{
  "enable_thinking": true,
  "thinking_budget": 2048
}

7.2 Gemini native requests as the source format

7.2.1 Gemini -> OpenAI Chat / Completions

Input:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 2048,
      "includeThoughts": true
    }
  },
  "contents": [{"role": "user", "parts": [{"text": "hello"}]}]
}

Output:

{
  "reasoning_effort": "low",
  "messages": [{"role": "user", "content": "hello"}]
}

7.2.2 Gemini -> OpenAI Responses

Input:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingLevel": "high"
    }
  },
  "contents": [{"role": "user", "parts": [{"text": "hello"}]}]
}

Output:

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

7.2.3 Gemini -> Anthropic official

Input:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 2048,
      "includeThoughts": true
    }
  },
  "contents": [{"role": "user", "parts": [{"text": "hello"}]}]
}

Output for an older Claude model:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  }
}

7.2.4 Gemini -> Anthropic adaptive Claude

Input:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 2048,
      "includeThoughts": true
    }
  },
  "contents": [{"role": "user", "parts": [{"text": "hello"}]}]
}

Output for claude-opus-4-7:

{
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "low"
  }
}

7.2.5 Gemini -> AWS Bedrock Claude

Representative output body:

{
  "anthropic_version": "bedrock-2023-05-31",
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  }
}

7.2.6 Gemini -> Vertex AI Claude

Representative output body:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  }
}

7.2.7 Gemini -> Ali

Input:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 2048,
      "includeThoughts": true
    }
  },
  "contents": [{"role": "user", "parts": [{"text": "hello"}]}]
}

Output:

{
  "enable_thinking": true,
  "thinking_budget": 2048
}

7.2.8 Gemini -> Zhipu / DeepSeek / Doubao

Input:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 2048,
      "includeThoughts": true
    }
  }
}

Output:

{
  "thinking": {
    "type": "enabled"
  }
}

Notes:

  • budget details are not preserved
  • the payload degrades to pure on/off semantics

7.2.9 Gemini -> Moonshot / Kimi K2.6

Input:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 2048,
      "includeThoughts": true
    }
  }
}

Output:

{
  "thinking": {
    "type": "enabled"
  }
}

Notes:

  • Gemini thinkingConfig is first normalized through the OpenAI-compatible reasoning_effort path, then the Moonshot hook writes Kimi thinking
  • budget details are not preserved

7.2.10 Gemini -> Qianfan

Input:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 0
    }
  },
  "contents": [{"role": "user", "parts": [{"text": "hello"}]}]
}

Output:

{
  "enable_thinking": false
}

Notes:

  • Gemini thinkingBudget<=0 is normalized to none
  • Qianfan writes disabled states according to the model field family: enable_thinking=false, thinking.type=disabled, or no reasoning field
  • enabled Gemini budgets first become normalized effort, then Qianfan emits the field family supported by the target model

7.3 Claude / Anthropic requests as the source format

7.3.1 Claude -> OpenAI Chat / Completions

Input:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "reasoning_effort": "low",
  "messages": [{"role": "user", "content": "hello"}]
}

7.3.2 Claude adaptive -> OpenAI Chat / Completions

Input:

{
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "high"
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "reasoning_effort": "high"
}

7.3.3 Claude -> OpenAI Responses

Input:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "reasoning": {
    "effort": "low"
  }
}

7.3.4 Claude -> Gemini

Input:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 16384
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Output for gemini-2.5-pro:

{
  "generationConfig": {
    "thinkingConfig": {
      "thinkingBudget": 16384,
      "includeThoughts": true
    }
  }
}

7.3.5 Native Anthropic -> Anthropic official

Input:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "enabled",
    "budget_tokens": 2048
  }
}

Notes:

  • this path preserves native Claude thinking fields
  • it does not rewrite them into another thinking dialect

7.3.6 Native Anthropic -> AWS / Vertex Claude wrappers

Input:

{
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "low"
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Representative AWS wrapper:

{
  "anthropic_version": "bedrock-2023-05-31",
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "low"
  }
}

Representative Vertex body:

{
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "low"
  }
}

7.3.7 Claude -> Moonshot / Kimi K2.6

Input:

{
  "thinking": {
    "type": "disabled"
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "thinking": {
    "type": "disabled"
  }
}

Notes:

  • Claude thinking is first normalized through the OpenAI-compatible reasoning_effort path, then the Moonshot hook writes Kimi thinking
  • budget and adaptive effort details are not preserved by the Kimi target

7.3.8 Claude -> Qianfan

Input:

{
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "high"
  },
  "messages": [{"role": "user", "content": "hello"}]
}

Output:

{
  "enable_thinking": true,
  "thinking_budget": 16384
}

Notes:

  • Claude thinking / output_config are first normalized through the OpenAI-compatible path
  • Qianfan then writes according to the target model's field family; the example qwen3-* target uses enable_thinking and thinking_budget
  • disabled Claude thinking is expressed according to the target model's field family, or omitted when the model cannot disable reasoning

7.4 OpenAI Responses requests as the source format

7.4.1 OpenAI Responses -> Qianfan

Input:

{
  "model": "deepseek-v3.2",
  "input": "hello",
  "reasoning": {
    "effort": "none"
  }
}

Output:

{
  "model": "deepseek-v3.2",
  "input": "hello",
  "thinking": {
    "type": "disabled"
  }
}

Notes:

  • reasoning.effort:none is removed
  • the example target model supports thinking, so Qianfan receives thinking.type=disabled
  • if a Responses request already contains native thinking, that native thinking wins and reasoning is removed

8. What this feature does not do

This feature intentionally does not do the following:

  • it does not parse thinking dialects that do not belong to the current request mode
    • for example, OpenAI Chat mode does not parse Gemini thinkingConfig
    • Gemini mode does not parse Claude thinking
  • it does not migrate every native request into a different thinking dialect
  • it does not guarantee that every upstream preserves detailed budget and effort semantics
    • especially for Zhipu / Doubao / DeepSeek, which currently preserve only enabled / disabled
  • it does not automatically add reasoning compatibility to adaptors that do not yet install the required hooks

9. Maintenance guidance

If a new vendor or request format needs thinking compatibility in the future, the recommended workflow is:

  1. define the native parsing entry for that request mode
  2. normalize it into NormalizedReasoning
  3. write it back using the actual upstream-supported schema
  4. only apply constraints and validity fixes to the converted request body
  5. document the model-name matching order for capability checks; use origin-first / actual-fallback by default, but use ActualModel when the final upstream model name determines parameter validity
  6. add tests for at least:
    • explicit disable
    • old-model vs new-model behavior
    • budget minimum and maximum limits
    • max_tokens / maxOutputTokens interactions
    • OriginModel match and ActualModel fallback match