Unified Thinking / Reasoning
Every model group speaks one thinking/reasoning contract, no matter which provider or model backs it. Send the same reasoning object regardless of whether the model group routes to OpenAI, Anthropic, Google, or anything else — you never need to know the underlying backend's native parameter name or shape.
Request: The reasoning Parameter
Send a top-level reasoning object in any chat completion request:
{
"model": "reasoning",
"reasoning": {
"effort": "medium",
"max_tokens": 8000
},
"messages": [{ "role": "user", "content": "..." }]
}Fields
| Field | Type | Description |
|---|---|---|
enabled | boolean | false disables thinking. Equivalent to effort: "none". |
effort | string | "none" | "low" | "medium" | "high" | "xhigh". |
max_tokens | number | Maximum tokens the model may spend on reasoning (thinking budget). |
exclude | boolean | When true, asks the model not to include thought content in the response. |
All fields are optional. reasoning: {} (empty object) enables thinking with the model group's defaults.
Disabling thinking
{ "reasoning": { "enabled": false } }or
{ "reasoning": { "effort": "none" } }Response: Normalized Output Shape
Regardless of which backend served the request, reasoning text always comes back in the same place:
choices[n].message.reasoning (non-streaming)
choices[n].delta.reasoning (streaming)Both are a plain string. The field is omitted when the model produced no thinking output.
Non-streaming
{
"choices": [{
"message": {
"role": "assistant",
"content": "The answer is 42.",
"reasoning": "Let me work through this step by step..."
},
"finish_reason": "stop"
}]
}Streaming
data: {"choices":[{"delta":{"reasoning":"Let me work"},"index":0}]}
data: {"choices":[{"delta":{"reasoning":" through this"},"index":0}]}
data: {"choices":[{"delta":{"content":"The answer is 42."},"index":0}]}
data: {"choices":[{"delta":{},"finish_reason":"stop","index":0}]}Reasoning deltas and content deltas are sent in separate chunks. A chunk will have at most one of delta.reasoning or delta.content.
Model Group Defaults and Enforcement
Each model group carries its own thinking policy, set once by whoever manages it, so calling applications don't have to send reasoning on every request (or even know it exists) to get consistent behavior.
A policy has three parts:
| Field | Type | Description |
|---|---|---|
mode | "default" | "force" | See below. |
enabled | boolean | Whether thinking is on when the policy applies. |
budget_tokens | number | Thinking budget when enabled. Defaults to 1024. |
mode: "default" — the model group's policy only applies when the caller sends no reasoning at all. Any reasoning the caller does send is used as-is. This is the default for every model group that hasn't configured a policy: thinking is on, with a 1024-token budget, unless the caller says otherwise.
mode: "force" — the model group's policy always wins, overriding anything the caller sends. Use this to guarantee a model group always reasons (for a group dedicated to complex analysis) or never does (for a group dedicated to fast, cheap responses), regardless of what individual callers request.
Because this is resolved per model group before the request reaches any backend, a caller can build against one reasoning contract without needing to know — or coordinate on — which model groups enforce a policy and which don't.

