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.

AliasBase model
qwen3.6:35b-a3b-thinkingqwen3.6:35b-a3b (also qwen3.5:35b-a3b-thinking)
qwen3.5:9b-thinkingqwen3.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

bash
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:

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

text
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:

python
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)     # answer

In 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_tokensAutomatic thinking budgetLeft for the answer
1,500988512
4,0963,0721,024
16,384 (default if you do not set max_tokens)12,2884,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:

bash
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:

python
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_tokens of 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-thinking is best for hard problems. qwen3.5:9b-thinking is 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. The thinking parameter 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 content as the assistant message in the next request.