Skip to content

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:

json
{
  "model": "reasoning",
  "reasoning": {
    "effort": "medium",
    "max_tokens": 8000
  },
  "messages": [{ "role": "user", "content": "..." }]
}

Fields

FieldTypeDescription
enabledbooleanfalse disables thinking. Equivalent to effort: "none".
effortstring"none" | "low" | "medium" | "high" | "xhigh".
max_tokensnumberMaximum tokens the model may spend on reasoning (thinking budget).
excludebooleanWhen 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

json
{ "reasoning": { "enabled": false } }

or

json
{ "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

json
{
  "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:

FieldTypeDescription
mode"default" | "force"See below.
enabledbooleanWhether thinking is on when the policy applies.
budget_tokensnumberThinking 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.

ModelPlane documentation for multi-model, multi-provider, and coding-plan use cases.