Reasoning effort
Set how much a tier thinks before it answers, from none to high, and keep hidden thinking from spending your whole max_tokens budget.
reasoning_effort caps the hidden thinking a tier does before it answers. You
send one of four values on a chat request, or omit it and the tier applies its
default. The effort the call ran at comes back on the response as
cruxy.reasoning_effort.
This request asks kavi for a one-line answer, so it turns thinking off and
keeps the budget small:
curl https://api.cruxy.ai/api/v1/chat \
-H "Authorization: Bearer $CRUXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kavi",
"stream": false,
"reasoning_effort": "none",
"max_tokens": 256,
"messages": [
{ "role": "user", "content": "Is Mumbai north or south of Chennai? Answer in one sentence." }
]
}'const res = await fetch('https://api.cruxy.ai/api/v1/chat', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CRUXY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'kavi',
stream: false,
reasoning_effort: 'none',
max_tokens: 256,
messages: [
{ role: 'user', content: 'Is Mumbai north or south of Chennai? Answer in one sentence.' },
],
}),
})
const completion = await res.json()
console.log(completion.cruxy.reasoning_effort) // "none"
console.log(completion.message.content)The four values
| Value | Effect |
|---|---|
none | Turns thinking off. The tier answers directly. |
low, medium, high | Turn thinking on. On a graded tier the three are different depths, from low to high; on an on-or-off tier each one turns thinking on. |
Any other value is 400. The field is accepted on POST /api/v1/chat,
POST /api/v2/chat, and POST /v1/chat/completions.
Defaults per tier
Omit reasoning_effort and each tier applies its own default. graded says
whether low, medium, and high are different depths on the tier. When it
is false the control is on or off: none turns thinking off, every other
value turns it on, low thinks as long as high, and only max_tokens bounds
it.
| Tier | Profile | Default | graded |
|---|---|---|---|
kavi | Deep | high | false: on or off |
vaani | Balanced | low | true |
mira | Fast | none | true |
With model: "auto", the router chooses the effort per request unless you send
one. These are the shipped defaults; GET /api/v1/models reports each tier's
live reasoning.default_effort and reasoning.graded, read from the same
configuration the chat endpoints apply.
Thinking spends max_tokens
A tier that thinks generates its thinking inside the same output budget as its
answer. On POST /api/v1/chat and POST /v1/chat/completions, the thinking is
not returned as content. So a small max_tokens on a tier that thinks can be
used up before the answer starts.
Check for the trap when you read a response, so a truncated turn never reaches your user as an empty answer:
const { message, finish_reason } = completion
if (finish_reason === 'length' && !message.content) {
// The budget ran out before the answer began. Resend with
// reasoning_effort: 'none', or with max_tokens unset.
}When to send none
Send none when the answer needs no deliberation and you want it fast:
- Short, direct answers: a classification, a yes or no, an extracted field, a one-line reply.
- A small
max_tokens: any time you cap the output tightly, so thinking cannot consume the cap. - A turn that times out: a
stream: falsecall is bounded by the tier's deadline and answered504upstream_timeoutpast it. Loweringreasoning_effortormax_tokensshortens the turn; streaming lifts the limit to five minutes.
Keep thinking on when the answer needs deliberation, and leave max_tokens
unset there so the thinking and the answer share the tier's full cap.
See the thinking
The OpenAI-shaped wires do not return the thinking. The native wire,
POST /api/v2/chat, does: a non-streaming response carries a top-level
reasoning string, and a stream sends the thinking as reasoning events,
separate from the answer's delta events. Either way it is present only when
the tier reasoned.
- It is not part of the answer, and it sits outside
messageon purpose. Do not send it back in the conversation. - There is no separate reasoning-token count.
usage.output_tokensalready includes the thinking, undivided. - The effort is echoed as
routing.reasoning_efforton the native wire.
Next steps
- Choosing a tier: each tier's profile, output cap, and default.
- Auto routing: let the router choose the tier and the effort.
- Streaming: read the stream and its
finish_reason. - Errors and retries: what to do about
504upstream_timeout.