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_effortparameter for O-series and GPT-5.x thinking models - Anthropic: Converted to
thinking.budget_tokensfor Claude models with extended thinking, or to{"type": "adaptive"}forautoeffort - Gemini: Native
reasoning_effortfor 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)¶
Nested Format (Responses API)¶
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(standardorpro) andreasoning.context(auto,current_turn, orall_turns)text.verbosity,safety_identifier, andincludeprompt_cache_key,prompt_cache_options, and explicitprompt_cache_breakpointmarkers oninput_text,input_image, andinput_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):
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:
- Backend declaration:
backends[].model_configs[].metadata.reasoning_efforts. This is the operator's statement about the engine actually serving the model. - Model metadata:
metadata.reasoning_effortsinmodel-metadata.yamlor amodel-metadata.d/drop-in. - Backend-type default:
openaiandazureuse 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, andtensorrtllmuselow,medium, andhigh, with bothhighandxhighsent ashigh.
- 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-previewo3,o3-mini,o3-proo4-mini
GPT-6 models:
gpt-6-astra(low,medium,high,xhigh, andmax).nonereturns a400from OpenAI, so this is the first OpenAI reasoning model with no way to turn reasoning off.gpt-6-solandgpt-6-luna(nonethroughmax, defaultmedium). Unlike Astra they acceptnone.
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(nonethroughmax)gpt-5.2throughgpt-5.5reasoning tiers (none,low,medium,high,xhigh)gpt-5.1reasoning tiers (none,low,medium,high)gpt-5,gpt-5-mini, andgpt-5-nano(minimal,low,medium,high)gpt-5-pro(highonly; Responses API only)gpt-5.2-pro,gpt-5.4-pro, andgpt-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"}) withoutput_config.effort, matching Opus 4.8 (legacybudget_tokensrejected with HTTP 400;temperature/top_p/top_kdropped automatically;maxeffort supported). An explicitthinking.type == "disabled"is also rejected (HTTP 400), so the router omits thethinkingparameter entirely instead of forwarding it. - Claude Opus 5.5 (
claude-opus-5-5): Thinking is always on using adaptive thinking withoutput_config.effort(lowthroughmax; Anthropic's default ismedium, one level below Opus 5). Both explicitthinking.type == "disabled"and legacybudget_tokensare rejected (HTTP 400) at every effort, so the router omits disabled-thinking configs and normalizesenabledtoadaptive. Forced tool use is removed as on Fable 5.1 (see below). - Claude Sonnet 5.5 (
claude-sonnet-5-5): Uses adaptive thinking withoutput_config.effort(lowthroughmax).xhighmaps tomax. Explicitthinking.type == "disabled"and forced tool use (tool_choiceanyortool) 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"}) withoutput_config.effortto control thinking depth. The legacythinking.type == "enabled"+budget_tokensshape is rejected (HTTP 400). Sampling parameterstemperature,top_p, andtop_kare dropped automatically. Effort default ishigh. - Claude Haiku 5.5 (
claude-haiku-5-5, no-latestalias): Uses adaptive thinking ({"type": "adaptive"}, on by default) withoutput_config.effort(lowthroughmax; Anthropic's default ismedium). The router converts the legacythinking.type == "enabled"+budget_tokensshape to adaptive and dropstemperature,top_p, andtop_k, which Haiku 5.5 rejects with HTTP 400.xhighmaps tomax. An explicitthinking.type == "disabled"is accepted and forwarded. - Claude Haiku 4.5 (
claude-haiku-4-5-*): Usesthinking.budget_tokensfor extended thinking, like Sonnet 4.x. It has no effort parameter. - Claude Sonnet 5 (
claude-sonnet-5-*, aliasclaude-sonnet-5-latest): Uses adaptive thinking ({"type": "adaptive"}) withoutput_config.effort. The legacythinking.type == "enabled"+budget_tokensshape is rejected (HTTP 400), andtemperature,top_p, andtop_kare dropped automatically.xhighdowngrades to"high"(maxis Opus/Mythos-class only). Effort default ishigh. - Claude 4.6+ adaptive models (Opus 4.6, Sonnet 4.6, Opus 4.7): Use adaptive thinking (
{"type": "adaptive"}) withoutput_config.effortto control thinking depth. Claude Opus 4.7 requires this API; sending the legacythinking.type == "enabled"+budget_tokensshape to Opus 4.7 produces HTTP 400. - Pre-4.6 models (Opus 4.5, Sonnet 4, etc.): Use
thinking.budget_tokensfor 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):
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):
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):
Transformed (Anthropic format):
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 noanthropic-versionheader 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/responsesexpresses "no thinking" asreasoning.effort: "none"(which omitsthinkingrather than disabling it explicitly). Validation runs per request, catching later turns that raise effort while thinking stays disabled. - Ungated surfaces:
/anthropic/v1/messages/count_tokensis left ungated because it neither generates nor bills tokens, and Bedrock Converse (endpoint_type: converse) forwardsextra_body/additionalModelRequestFieldsverbatim by design. - No silent rewrite: The error body lists both remedies: lower
output_config.efforttohighor 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 structuredtool_useblocks, 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'sanyandtool),/anthropic/v1/messages, and the Responses-to-Anthropic converter. - Ungated surfaces:
/anthropic/v1/messages/count_tokensand 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_choiceautowith an instruction naming the tool, setstrict: trueon the tool definition, or request structured output. The router never downgrades a forced choice toauto, 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;
maxsupported; sampling params dropped; explicit disabled-thinking omitted):claude-fable-5-*(aliasclaude-fable-5-latest),claude-mythos-5-*(aliasclaude-mythos-5-latest, limited release) - Claude Sonnet 5.5 (adaptive thinking + effort;
maxsupported; 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-*, aliasclaude-opus-4-8-latest - Claude Sonnet 5 (adaptive thinking + effort; sampling params dropped;
xhighdowngraded tohigh; effort defaulthigh):claude-sonnet-5-*, aliasclaude-sonnet-5-latest - Claude Opus 4.7 (adaptive thinking + effort; sampling params deprecated):
claude-opus-4-7-* - Claude Haiku 5.5 (adaptive thinking + effort;
maxsupported; sampling params dropped; effort defaultmedium):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-flashgemini-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:
- Sets
include_thoughts: trueto expose reasoning content - Sets default
max_completion_tokens: 16384if not specified - 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-budgetif 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¶
- Use appropriate effort levels: Higher effort improves reasoning depth but increases latency and token cost.
- Check model support: Not all models support reasoning parameters.
- Handle upper effort levels carefully: GPT-5.2 through GPT-5.6 support
xhigh, while only GPT-5.6 and GPT-6 supportmax; the router applies documented fallbacks for older models. - Consider cost: Extended thinking consumes additional tokens (especially with Anthropic's
budget_tokens). - Test with your backend: Generic backends have varying support levels.
Related Documentation¶
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.