Reasoning
Thinking tokens in a separate field, an effort dial, and an off switch for the output.
The reasoning field
Reasoning models think before they answer. vLLM separates that text out of the final answer and returns it as reasoning on the assistant message, alongside the normal content. ThaiRouter passes it through unless you ask it not to.
reasoning (verified against production, non-streaming and streaming alike); older builds emitted reasoning_content. ThaiRouter forwards whichever arrives and strips both on exclude, so read reasoning ?? reasoning_content to stay safe.{
"choices": [{
"message": {
"role": "assistant",
"reasoning": "17 × 23: 17 × 20 = 340, 17 × 3 = 51, total 391.",
"content": "17 × 23 = 391"
},
"finish_reason": "stop"
}],
"thairouter": { "usage_id": "…", "cost": 0.0011, "reasoning_effort": "low" }
}OpenAI SDK types don't declare the field, but the value is there: in Python read it with getattr(msg, "reasoning", None) or msg.model_extra; in TypeScript cast the message.
Choosing an effort
Two spellings are accepted on every chat completion; use whichever your client already knows.
| Style | Field | Values |
|---|---|---|
| OpenAI | reasoning_effort | none, minimal, low, medium, high, xhigh, max |
| OpenRouter | reasoning.effort | same list |
| OpenRouter | reasoning.enabled | false is shorthand for effort: "none" |
Models don't all distinguish the same levels, so the value you send is snapped to the nearest level the model's chat template understands (ties round up). none on a model that can't stop thinking becomes its lowest level. The level actually used is echoed in thairouter.reasoning_effort (or the x-thairouter-reasoning-effort header on streams). Omit the field for the model's default. Non-reasoning models ignore it. An unknown string is a 400.
curl https://api.thairouter.ai/v1/chat/completions \
-H "Authorization: Bearer $THAIROUTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "thairouter/glm-5.3-flash",
"messages": [{"role": "user", "content": "17 × 23 = ?"}],
"reasoning_effort": "low"
}'Hiding the thinking
Set reasoning.exclude: true to have ThaiRouter strip reasoning from the response, including every streaming delta. The model still thinks and the tokens are still billed; this only trims what crosses the wire, useful for clients that render every field.
{ "model": "thairouter/glm-5.3-flash", "messages": [...], "reasoning": { "effort": "high", "exclude": true } }Streaming
While streaming, thinking arrives first as delta.reasoning chunks, then the answer as delta.content. A chunk carries one or the other. The playground renders these as a collapsible "thinking" block; the snippet below does the same in a terminal.
stream = client.chat.completions.create(
model="thairouter/glm-5.3-flash",
messages=[{"role": "user", "content": "17 × 23 = ?"}],
stream=True,
)
for chunk in stream:
if not chunk.choices:
continue
d = chunk.choices[0].delta
thinking = getattr(d, "reasoning", None) or getattr(d, "reasoning_content", None)
if thinking:
print(f"\x1b[2m{thinking}\x1b[0m", end="") # dim
if d.content:
print(d.content, end="")Billing
Reasoning tokens are completion tokens. They count toward usage.completion_tokens, toward max_tokens, and are billed at the model's output price, even with exclude. A model that hits max_tokens mid-thought returns finish_reason: "length" with little or no content.
reasoning_effort: "low"; if you omit max_tokens the whole output budget is reserved, so set one. See Billing.Multi-turn
- Send only
contentback as prior assistant turns. Do not echoreasoninginto history; it wastes prompt tokens and can confuse the model. - Nothing about the reasoning is stored by ThaiRouter, in line with the logging policy.
Per-model support
Straight from the catalog. "Off" means none is honoured; otherwise it snaps to the lowest level.
| Model | Status | Levels | Default | Off |
|---|---|---|---|---|
| thairouter/glm-5.3-flash | live | Low · High · Max | Max | no |
| thairouter/deepseek-v4-pro | coming soon | High | High | yes |
| thairouter/qwen3.8-27b | coming soon | High | High | yes |
| thairouter/muse-glimmer | coming soon | Low · Medium · High | High | no |
| thairouter/gpt-oss-120b | coming soon | Low · Medium · High | High | no |
Models not listed here return content only and ignore the effort fields.