Thinking and reasoning
Last updated:
On this page
With thinking, the model reasons step by step before it answers. This gives better answers to problems that need several steps, such as math, logic, debugging and planning, but takes longer and uses more tokens. The reasoning is delivered separately from the answer, so your application can show it, log it or ignore it.
Models with thinking
Thinking is enabled with the suffix -thinking on the model name. The alias runs
the same model on the same hardware as the base model.
| Alias | Base model |
|---|---|
qwen3.6:35b-a3b-thinking | qwen3.6:35b-a3b (also qwen3.5:35b-a3b-thinking) |
qwen3.5:9b-thinking | qwen3.5:9b |
The base models answer directly without reasoning, which is fastest for simple questions.
Only the aliases in the table support thinking
gemma4:31b has no thinking alias. A name like gemma4:31b-thinking is
treated as an unknown model and is answered by the default model
(qwen3.6:35b-a3b) without thinking. Check model in the response if you
are unsure which model answered.
Calling with thinking
curl https://api.staik.se/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-st-your-key" \
-d '{
"model": "qwen3.6:35b-a3b-thinking",
"messages": [{"role": "user", "content": "What is 12 * 13?"}],
"max_tokens": 4096
}'Where the reasoning is in the response
The reasoning is in the reasoning field and the answer in content:
{
"model": "qwen3.6:35b-a3b",
"choices": [{
"message": {
"role": "assistant",
"reasoning": "The user wants 12 * 13. 12 * 13 = 12 * 10 + 12 * 3 = 120 + 36 = 156.",
"content": "12 * 13 = 156"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 143,
"total_tokens": 163,
"completion_tokens_details": {"reasoning_tokens": 137}
}
}usage.completion_tokens_details.reasoning_tokens shows how many of the output
tokens went to the reasoning. Reasoning tokens count as output tokens in usage
and towards your token limit.
When streaming, the reasoning arrives first as delta.reasoning chunks,
followed by the answer as delta.content chunks:
data: {"choices":[{"delta":{"reasoning":"The user wants"}}], ...}
data: {"choices":[{"delta":{"reasoning":" 12 * 13."}}], ...}
data: {"choices":[{"delta":{"content":"12 * 13 = 156"}}], ...}
data: [DONE]The field is called reasoning, not reasoning_content
Some clients and older examples read reasoning_content (DeepSeek's
convention). staik follows the newer convention and sends reasoning. If
your client only reads reasoning_content you will not see any reasoning,
but the answer in content is unaffected.
Reading the reasoning in code
The OpenAI SDKs have no dedicated field for reasoning, but the value is
included in the response and can be read directly:
from openai import OpenAI
client = OpenAI(base_url="https://api.staik.se/v1", api_key="sk-st-your-key")
# Without streaming
response = client.chat.completions.create(
model="qwen3.6:35b-a3b-thinking",
messages=[{"role": "user", "content": "What is 12 * 13?"}],
max_tokens=4096,
)
message = response.choices[0].message
print("Reasoning:", getattr(message, "reasoning", None))
print("Answer:", message.content)
# With streaming
stream = client.chat.completions.create(
model="qwen3.6:35b-a3b-thinking",
messages=[{"role": "user", "content": "What is 12 * 13?"}],
max_tokens=4096,
stream=True,
)
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
if getattr(delta, "reasoning", None):
print(delta.reasoning, end="", flush=True) # reasoning
elif delta.content:
print(delta.content, end="", flush=True) # answerIn TypeScript, reasoning is missing from the SDK types. Read it with
(message as { reasoning?: string }).reasoning.
Thinking budget
The reasoning counts towards max_tokens. Without a limit, the model can keep
reasoning until the whole budget is used up and never get to the answer.
qwen3.5:9b in particular tends to keep double-checking an answer it already has.
staik therefore automatically sets a thinking budget on every thinking
request. When the budget is reached, the model ends its reasoning and writes the
answer, so the answer always fits within max_tokens:
max_tokens | Automatic thinking budget | Left for the answer |
|---|---|---|
| 1,500 | 988 | 512 |
| 4,096 | 3,072 | 1,024 |
16,384 (default if you do not set max_tokens) | 12,288 | 4,096 |
The budget is 75% of max_tokens, but at least 512 tokens (or half of
max_tokens if that is smaller) are always reserved for the answer.
Setting your own budget
Send thinking_token_budget to control the budget yourself. Your value is used
instead of the automatic one:
curl https://api.staik.se/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-st-your-key" \
-d '{
"model": "qwen3.5:9b-thinking",
"messages": [{"role": "user", "content": "Plan a three-day trip to Gothenburg."}],
"max_tokens": 8192,
"thinking_token_budget": 2000
}'In OpenAI's Python SDK, fields the SDK does not know about are sent with
extra_body:
response = client.chat.completions.create(
model="qwen3.5:9b-thinking",
messages=[{"role": "user", "content": "Plan a three-day trip to Gothenburg."}],
max_tokens=8192,
extra_body={"thinking_token_budget": 2000},
)A low budget gives faster answers with shorter reasoning. A high budget gives
the model more room on hard problems. Keep the budget lower than max_tokens,
otherwise there is no room left for the answer.
Recommendations
- Use
max_tokensof at least 4096 for thinking requests. With lower values both the reasoning and the answer get shorter. - Choose the base model for simple questions. Thinking makes the request slower and more expensive without improving answers to factual questions or simple rewrites.
qwen3.6:35b-a3b-thinkingis best for hard problems.qwen3.5:9b-thinkingis faster but less accurate.- Stream if you show the answer to a user. The reasoning can take several seconds before the first word of the answer arrives.
If the reasoning is missing
If the model still does not get to the answer despite the budget, staik
automatically retries without thinking so that you always get an answer. That
answer has no reasoning field. This is rare with the thinking budget, but your
code should handle a missing reasoning.
Limitations
/v1/messages(the Anthropic format, for example Claude Code) does not support thinking. Thethinkingparameter is ignored and the thinking aliases behave like the base model. Use chat completions for thinking.- Do not send the reasoning back in the conversation history. Send only
contentas the assistant message in the next request.