Reasoning effort
NewA model that reasons returns its reasoning text next to the answer, in a field of its own. On the hosted open-weight models one request field sets how deeply the model reasons before it answers.
POST https://api.shannon-ai.com/v1/chat/completions
from openai import OpenAI
client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1")
response = client.chat.completions.create(
model="Kimi-K3-3BIT-REAP",
reasoning_effort="low",
messages=[{"role": "user", "content": "Is 221 a prime number?"}],
)
message = response.choices[0].message
print("Reasoning:", getattr(message, "reasoning_content", None))
print("Answer:", message.content)
print("Reasoning tokens:", response.usage.completion_tokens_details.reasoning_tokens) import OpenAI from "openai";
const client = new OpenAI({ apiKey: "YOUR_API_KEY", baseURL: "https://api.shannon-ai.com/v1" });
const response = await client.chat.completions.create({
model: "Kimi-K3-3BIT-REAP",
reasoning_effort: "low",
messages: [{ role: "user", content: "Is 221 a prime number?" }],
});
const message = response.choices[0].message;
console.log("Reasoning:", message.reasoning_content);
console.log("Answer:", message.content);
console.log("Reasoning tokens:", response.usage.completion_tokens_details.reasoning_tokens); curl https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Kimi-K3-3BIT-REAP",
"reasoning_effort": "low",
"messages": [
{"role": "user", "content": "Is 221 a prime number?"}
]
}' The reply carries the reasoning text in reasoning_content, the answer in content, and the reasoning tokens in usage:
{
"id": "chatcmpl-2a6c8e0b4d1f4a3c9e7b5d3f1a8c6e4b",
"object": "chat.completion",
"created": 1791590400,
"model": "Kimi-K3-3BIT-REAP",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "No. 221 = 13 x 17, so it is not prime.",
"reasoning_content": "Try small primes. 221 / 13 = 17, so 221 = 13 x 17."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 14,
"completion_tokens": 43,
"total_tokens": 57,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 27}
}
} Which models reason
| Model | Returns reasoning text | Applies the effort field |
|---|---|---|
shannon-1.6-lite | — | — |
shannon-1.6-pro, shannon-2-lite, shannon-2-pro, shannon-3, shannon-3-pro, shannon-3.1, shannon-3.1-pro, shannon-coder-1 | Yes | — |
| Every hosted open-weight model | Yes | Yes |
These are the flags capabilities.reasoning and capabilities.reasoning_effort that GET /v1/models reports for each id. A reply carries reasoning text only when the model produced some. Models & pricing
Setting the effort
Each endpoint has its own spelling of the effort. All of them select one of the same four levels.
| Field | Type | Default | Description | Applied by |
|---|---|---|---|---|
reasoning_effort | string | high | /v1/chat/completions. How deeply the model reasons before it answers: off, low, medium or high. | Hosted open-weight models |
reasoning | object | string | /v1/chat/completions. The same value as {"effort": "low"} or as a bare string. When both fields are sent, reasoning_effort counts. | Hosted open-weight models | |
reasoning.effort | string | high | /v1/responses. Written as the object {"effort": "low"}. | Hosted open-weight models |
thinking | object | /v1/messages. {"type": "enabled", "budget_tokens": n} or {"type": "disabled"}. The budget selects one of the levels; see the table further down. | Hosted open-weight models |
{
"model": "Kimi-K3-3BIT-REAP",
"reasoning_effort": "low",
"messages": [{"role": "user", "content": "Is 221 a prime number?"}]
} {
"model": "Kimi-K3-3BIT-REAP",
"reasoning": {"effort": "low"},
"input": "Is 221 a prime number?"
} {
"model": "Kimi-K3-3BIT-REAP",
"max_tokens": 1024,
"thinking": {"type": "enabled", "budget_tokens": 2048},
"messages": [{"role": "user", "content": "Is 221 a prime number?"}]
} | Value | Also accepted | Effect |
|---|---|---|
off | none, minimal | No reasoning. The reply carries no reasoning text. |
low | Brief reasoning. | |
medium | default | Medium depth. |
high | max | The deepest level. This is the level of a request that sends no value. |
- Values are read without regard to upper or lower case.
- The value
defaultmeansmedium. Leaving the field out meanshigh. - The Shannon models (
shannon-*) set their own depth in every request;GET /v1/modelsreportsreasoning_effort: falsefor them.
The thinking budget on /v1/messages
On /v1/messages the thinking field selects the level. budget_tokens picks one of the levels above; it is not a token limit of its own. adaptive is read like enabled.
| You send | Level |
|---|---|
{"type": "disabled"} | off |
{"type": "enabled", "budget_tokens": n} with n up to 2,048 | low |
n from 2,049 to 8,192 | medium |
A larger n, or enabled without budget_tokens | high |
No thinking field | high |
Where the reasoning text arrives
Reasoning text is always separate from the answer. Each format has its own place for it:
| Endpoint | Not streamed | Streamed |
|---|---|---|
/v1/chat/completions | choices[0].message.reasoning_content: a string, or null when the model produced none. | Chunks with choices[0].delta.reasoning_content, before the chunks with delta.content. |
/v1/responses | An output item {"type": "reasoning", "summary": [{"type": "summary_text", "text": "…"}]} before the message item. No item when there is no reasoning. | response.output_item.added with a reasoning item, then response.reasoning_summary_text.delta events and one response.reasoning_summary_text.done. |
/v1/messages | A first content block {"type": "thinking", "thinking": "…"}, then the text block. No thinking block when there is no reasoning. | A thinking content block: content_block_start, content_block_delta events with thinking_delta, content_block_stop. The text block follows. |
{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "No. 221 = 13 x 17, so it is not prime.",
"reasoning_content": "Try small primes. 221 / 13 = 17, so 221 = 13 x 17."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 14,
"completion_tokens": 43,
"total_tokens": 57,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 27}
}
} {
"output": [
{
"id": "rs_6f0a2c4e8b1d4f3a9c7e5b3d1f8a6c4e",
"type": "reasoning",
"summary": [
{
"type": "summary_text",
"text": "Try small primes. 221 / 13 = 17, so 221 = 13 x 17."
}
]
},
{
"id": "msg_1b3d5f7a9c2e4b6d8f0a2c4e6b8d0f1a",
"type": "message",
"role": "assistant",
"status": "completed",
"content": [
{
"type": "output_text",
"text": "No. 221 = 13 x 17, so it is not prime.",
"annotations": []
}
]
}
],
"usage": {
"input_tokens": 14,
"input_tokens_details": {"cached_tokens": 0},
"output_tokens": 43,
"output_tokens_details": {"reasoning_tokens": 27},
"total_tokens": 57
}
} {
"content": [
{
"type": "thinking",
"thinking": "Try small primes. 221 / 13 = 17, so 221 = 13 x 17."
},
{"type": "text", "text": "No. 221 = 13 x 17, so it is not prime."}
],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 14,
"cache_read_input_tokens": 0,
"cache_creation_input_tokens": 0,
"output_tokens": 43
}
} Where to look, per model:
| Models | Where the text arrives |
|---|---|
| Hosted open-weight models | On every endpoint, streamed or not. A request with effort off, or with response_format / text.format, is answered without reasoning text. |
shannon-3, shannon-3-pro, shannon-3.1, shannon-3.1-pro | On every endpoint, streamed or not, whenever the model produced reasoning text. |
shannon-2-lite, shannon-2-pro | /v1/chat/completions, not streamed: in message.reasoning_content. Streamed, in a request without tools or response_format: the stream carries comment lines : thinking while the model reasons, then the answer chunks. /v1/messages, streamed: a thinking block before the text block. |
shannon-1.6-pro, shannon-coder-1 | In streamed replies and in replies to requests that carry tools, whenever the model produced reasoning text. |
On /v1/responses, a reply that is not streamed carries the reasoning item for the Shannon 3 family and the hosted open-weight models.
Reading a stream
In a stream the reasoning text comes first, in deltas of its own, and the answer follows. Print or store the two separately:
from openai import OpenAI
client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1")
stream = client.chat.completions.create(
model="Kimi-K3-3BIT-REAP",
reasoning_effort="medium",
stream=True,
messages=[{"role": "user", "content": "Is 221 a prime number?"}],
)
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
reasoning = getattr(delta, "reasoning_content", None)
if reasoning:
print(reasoning, end="", flush=True) # the reasoning text comes first
if delta.content:
print(delta.content, end="", flush=True) # then the answer import OpenAI from "openai";
const client = new OpenAI({ apiKey: "YOUR_API_KEY", baseURL: "https://api.shannon-ai.com/v1" });
const stream = await client.chat.completions.create({
model: "Kimi-K3-3BIT-REAP",
reasoning_effort: "medium",
stream: true,
messages: [{ role: "user", content: "Is 221 a prime number?" }],
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta ?? {};
if (delta.reasoning_content) process.stdout.write(delta.reasoning_content); // the reasoning text comes first
if (delta.content) process.stdout.write(delta.content); // then the answer
} curl -N https://api.shannon-ai.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Kimi-K3-3BIT-REAP",
"reasoning_effort": "medium",
"stream": true,
"messages": [
{"role": "user", "content": "Is 221 a prime number?"}
]
}' The first chunk of a hosted open-weight model, reasoning or answer, also carries delta.role: "assistant". Streaming
Reasoning tokens and billing
On the hosted open-weight models, reasoning tokens are output tokens: they are billed at the output price of the model, and the output count already includes them. usage shows the reasoning share here:
| Endpoint | Field | Note |
|---|---|---|
/v1/chat/completions | usage.completion_tokens_details.reasoning_tokens | Already included in completion_tokens. |
/v1/responses | usage.output_tokens_details.reasoning_tokens | Already included in output_tokens. |
/v1/messages | usage.output_tokens | Reasoning and answer together. This format has no separate count. |
- On the hosted open-weight models
max_tokenslimits the answer text. Reasoning tokens are not counted against it. - Because reasoning is billed as output, the effort level is also a cost setting:
offproduces no reasoning tokens. - The Shannon models report
prompt_tokens,completion_tokensandtotal_tokens(input_tokensandoutput_tokenson/v1/messages) without a reasoning share, and bill one flat price per token. Models & pricing
Reasoning in the next request
You do not have to send reasoning back. A thinking block or a reasoning item in the conversation you send is not passed on to the model, so an assistant turn can go back with or without it. A thinking block has the two fields type and thinking; it has no signature.
Errors
| Status | Type | Message | When |
|---|---|---|---|
400 | invalid_request_error | unknown reasoning effort '<value>'; expected off, low, medium or high | A hosted open-weight model received a value outside the list, for example xhigh. Nothing is drawn from your balance. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | reasoning_effort is not a string. |
Authentication, balance and server errors are the same as for every request. Error Handling