# Reasoning models
URL: https://docs.munito.ai/reasoning

The public chat models on Munito AI, such as `qwen/qwen3.8-27b` and `zai-org/glm-5.3-flash`, reason before they answer by default. `google/gemma-4-12b` reasons only when a request turns its reasoning on. The API returns the reasoning in its own field, apart from the answer.
Reasoning tokens are output tokens: they count in the output usage, and you pay the output price for them.

The OpenAI SDK uses the base URL `https://api.munito.ai/inference/v1`, and the Anthropic SDK uses `https://api.munito.ai/inference`. Both read the key from `MUNITO_API_KEY`.

## Effort levels and the off switch [#effort-levels-and-the-off-switch]

| Model                                  | `reasoning_effort`              | Turn reasoning off                                                                |
| -------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------- |
| `qwen/qwen3.8-27b`                     | `low`, `medium`, `xhigh`        | `reasoning_effort: "none"`, or `chat_template_kwargs: {"enable_thinking": false}` |
| `zai-org/glm-5.3-flash`                | `low`, `high`, `max`            | Not available. `reasoning_effort: "none"` runs as `low`.                          |
| `deepseek-ai/deepseek-v4-flash`        | `low`, `high`, `max`            | `reasoning_effort: "none"`, or `chat_template_kwargs: {"thinking": false}`        |
| `deepseek-ai/deepseek-v4.1-flash-1017` | `low`, `high`, `max`            | `reasoning_effort: "none"`, or `chat_template_kwargs: {"enable_thinking": false}` |
| `aleph-alpha/kolibri-1`                | `none`, `low`, `medium`, `high` | `reasoning_effort: "none"`, or `chat_template_kwargs: {"enable_thinking": false}` |
| `google/gemma-4-12b`                   | -                               | Off by default. `chat_template_kwargs: {"enable_thinking": true}` turns it on.    |

A level that a model does not list moves to the nearest level that it lists. On a tie, it moves to the lower level. For example, `high` on `qwen/qwen3.8-27b` runs as `medium`.
`none` turns reasoning off with the switch of the model. A model that always reasons runs its lowest level instead.
`none` with a switch that the same request turns on returns `400`, and so does a value that is not an effort level:

```json
{
  "error": {
    "message": "'reasoning_effort' is not supported: choose one of: low, medium, xhigh",
    "type": "invalid_request_error",
    "param": "reasoning_effort",
    "code": null
  }
}
```

A `chat_template_kwargs` option that the model does not list returns `400` with `'chat_template_kwargs' is not supported: unsupported template option or value`.

## Read the reasoning [#read-the-reasoning]

```bash
pip install openai
```

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.munito.ai/inference/v1",
    api_key=os.environ["MUNITO_API_KEY"],
)
response = client.chat.completions.create(
    model="zai-org/glm-5.3-flash",
    messages=[{"role": "user", "content": "A train leaves at 09:40 and arrives at 13:15. How long is the trip?"}],
    reasoning_effort="low",
    max_tokens=2048,
)
message = response.choices[0].message
print("Reasoning:", (message.model_extra or {}).get("reasoning"))
print("Answer:", message.content)
print("Output tokens:", response.usage.completion_tokens)
```

| API              | Where the reasoning is                                          | Reasoning tokens                                                                          |
| ---------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Chat Completions | `message.reasoning`, and `delta.reasoning` in a stream          | In `usage.completion_tokens`                                                              |
| Responses        | An output item of type `reasoning`, with `reasoning_text` parts | In `usage.output_tokens`. `usage.output_tokens_details.reasoning_tokens` gives the count. |
| Messages         | A `thinking` block before the `text` block                      | In `usage.output_tokens`                                                                  |

In a Chat Completions stream, the usage chunk also gives the count in `usage.completion_tokens_details.reasoning_tokens`. See [Streaming](https://docs.munito.ai/streaming).

## The empty answer trap [#the-empty-answer-trap]

The model writes its reasoning first and its answer after it. `max_tokens` limits both together.
When the limit is too small, the output stops during the reasoning. `content` is then `null`, and `finish_reason` is `length`.

```json
{
  "message": { "role": "assistant", "content": null, "reasoning": "The user asks for 17 * 23. Let me" },
  "finish_reason": "length"
}
```

To prevent it:

- Set `max_tokens` to 2048 or more. The model stops when it is done, and you pay only for the tokens that it makes.
- Lower the effort with `reasoning_effort: "low"`.
- Turn the reasoning off where the model allows it.
- Check `finish_reason`. If it is `length`, send the request again with a higher limit.

## Turn reasoning off [#turn-reasoning-off]

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.munito.ai/inference/v1",
    api_key=os.environ["MUNITO_API_KEY"],
)
response = client.chat.completions.create(
    model="qwen/qwen3.8-27b",
    messages=[{"role": "user", "content": "Translate 'good morning' into German."}],
    extra_body={"chat_template_kwargs": {"enable_thinking": False}},
    max_tokens=512,
)
print(response.choices[0].message.content)
```

The OpenAI SDK has no `chat_template_kwargs` argument, so send it in `extra_body`. With curl, put it in the JSON body.

## Responses [#responses]

On Responses, set the level with `reasoning: {"effort": "low"}`. The levels are the same as for `reasoning_effort`.

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.munito.ai/inference/v1",
    api_key=os.environ["MUNITO_API_KEY"],
)
response = client.responses.create(
    model="qwen/qwen3.8-27b",
    input="A train leaves at 09:40 and arrives at 13:15. How long is the trip?",
    reasoning={"effort": "low"},
    max_output_tokens=2048,
)
print(response.output_text)
print("Reasoning tokens:", response.usage.output_tokens_details.reasoning_tokens)
```

## Messages [#messages]

On Messages, `thinking: {"type": "disabled"}` turns the reasoning off. On `zai-org/glm-5.3-flash`, which always reasons, it selects the `low` level.
`thinking: {"type": "enabled"}` and `{"type": "adaptive"}` turn it on. Set the level with `output_config.effort`.

```bash
pip install anthropic
```

```python
import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.munito.ai/inference",
    api_key=os.environ["MUNITO_API_KEY"],
)
message = client.messages.create(
    model="qwen/qwen3.8-27b",
    max_tokens=2048,
    messages=[{"role": "user", "content": "What is 17 * 23?"}],
    thinking={"type": "disabled"},
)
for block in message.content:
    print(block.type, ":", getattr(block, "text", None) or getattr(block, "thinking", ""))
```

`thinking.budget_tokens` is accepted from 1024 up to below `max_tokens`, but the model does not keep to it. `max_tokens` is the only limit on the output.

See [Models and pricing](https://docs.munito.ai/models#prices) for the output price of each model.
