Skip to content

Reasoning Effort Parameter

This document describes how Continuum Router handles the reasoning_effort parameter across different LLM backends, including the token budget conversions and supported effort levels.

Overview

The reasoning_effort parameter controls the amount of computational effort a model spends on reasoning before generating a response. Different backends implement this feature differently:

  • OpenAI: Native reasoning_effort parameter for O-series and GPT-5.x thinking models
  • Anthropic: Converted to thinking.budget_tokens for Claude models with extended thinking, or to {"type": "adaptive"} for auto effort
  • Gemini: Native reasoning_effort for thinking-capable models via OpenAI-compatible endpoint
  • Other backends: Pass-through (no transformation)

Parameter Formats

Continuum Router supports two input formats, both normalized internally:

Flat Format (Chat Completions API)

{
  "model": "o3-mini",
  "reasoning_effort": "high",
  "messages": [...]
}

Nested Format (Responses API)

{
  "model": "o3-mini",
  "reasoning": {
    "effort": "high"
  },
  "input": "..."
}

Both formats are automatically normalized to the flat reasoning_effort format before processing. If both are present, the flat format takes precedence.

GPT-5.6 Native Responses Controls

For a backend that exposes the native Responses API, Continuum Router preserves the newer GPT-5.6 request surface verbatim:

  • reasoning.mode (standard or pro) and reasoning.context (auto, current_turn, or all_turns)
  • text.verbosity, safety_identifier, and include
  • prompt_cache_key, prompt_cache_options, and explicit prompt_cache_breakpoint markers on input_text, input_image, and input_file
  • Programmatic Tool Calling tools and replay items (program, program_output, and caller-linked function outputs)
  • Beta Multi-agent configuration and output items
{
  "model": "gpt-5.6-sol",
  "input": "Continue the investigation.",
  "previous_response_id": "resp_123",
  "reasoning": {
    "mode": "pro",
    "effort": "max",
    "context": "all_turns"
  },
  "text": {
    "verbosity": "high"
  },
  "safety_identifier": "usr_privacy_preserving_hash"
}

Native previous_response_id values are forwarded unchanged, allowing OpenAI to reuse persisted reasoning. Conversion backends still use the router's local session expansion. Requests containing native-only fields fail with unsupported_request_parameter when routing selects a conversion strategy instead of silently dropping fields. Stateful and native-only requests bypass the router's local response cache.

Multi-agent raw HTTP clients must send OpenAI-Beta: responses_multi_agent=v1; the router forwards this header while continuing to filter client credentials and transport headers.

Direct Token Budget Specification

For advanced use cases, you can specify the token budget directly instead of using reasoning_effort levels:

Anthropic: Direct thinking Parameter

{
  "model": "claude-sonnet-4-5-20250929",
  "thinking": {
    "type": "enabled",
    "budget_tokens": 16000
  },
  "messages": [...]
}

For adaptive thinking (model decides when and how much to think):

{
  "model": "claude-opus-4-6",
  "thinking": {
    "type": "adaptive"
  },
  "messages": [...]
}

Gemini: Direct thinking_budget via extra_body

{
  "model": "gemini-2.5-pro",
  "extra_body": {
    "google": {
      "thinking_config": {
        "thinking_budget": 10000,
        "include_thoughts": true
      }
    }
  },
  "messages": [...]
}

Priority

If both reasoning_effort and direct token specification are present, the direct specification takes precedence for Anthropic (thinking parameter). For Gemini, both can coexist but thinking_budget in extra_body is used for fine-grained control.


Fitting a derived reasoning effort

The Anthropic Messages ingress (POST /anthropic/v1/messages) derives a reasoning_effort from the client's thinking and output_config.effort fields when it routes a request to an OpenAI-wire backend. The derived level is fitted to what the selected target is known to accept before the request is sent, on the Chat Completions wire (reasoning_effort) and on the Responses wire (reasoning.effort). The dispatch-side conversion for a native Anthropic thinking request sent to /v1/chat/completions applies the same fit.

The vocabulary is resolved in the following order, and the first source that answers wins:

  1. Backend declaration: backends[].model_configs[].metadata.reasoning_efforts. This is the operator's statement about the engine actually serving the model.
  2. Model metadata: metadata.reasoning_efforts in model-metadata.yaml or a model-metadata.d/ drop-in.
  3. Backend-type default:
    • openai and azure use the built-in OpenAI reasoning-family table. A model the table does not describe keeps the derived value.
    • generic, gemini, vllm, ollama, llamacpp, mlxcel, lmstudio, sglang, and tensorrtllm use low, medium, and high, with both high and xhigh sent as high.
  4. Passthrough: no source describes the target, so the derived value is forwarded unchanged.

When the fit changes the value, the router logs it at debug level with vocabulary_source set to backend_model_config, model_metadata, or backend_type_default, together with backend, model, effort_field, and the fitted reasoning_effort. A request that reaches passthrough logs the same fields with vocabulary_source=passthrough.

The fit never rewrites a client-supplied reasoning_effort on /v1/chat/completions. It applies only to values the router derived on the Anthropic ingress and in the dispatch-side conversion.


Backend-Specific Behavior

OpenAI Backend

OpenAI models support the reasoning_effort parameter natively. The router passes the value directly to the OpenAI API.

Supported Effort Levels

Effort Level Supported Models Description
none GPT-5.x models that expose it Disable reasoning
minimal Original GPT-5 family Minimum reasoning effort
low O-series, GPT-5.x thinking Minimal reasoning, faster responses
medium O-series, GPT-5.x thinking Balanced reasoning effort
high O-series, GPT-5.x thinking Deep reasoning, slower responses
xhigh GPT-5.2 through GPT-5.6 reasoning tiers, GPT-6 Astra Extra-high reasoning effort
max GPT-5.6 family and GPT-6 Astra Maximum reasoning effort

Models Supporting reasoning_effort

O-series models (support low, medium, high):

  • o1, o1-mini, o1-preview
  • o3, o3-mini, o3-pro
  • o4-mini

GPT-6 models:

  • gpt-6-astra (low, medium, high, xhigh, and max). none returns a 400 from OpenAI, so this is the first OpenAI reasoning model with no way to turn reasoning off.
  • gpt-6-sol and gpt-6-luna (none through max, default medium). Unlike Astra they accept none.

GPT-5.x thinking models (exact levels vary by generation):

  • gpt-5.6, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna (none through max)
  • gpt-5.2 through gpt-5.5 reasoning tiers (none, low, medium, high, xhigh)
  • gpt-5.1 reasoning tiers (none, low, medium, high)
  • gpt-5, gpt-5-mini, and gpt-5-nano (minimal, low, medium, high)
  • gpt-5-pro (high only; Responses API only)
  • gpt-5.2-pro, gpt-5.4-pro, and gpt-5.5-pro (medium, high, xhigh; Responses API only)

Exact model validation

Streaming and non-streaming Chat Completions requests use the same model-specific validation. Unsupported pairs such as GPT-5 + xhigh, GPT-5.5 + max, or any OpenAI model + auto return 400 invalid_request_error. The router never silently changes the requested OpenAI effort.

Self-hosted engines declared type: openai

For a model that is not a known OpenAI reasoning family, the router strips reasoning and reasoning_effort only when the backend is the OpenAI cloud (api.openai.com) or type: azure, since those reject the field. A type: openai backend on any other URL (vLLM, SGLang, llama.cpp and similar) receives the client's value unchanged on both streaming and non-streaming requests. A strip that does happen is logged at info.

Function tools with reasoning effort

OpenAI refuses /v1/chat/completions on some models when the request carries function tools and the effective reasoning effort is anything other than none:

400 Function tools with reasoning_effort are not supported for gpt-5.6-sol in
    /v1/chat/completions. To use function tools, use /v1/responses or set
    reasoning_effort to 'none'.

The restriction is per model, not per family, and it is not the same rule in both affected generations:

Model Tools + effort omitted Tools + low/medium/high Tools + none No tools
gpt-6-astra Refused Refused Refused by the model Accepted
gpt-6-sol, gpt-6-luna Refused Refused Accepted Accepted
gpt-5.6-sol (alias gpt-5.6), gpt-5.6-terra, gpt-5.6-luna Refused Refused Accepted Accepted
gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.4-nano Accepted Refused Accepted Accepted
gpt-5.2 and earlier, O-series Accepted Accepted Accepted Accepted

The gpt-5.6 row differs because the upstream default effort for that family is medium: omitting the field does not mean "no reasoning" there, so the refusal fires on an omitted effort too.

GPT-6 Astra is stricter again: it rejects reasoning_effort: "none" on every request, with or without tools, so the second remedy OpenAI's own error text names does not exist for it. Every tool-carrying Chat Completions request for that model is bridged, and chat_completions_default_reasoning_effort: "medium" on its entry serves as a non-none placeholder since OpenAI does not publish the effort applied when the field is omitted.

GPT-6 Sol and Luna behave like the gpt-5.6 family: they accept none, OpenAI documents that Chat Completions takes function tools only at none, and their default effort is medium, so an omitted effort is bridged too.

Every cell above except the GPT-6 rows was measured against the live API on 2026-09-03; the GPT-6 rows are read from OpenAI's model documentation and refusal text. The split does not follow a name prefix in either direction: the whole GPT-5.4 generation is affected including its mini and nano tiers, while GPT-5.2 and earlier are not, so the rule is stored as data on each model entry rather than a family check.

The router bridges only the refused requests. When a model carries chat_completions_tools_require_none_reasoning, the request has a non-empty tools array, and the effective effort would be refused, the router dispatches to /v1/responses and translates the reply back into the Chat Completions shape (including streaming). The requested effort rides through as reasoning.effort, an omitted effort stays omitted, and the client receives a standard chat.completion or chat.completion.chunk response with its tool calls.

Every other request goes straight to /v1/chat/completions, including reasoning_effort: "none" with tools, any request without tools, and every unaffected model. Validation still runs first, so an effort outside the model's supported set returns the router's own 400 before reaching the bridge. The router does not rewrite the requested effort to none, which would silently change model behavior.

Two metadata keys drive this behavior on the model entry next to responses_only:

Key Type Meaning
chat_completions_tools_require_none_reasoning bool (default false) Upstream refuses function tools with a non-none effort on /v1/chat/completions for this model.
chat_completions_default_reasoning_effort string (optional) The effort upstream applies when the request omits reasoning_effort. Set to medium on the gpt-5.6 and GPT-6 entries so an omitted effort is bridged too; unset on gpt-5.4 and gpt-5.5. The router never sends this value.

Both ship set on the affected entries (gpt-6-astra, gpt-6-sol, gpt-6-luna, gpt-5.6-sol and its gpt-5.6 alias, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.4-nano), in the built-in OpenAI registry and in model-metadata.yaml. Because they are ordinary metadata, a model-metadata.d/ drop-in can clear them the day OpenAI lifts the restriction, without waiting for a router release:

# model-metadata.d/90-lift-tools-reasoning-bridge.yaml
models:
  - id: gpt-5.6-sol
    metadata:
      chat_completions_tools_require_none_reasoning: false

Operators can watch the split on the responses_bridge_total metric, whose reason label separates tools_with_reasoning from an unconditionally bridged responses_only_model.

Interaction with responses_only: true

Setting responses_only: true on one of these models routes every request for that model through /v1/responses, including plain chat with no tools. continuum-router config validate warns when responses_only: true is set on one of these models in backends[].model_configs[], pointing at chat_completions_tools_require_none_reasoning instead.

Models NOT Supporting reasoning_effort

The following models do not support reasoning parameters (parameter is stripped):

  • GPT-4o, GPT-4o-mini, GPT-4-turbo, GPT-4
  • GPT-5.2-chat-latest, GPT-5.2-instant (non-thinking variants)
  • GPT-3.5-turbo
  • Embedding models, Image models

Anthropic Backend (Claude)

Anthropic Claude models use different mechanisms depending on the model generation:

  • Claude Fable 5 and Mythos 5 (Mythos-class; Mythos 5 is the safeguards-lifted, limited-release variant of Fable 5): Use adaptive thinking ({"type": "adaptive"}) with output_config.effort, matching Opus 4.8 (legacy budget_tokens rejected with HTTP 400; temperature/top_p/top_k dropped automatically; max effort supported). An explicit thinking.type == "disabled" is also rejected (HTTP 400), so the router omits the thinking parameter entirely instead of forwarding it.
  • Claude Opus 5.5 (claude-opus-5-5): Thinking is always on using adaptive thinking with output_config.effort (low through max; Anthropic's default is medium, one level below Opus 5). Both explicit thinking.type == "disabled" and legacy budget_tokens are rejected (HTTP 400) at every effort, so the router omits disabled-thinking configs and normalizes enabled to adaptive. Forced tool use is removed as on Fable 5.1 (see below).
  • Claude Sonnet 5.5 (claude-sonnet-5-5): Uses adaptive thinking with output_config.effort (low through max). xhigh maps to max. Explicit thinking.type == "disabled" and forced tool use (tool_choice any or tool) are rejected, so the router omits disabled-thinking configs and rejects a forced choice before dispatch.
  • Claude 4.8+ models (Opus 4.8): Use adaptive thinking ({"type": "adaptive"}) with output_config.effort to control thinking depth. The legacy thinking.type == "enabled" + budget_tokens shape is rejected (HTTP 400). Sampling parameters temperature, top_p, and top_k are dropped automatically. Effort default is high.
  • Claude Haiku 5.5 (claude-haiku-5-5, no -latest alias): Uses adaptive thinking ({"type": "adaptive"}, on by default) with output_config.effort (low through max; Anthropic's default is medium). The router converts the legacy thinking.type == "enabled" + budget_tokens shape to adaptive and drops temperature, top_p, and top_k, which Haiku 5.5 rejects with HTTP 400. xhigh maps to max. An explicit thinking.type == "disabled" is accepted and forwarded.
  • Claude Haiku 4.5 (claude-haiku-4-5-*): Uses thinking.budget_tokens for extended thinking, like Sonnet 4.x. It has no effort parameter.
  • Claude Sonnet 5 (claude-sonnet-5-*, alias claude-sonnet-5-latest): Uses adaptive thinking ({"type": "adaptive"}) with output_config.effort. The legacy thinking.type == "enabled" + budget_tokens shape is rejected (HTTP 400), and temperature, top_p, and top_k are dropped automatically. xhigh downgrades to "high" (max is Opus/Mythos-class only). Effort default is high.
  • Claude 4.6+ adaptive models (Opus 4.6, Sonnet 4.6, Opus 4.7): Use adaptive thinking ({"type": "adaptive"}) with output_config.effort to control thinking depth. Claude Opus 4.7 requires this API; sending the legacy thinking.type == "enabled" + budget_tokens shape to Opus 4.7 produces HTTP 400.
  • Pre-4.6 models (Opus 4.5, Sonnet 4, etc.): Use thinking.budget_tokens for extended thinking.

Conversion Table: Claude 4.6+ Models (Adaptive Thinking)

Claude 4.6+ adaptive models (claude-opus-4-6-*, claude-sonnet-4-6-*, claude-opus-4-7-*, claude-opus-4-8-*, claude-sonnet-5-*, claude-haiku-5-5, claude-fable-5-*, claude-mythos-5-*) use output_config.effort instead of budget_tokens:

reasoning_effort thinking output_config.effort Description
none disabled (omitted) Thinking disabled
minimal {"type": "adaptive"} "low" Mapped to low (no minimal level in Anthropic API)
auto {"type": "adaptive"} (omitted) Model uses default effort (high)
low {"type": "adaptive"} "low" Low thinking depth
medium {"type": "adaptive"} "medium" Moderate thinking depth
high {"type": "adaptive"} "high" High thinking depth
xhigh (Opus 4.6+, Sonnet 5.5+, Haiku 5.5, Fable 5, Mythos 5) {"type": "adaptive"} "max" Maximum thinking depth
xhigh (Sonnet 4.6, Sonnet 5) {"type": "adaptive"} "high" Downgraded (max is Opus/Mythos-class-only)

Conversion Table: Pre-4.6 Claude Models (Budget Tokens)

Pre-4.6 models use thinking.budget_tokens:

reasoning_effort Anthropic thinking Description
none disabled Thinking feature disabled
minimal {"type": "enabled", "budget_tokens": 1024} Minimum allowed budget
auto {"type": "adaptive"} Model decides when and how much to think
low {"type": "enabled", "budget_tokens": 4096} Light reasoning
medium {"type": "enabled", "budget_tokens": 10240} Moderate reasoning
high {"type": "enabled", "budget_tokens": 32768} Deep reasoning
xhigh {"type": "enabled", "budget_tokens": 32768} Falls back to high budget

Transformation Example: Claude 4.6+ (Adaptive Thinking + Effort)

Input (OpenAI format):

{
  "model": "claude-opus-4-6",
  "reasoning_effort": "high",
  "messages": [...]
}

Transformed (Anthropic format):

{
  "model": "claude-opus-4-6",
  "thinking": {
    "type": "adaptive"
  },
  "output_config": {
    "effort": "high"
  },
  "messages": [...]
}

For xhigh on Opus 4.6 or Opus 4.7, output_config.effort is set to "max":

{
  "model": "claude-opus-4-6",
  "thinking": {"type": "adaptive"},
  "output_config": {"effort": "max"},
  "messages": [...]
}

Transformation Example: Pre-4.6 Claude (Budget Tokens)

Input (OpenAI format):

{
  "model": "claude-sonnet-4-5-20250929",
  "reasoning_effort": "high",
  "messages": [...]
}

Transformed (Anthropic format):

{
  "model": "claude-sonnet-4-5-20250929",
  "thinking": {
    "type": "enabled",
    "budget_tokens": 32768
  },
  "messages": [...]
}

Adaptive Thinking (Auto)

When reasoning_effort is set to "auto", the router emits {"type": "adaptive"} without specifying output_config.effort. This lets Anthropic use its default effort level (currently high) and lets the model dynamically decide when and how much reasoning to apply.

Input (OpenAI format):

{
  "model": "claude-opus-4-6",
  "reasoning_effort": "auto",
  "messages": [...]
}

Transformed (Anthropic format):

{
  "model": "claude-opus-4-6",
  "thinking": {
    "type": "adaptive"
  },
  "messages": [...]
}

Direct output_config Pass-Through

When a request already contains explicit thinking and output_config parameters, they are passed through directly without conversion:

{
  "model": "claude-opus-4-6",
  "thinking": {"type": "adaptive"},
  "output_config": {"effort": "medium"},
  "messages": [...]
}

Adaptive Thinking Availability

Adaptive thinking with output_config.effort is available on Claude 4.6+ adaptive models (claude-opus-4-6-*, claude-sonnet-4-6-*, claude-opus-4-7-*, claude-opus-4-8-*, claude-sonnet-5-*, claude-haiku-5-5) and the Mythos-class family (claude-fable-5-*, claude-mythos-5-*). Claude Opus 4.7/4.8, Claude Sonnet 5, Claude Haiku 5.5, and the Mythos-class models require this API; the router normalizes explicit legacy thinking.type == "enabled" requests for these models to avoid upstream HTTP 400 errors. Pre-4.6 models use budget_tokens instead.

max Effort Level

The "max" effort level in output_config.effort is available on Opus 4.6 and later, Sonnet 5.5 and later, Claude Haiku 5.5, and the Mythos-class family (Fable 5, Mythos 5). When xhigh is requested for Sonnet 5 or Sonnet 4.6, the router automatically downgrades it to "high" with an info-level log message.

Claude Opus 4.7/4.8, Sonnet 5, Haiku 5.5, and Mythos-class Sampling Parameter Deprecation

Anthropic deprecated temperature, top_p, and top_k for Claude Opus 4.7, Opus 4.8, Sonnet 5, Haiku 5.5, Fable 5, and Mythos 5. The router automatically drops all three parameters before forwarding to any claude-opus-4-7-*, claude-opus-4-8-*, claude-sonnet-5-*, claude-haiku-5-5, claude-fable-5-*, or claude-mythos-5-* model to avoid HTTP 400 errors.

anthropic-version: 2026-10-21 and Sampling Parameters

Under anthropic-version 2026-10-21 or later, the default of the Anthropic SDK majors released that day, temperature, top_p, and top_k return HTTP 400 on every Claude model. The native /anthropic/v1/messages ingress forwards the client's anthropic-version to a native Anthropic backend, and sends the backend's anthropic_version when the client sent none; the router does not write a sampling value into a request sent under such a version.

  • Policy values: A sampling default or override remains a router-authored HTTP 400. On native Messages requests, a limit sees client sampling as absent when every eligible backend drops it. Mixed provider candidates retain the conservative capability check, so an unsupported governed value is refused.
  • Client values: Native Messages drops the three fields on each Anthropic hop that forbids them, including fallback and Unix-socket sends. Other providers retain the client values. Bedrock behavior is unchanged.
  • Converted requests: A backend configured with anthropic_version: "2026-10-21" receives OpenAI and Responses requests without the three fields, and a policy value for one of them is refused the same way.
  • Older versions: Under 2023-06-01, or with no anthropic-version header on a backend left at the default, policy values apply as before.

Mythos-class Disabled-Thinking Rejection

Claude Fable 5, Mythos 5, Claude Opus 5.5 and later, and Claude Sonnet 5.5 and later reject an explicit thinking: {"type": "disabled"} with HTTP 400 at every effort level. When a client sends a disabled-thinking config to these models, the router omits the thinking parameter entirely instead of forwarding it. Opus 4.8 and earlier and Sonnet 5 and earlier accept an explicit disabled-thinking config unchanged; Opus 5 accepts it only up to effort high (see below).

Claude Opus 5 Disabled Thinking Above high Effort

Claude Opus 5 accepts an explicit thinking: {"type": "disabled"} config only while output_config.effort stays at high or below. Pairing it with xhigh or max is rejected by Anthropic with HTTP 400.

  • Ingress validation: The router rejects this combination with HTTP 400 before any provider round-trip on /v1/chat/completions (streaming and non-streaming) and /anthropic/v1/messages. The Responses-to-Anthropic converter carries the same guard on its assembled payload, though /v1/responses expresses "no thinking" as reasoning.effort: "none" (which omits thinking rather than disabling it explicitly). Validation runs per request, catching later turns that raise effort while thinking stays disabled.
  • Ungated surfaces: /anthropic/v1/messages/count_tokens is left ungated because it neither generates nor bills tokens, and Bedrock Converse (endpoint_type: converse) forwards extra_body/additionalModelRequestFields verbatim by design.
  • No silent rewrite: The error body lists both remedies: lower output_config.effort to high or below, or remove the disabled-thinking config to run adaptive thinking at the requested effort. The router does not pick one automatically because Opus 5 with thinking disabled has two documented failure modes: emitting tool calls as plain text instead of structured tool_use blocks, and leaking <thinking> tags into the output.

Mythos-class 5.1 and Opus 5.5 Forced Tool Choice Removal

Claude Fable 5.1, Claude Mythos 5.1, Claude Opus 5.5, and Claude Sonnet 5.5 reject forced tool use (tool_choice: {"type": "any"} and {"type": "tool", "name": ...}) with HTTP 400 (tool_choice: type "tool" and "any" are not supported for this model.). auto and none are unaffected, and disable_parallel_tool_use still works with auto. The gate keys on the parsed family version: Mythos-class >= 5.1, Opus >= 5.5, and Sonnet >= 5.5.

  • Ingress validation: The router rejects the combination with HTTP 400 before any provider round-trip across all three ingresses that can express a forced call: /v1/chat/completions (streaming and non-streaming, where OpenAI's "required" and {"type": "function", ...} map to Anthropic's any and tool), /anthropic/v1/messages, and the Responses-to-Anthropic converter.
  • Ungated surfaces: /anthropic/v1/messages/count_tokens and Bedrock Converse (additionalModelRequestFields) remain ungated for the same reasons as above.
  • No silent downgrade: The error body names the model, echoes the client's spelling, and lists Anthropic's documented remedies: use tool_choice auto with an instruction naming the tool, set strict: true on the tool definition, or request structured output. The router never downgrades a forced choice to auto, which would allow plain-text replies and break agent loops that depend on a guaranteed tool call.

Models Supporting Extended Thinking

Extended thinking is supported by:

  • Claude Fable 5 / Mythos 5 (adaptive thinking + effort; max supported; sampling params dropped; explicit disabled-thinking omitted): claude-fable-5-* (alias claude-fable-5-latest), claude-mythos-5-* (alias claude-mythos-5-latest, limited release)
  • Claude Sonnet 5.5 (adaptive thinking + effort; max supported; sampling params dropped; explicit disabled-thinking omitted; forced tool choice rejected): claude-sonnet-5-5
  • Claude Opus 4.8 (adaptive thinking + effort; sampling params deprecated; effort default high): claude-opus-4-8-*, alias claude-opus-4-8-latest
  • Claude Sonnet 5 (adaptive thinking + effort; sampling params dropped; xhigh downgraded to high; effort default high): claude-sonnet-5-*, alias claude-sonnet-5-latest
  • Claude Opus 4.7 (adaptive thinking + effort; sampling params deprecated): claude-opus-4-7-*
  • Claude Haiku 5.5 (adaptive thinking + effort; max supported; sampling params dropped; effort default medium): claude-haiku-5-5
  • Claude 4.6 family (adaptive thinking + effort): claude-opus-4-6-*, claude-sonnet-4-6-*
  • Claude Opus models (budget tokens): claude-opus-4-*, claude-opus-4-5-*
  • Claude Sonnet 4 models (budget tokens): claude-sonnet-4-*, claude-sonnet-4-5-*
  • Claude Haiku 4.5 (budget tokens): claude-haiku-4-5-*

Temperature Restriction

When extended thinking is enabled, Claude does not support custom temperature settings. The router automatically removes the temperature parameter when thinking is active. For Claude Opus 4.7, Opus 4.8, Haiku 5.5, Fable 5, and Mythos 5, temperature, top_p, and top_k are always dropped regardless of whether thinking is enabled.

xhigh for Pre-4.6 Claude

When xhigh is requested for pre-4.6 Claude models, the router automatically downgrades it to high (32,768 budget_tokens).

auto outside Anthropic

The auto effort level maps to Anthropic's adaptive thinking. OpenAI requests using auto are rejected because it is not an OpenAI effort value; Gemini retains its provider-specific auto to medium conversion.

Reverse Direction on Fallback Hops

Everything above describes the forward direction: an OpenAI reasoning_effort becoming a Claude thinking config. A cross-provider fallback hop can require the reverse. When a request carrying thinking or output_config falls back onto an OpenAI-wire backend, dispatch converts that config into a reasoning_effort value the selected target accepts before stripping the Anthropic-native fields, preserving the caller's reasoning intent across the hop.

During this reverse conversion, output_config.effort maps level for level (with max becoming xhigh where the target's vocabulary includes it), budget_tokens maps through the shared band table, adaptive thinking without an effort and disabled thinking emit nothing, and a client-supplied reasoning_effort always takes precedence. See Model fallback for the full band table, per-target vocabulary rules, and local-engine behavior.

The Anthropic Messages API ingress (/anthropic/v1/messages) applies the same bands through the same function on both its Chat Completions transform and the Responses API transform used for responses_only models. A budget_tokens value therefore resolves to a single reasoning level across the router, whether the request arrived via /anthropic/v1/messages or fell back onto an OpenAI-wire backend.


Gemini Backend

Gemini models support reasoning_effort natively through their OpenAI-compatible endpoint. The router validates and passes the value directly.

Supported Effort Levels

Effort Level Supported Models Description
none Every model except those flagged rejects_reasoning_effort_none Disable thinking
minimal All thinking models Minimal reasoning
low All thinking models Light reasoning
medium All thinking models Moderate reasoning
high All thinking models Deep reasoning

Which models accept none

none is carried through to Gemini unless the model's metadata says the provider refuses it. The refusal is a deny list, rejects_reasoning_effort_none in model-metadata.yaml, and it ships set on the models measured to refuse:

  • gemini-3.6-flash
  • gemini-3.5-flash-lite
  • every Gemini Pro tier

Everything else, including a model released after the router build, is forwarded and answered by Gemini so that newly released Gemini minor versions retain none support without waiting for a router release.

Support does not follow a family prefix. Measured against the OpenAI-compatible endpoint on 2026-09-10, one reasoning_effort: "none" request per catalog id: 2.5 Flash, 2.5 Flash-Lite, 3 Flash, 3.1 Flash-Lite, 3.5 Flash, 3.7 Flash, and 3.8 Flash answered 200; gemini-3.5-flash-lite and gemini-3.6-flash answered 400 INVALID_ARGUMENT; the Pro tiers answered 400 Budget 0 is invalid. This model only works in thinking mode.; the 2.0 family answered 404, having been retired upstream.

The router's own 400 exists for the two Flash tiers, whose upstream error names neither the field nor the model. A Pro model's upstream message is already specific, so the flag there mainly saves the round trip. The check runs on the /v1/chat/completions ingress, before backend selection, so it covers streaming and non-streaming alike. An operator who disagrees with a row can set or clear it in a model-metadata.d/ drop-in without waiting for a release.

A fallback hop is fitted rather than gated. While the ingress check evaluates the client-requested model, a hop lands on a different model chosen by the operator's chain, whose metadata may carry the flag even when the original did not. On such a hop the router drops reasoning_effort: "none" and logs it, serving a reasoned reply from the fallback model rather than failing the request. Because the substitute reasons, a small max_tokens sized for a non-reasoning answer may truncate the output.

The native thinkingConfig.thinkingLevel: "MINIMAL" field is a different question with a different answer: Gemini 3.8 Flash refuses it while Gemini 3.6 Flash accepts it, the reverse of the two rows here. Google documents the minimal level rather than this mapping, so a documentation statement about minimal must not be used to edit the deny list. Measure the id.

xhigh Automatic Fallback

When xhigh is requested for Gemini models, the router automatically downgrades it to high since Gemini doesn't support xhigh.

Additional Features

For Gemini thinking models, the router automatically:

  1. Sets include_thoughts: true to expose reasoning content
  2. Sets default max_completion_tokens: 16384 if not specified
  3. Validates effort level against model capabilities

All three apply on both routes in the same order, so a rejected effort returns the same 400 on streaming and non-streaming requests alike.

Which models get these injections is a table of families (THINKING_FAMILIES in src/infrastructure/backends/gemini/transform.rs), not a name substring. A version, date, or preview suffix reduces away so gemini-3.1-pro-preview and gemini-3.1-pro resolve to the same family, while a -lite segment does not reduce away because Flash and Flash-Lite are different models.

The thought summary itself needs the response side. Google returns it inline in message.content, wrapped in <thought>...</thought> and marked extra_content.google.thought: true, rather than in a field of its own. The router moves that text into message.reasoning_content and leaves content holding only the answer, so the markup never reaches the client and content is always present, empty rather than absent when the model produced nothing but thought.

Malformed function calls

Gemini intermittently ends a tool turn with finish_reason: "function_call_filter: MALFORMED_FUNCTION_CALL" (the native API spells it MALFORMED_FUNCTION_CALL): HTTP 200, an assistant message carrying neither content nor tool_calls, and completion_tokens: 0. The model emitted something meant to be a function call and the provider could not parse it. Measured on 2026-09-10, one call in two and one call in five on gemini-3.1-pro-preview with a single tool; every other call returned a proper tool_calls response.

The router refuses to forward that as a success. It becomes a retryable upstream failure (502, type: upstream_error, whose message names malformed_function_call), so the retry loop and any configured fallback.fallback_chains hop engage. A retry succeeded in every observed case, and answering stop instead would hide a turn that produced nothing and still cost prompt tokens. On a streaming request the failure is detected after the response is committed, so it arrives as an SSE error event rather than an HTTP status.

Two shape guarantees hold on every Gemini route as a result. finish_reason is always one of the five OpenAI values (stop, length, tool_calls, content_filter, function_call), because one shared table maps the provider's spellings and reduces anything unrecognized to stop. An assistant message always carries a content key, null only alongside tool_calls and empty otherwise.


Generic/HTTP Backend

Generic HTTP backends (used for Ollama, vLLM, LocalAI, LM Studio, etc.) pass requests through without transformation.

Behavior Description
Pass-through reasoning_effort is forwarded as-is to the backend
No validation The router does not validate effort levels
No conversion No token budget conversion is performed

Backend Responsibility

For generic backends, the target LLM server is responsible for handling (or ignoring) the reasoning_effort parameter. If the server doesn't support it, it may return an error or ignore the parameter.


llama.cpp Backend

The llama.cpp backend uses pass-through behavior:

Behavior Description
Pass-through Parameters forwarded unchanged
Server-dependent Support depends on llama-server configuration

Thinking Model Support in llama.cpp

Recent versions of llama-server support thinking models (e.g., DeepSeek-R1) with <think> tag handling. However, there is no standardized API parameter for reasoning_effort or budget_tokens. The thinking behavior is typically controlled by:

  • Model's built-in chat template with thinking tags
  • Server-side configuration (e.g., --thinking-budget if available)
  • Sampling parameters like temperature and top-p

vLLM Backend

vLLM provides an OpenAI-compatible API but reasoning effort support depends on the model:

Behavior Description
Pass-through Parameters forwarded via generic backend
Model-dependent Support varies by model type

Thinking Model Support in vLLM

vLLM can run thinking-capable models (e.g., DeepSeek-R1, QwQ) but the reasoning_effort parameter handling depends on:

  • Whether the model supports structured thinking
  • vLLM server version and configuration
  • Model's chat template configuration

For DeepSeek-R1 and similar models, thinking is often implicit in the model's behavior rather than controlled by an explicit budget parameter.

Response Field Normalization

Newer vLLM versions emit reasoning text as choices[].delta.reasoning (streaming) or choices[].message.reasoning (non-streaming) rather than the canonical reasoning_content. The router normalizes this field on all Chat Completions relay paths, so downstream clients always see reasoning_content regardless of vLLM version. Backends that already use reasoning_content are not modified.


Summary Table

Backend Effort Levels Conversion Notes
OpenAI Model-specific subset of none, minimal, low, medium, high, xhigh, max None (native) Exact validation; no silent effort fallback
Anthropic (Fable 5 / Mythos 5) none, minimal, auto, low, medium, high, xhigh → adaptive + output_config.effort xhigh → max; drops temperature/top_p/top_k always; explicit disabled-thinking omitted
Anthropic (Opus 4.8) none, minimal, auto, low, medium, high, xhigh → adaptive + output_config.effort xhigh → max; drops temperature/top_p/top_k always; effort default high
Anthropic (Opus 4.7) none, minimal, auto, low, medium, high, xhigh → adaptive + output_config.effort xhigh → max; drops temperature/top_p/top_k always
Anthropic (Haiku 5.5) none, minimal, auto, low, medium, high, xhigh → adaptive + output_config.effort xhigh → max; drops temperature/top_p/top_k always; effort default medium
Anthropic (Sonnet 5.5) none, minimal, auto, low, medium, high, xhigh → adaptive + output_config.effort xhigh → max; drops temperature/top_p/top_k; explicit disabled thinking omitted
Anthropic (4.6) none, minimal, auto, low, medium, high, xhigh* → adaptive + output_config.effort *xhigh → max for Opus 4.6, high for Sonnet 4.6; removes temperature
Anthropic (pre-4.6) none, minimal, auto, low, medium, high → budget_tokens or adaptive auto maps to adaptive thinking; removes temperature
Gemini none*, auto**, minimal, low, medium, high None (native) *none only for Flash models; **auto downgraded to medium
vLLM Model-dependent Pass-through DeepSeek-R1, QwQ use implicit thinking
llama.cpp Model-dependent Pass-through Uses <think> tag in chat template
Generic Any Pass-through Backend handles validation

Response Format

When reasoning/thinking is enabled, the response includes the model's reasoning process:

OpenAI Format (with reasoning_content)

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "The answer is 42.",
      "reasoning_content": "Let me think through this step by step..."
    },
    "finish_reason": "stop"
  }]
}

Claude Extended Thinking (transformed to OpenAI format)

The router transforms Claude's thinking blocks to the reasoning_content field:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "The answer is 42.",
      "reasoning_content": "I need to consider multiple factors..."
    },
    "finish_reason": "stop"
  }]
}

Best Practices

  1. Use appropriate effort levels: Higher effort improves reasoning depth but increases latency and token cost.
  2. Check model support: Not all models support reasoning parameters.
  3. Handle upper effort levels carefully: GPT-5.2 through GPT-5.6 support xhigh, while only GPT-5.6 and GPT-6 support max; the router applies documented fallbacks for older models.
  4. Consider cost: Extended thinking consumes additional tokens (especially with Anthropic's budget_tokens).
  5. Test with your backend: Generic backends have varying support levels.

Stop sequences on reasoning requests

A backend may match stop inside hidden reasoning and finish before producing visible content. For requests that will reason, the router removes stop sequences from the final upstream payload and logs the removal at debug level. The removed stop sequence is not enforced by the router. Visible output may continue past it, and normal token limits still apply.

The decision uses the selected backend and model on each attempt, after backend body extensions:

Signal Behavior
Effective enabled/adaptive thinking or a nonzero reasoning effort Remove stop sequences
Effective disabled thinking, effort none/off, or Gemini thinking budget 0 Preserve stop sequences
Declared default reasoning effort Use that default when no explicit control is present
Reasoning capability or thinking-pattern metadata, with no off/default override Treat the model as reasoning
Adaptive Claude on the OpenAI-compatible wire Reason by default
Pre-adaptive Claude that requires thinking opt-in Preserve stops until thinking is enabled
No reasoning signal Preserve stop sequences

Gemini's includeThoughts: false hides thoughts; it does not disable reasoning. Native provider decisions use the controls actually emitted by the converter. Native Anthropic Messages without enabled/adaptive thinking preserve stops, including when Responses reasoning.effort: none omits thinking. The Gemini Responses converter does not currently forward reasoning.effort; that ignored field is not an effective off switch.

The same protection covers buffered and streaming OpenAI-compatible sends over HTTP or Unix sockets, converted Responses and Anthropic Messages requests, and native Gemini generation configuration. Fallback attempts re-evaluate the rule on a fresh request copy: a reasoning backend's removal does not erase a stop that a later non-reasoning backend can honor. Body extensions cannot reintroduce stops after this check.