> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gloo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Control reasoning effort

> Pick a reasoning effort, read reasoning summaries, size the output limit for reasoning models, and catch cut-off answers, on every Responses and Chat Completions endpoint.

Control how much a model reasons before it answers. You'll pick an effort level the model supports, read the reasoning back, size the output limit so the thinking doesn't crowd out the answer, and catch answers that were cut off. The last section has a working example for each of the six endpoints.

Want the concepts first? Read the [Reasoning guide](https://docs.gloo.com/api-guides/reasoning.md). New to reasoning models? [Models That Think Before They Answer](https://docs.gloo.com/ai-learning-center/gloo-ai-102/models-that-think.md) explains what reasoning is and when more effort pays off.

> [!NOTE]
>
> **One set of settings for every model.** On the Responses and Chat Completions endpoints (guarded, grounded and direct), `reasoning_effort` and the `reasoning` object work the same way for Claude, Gemini, OpenAI and open-weight models. Gloo translates them for each provider, so the code in this tutorial works across [model families](https://docs.gloo.com/getting-started/glossary.md#model-family).

> [!NOTE]
>
> **Using the Anthropic SDK?** [Anthropic Messages](https://docs.gloo.com/api-guides/anthropic-messages.md) uses Anthropic's own `thinking` and `output_config.effort` instead. See [Use Claude through the Anthropic Messages endpoint](https://docs.gloo.com/tutorials/anthropic-messages.md#step-4-see-claudes-thinking).

## Prerequisites

You need:

* A Gloo AI Studio account
* Your API key from the [API Keys page](https://docs.gloo.com/studio/manage-api-credentials.md), exported as `GLOO_API_KEY`
* **Authentication setup** - Complete the [Authentication Tutorial](https://docs.gloo.com/tutorials/authentication.md) first
* Python 3.10+ or Node.js 18+, and the OpenAI SDK for your language. The Gloo endpoints accept its requests, and it sends `reasoning_effort` and `reasoning` natively:

```bash Python theme={null}
pip install openai requests
```

```bash TypeScript theme={null}
npm install openai
```

The short TypeScript examples use top-level `await`. To run one on its own, save it with an `.mts` extension and run it with `npx tsx example.mts`. The [Complete Example](#complete-example) runs as a plain `.ts` file.

## Understanding the settings

| | Chat Completions ([Completions](https://docs.gloo.com/api-guides/completions-v2.md), [Grounded](https://docs.gloo.com/api-guides/grounded-completions.md), [Direct](https://docs.gloo.com/api-guides/direct-completions.md)) | Responses ([Responses](https://docs.gloo.com/api-guides/responses.md), [Grounded](https://docs.gloo.com/api-guides/grounded-responses.md), [Direct](https://docs.gloo.com/api-guides/direct-responses.md)) |
| :- | :- | :- |
| **Set how much it reasons** | `"reasoning_effort": "high"`, or `"reasoning": {"effort": "high"}` | `"reasoning": {"effort": "high"}` |
| **Get the reasoning back** | Set any effort except `none` | Add `"summary": "auto"` to `reasoning` |
| **Turn reasoning off** | `"reasoning_effort": "none"`, on models that can | `"reasoning": {"effort": "none"}`, on models that can |
| **Where the reasoning comes back** | `choices[0].message.reasoning_content`, streamed as `delta.reasoning_content` | A `reasoning` item in `output[]` before the `message` item |
| **Output limit** | `max_tokens` (or `max_completion_tokens`) | `max_output_tokens` |

Effort levels, from least to most reasoning: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Each model takes only some of them.

***

## Step 1: Find the levels a model takes

Every model in the catalog that reasons has a `reasoning` object. Look one up (this endpoint is public, so no key is needed):

```bash theme={null}
curl -s 'https://platform.ai.gloo.com/platform/v2/models' \
  | jq '.data[] | select(.id == "gloo-anthropic-claude-sonnet-5.5") | {id, max_output_tokens, reasoning}'
```

| Field | What it tells you |
| :- | :- |
| `effort_levels` | The levels the model runs as sent |
| `returns_summary` | Whether a request with an effort gets the reasoning text back |
| `thinks_by_default` | Whether the model reasons when you send no effort, which you pay for as output tokens |
| `can_disable` | Whether `none` reliably turns reasoning off |

A model that doesn't reason has `"reasoning": null`. For example, Claude 5 models take `low` to `max`, and GPT-5.4 takes `none`, `low`, `medium`, `high` and `xhigh`.

### What happens with a level the model doesn't list

You won't get an error. A level the model doesn't list runs at the nearest level below it that the model lists (or at its lowest level when none is below), and the response says so in `ignored_params`:

```json theme={null}
"ignored_params": {
  "params": ["reasoning_effort=minimal"],
  "model": "gloo-anthropic-claude-sonnet-5.5",
  "reason": "These parameters were ignored and did not affect the response: the model does not accept them, or the endpoint serving the model does not support them."
}
```

Chat Completions names the entry `reasoning_effort=<level>`; Responses names it `reasoning.effort=<level>`. On a stream, the `X-Ignored-Params` response header lists the names only. Match on `params`, not on the `reason` sentence.

To be sure of the level you get, send one the model lists. The Reasoning guide has a ready-made [`pick_effort` helper](https://docs.gloo.com/api-guides/reasoning#pick-an-effort-from-the-model-catalog) in Python and TypeScript that maps the level you want to the nearest one a model takes.

***

## Step 2: Pick an effort for the task

Effort is your main control over how long the model thinks, and so over cost and time. Reasoning tokens are billed as output tokens, whether or not any reasoning comes back.

**Turn effort up for:**

* Multi-step problems where one early mistake ruins the answer
* Math, scheduling, and anything with numbers that must add up
* Planning, where the model needs to weigh options and trade-offs
* Writing or reviewing code

**Keep effort low (or off, with `none`, on a model whose `can_disable` is `true`) for:**

* Simple lookups and factual questions
* Friendly back-and-forth chat
* Anything where speed matters most, like a live chat window

[When More Effort Is Worth It](https://docs.gloo.com/ai-learning-center/gloo-ai-102/models-that-think.md#when-more-effort-is-worth-it) walks through the trade-off. Use a lower effort to save tokens rather than a small output limit, which can cut the answer off.

With no effort set, the model follows its provider's default. Many models, including Claude 5 models, reason by default, and you pay for that reasoning as output tokens even though you didn't ask for it.

> [!TIP]
>
> Asking a model to [think step by step](https://docs.gloo.com/ai-learning-center/gloo-ai-102/think-step-by-step.md) in your prompt is a different tool. Step-by-step prompting puts the steps in the answer itself; reasoning happens before the answer, separate from it. They combine well: the effort controls how much the model thinks, and your prompt still guides what it thinks about.

Send an effort on Chat Completions:

```python Python theme={null}
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["GLOO_API_KEY"],
    base_url="https://platform.ai.gloo.com/ai/v2/direct",
)

completion = client.chat.completions.create(
    model="gloo-anthropic-claude-sonnet-5.5",
    reasoning_effort="low",
    max_tokens=8192,
    messages=[{"role": "user", "content": "Is this message a prayer request, a question or feedback? 'Please keep my mom in your prayers this week.'"}],
)

print(completion.choices[0].message.content)
print("Not applied:", getattr(completion, "ignored_params", None))
```

```typescript TypeScript theme={null}
import OpenAI from 'openai';

const client = new OpenAI({
  apiKey: process.env.GLOO_API_KEY,
  baseURL: 'https://platform.ai.gloo.com/ai/v2/direct',
});

const completion = await client.chat.completions.create({
  model: 'gloo-anthropic-claude-sonnet-5.5',
  reasoning_effort: 'low',
  max_tokens: 8192,
  messages: [{ role: 'user', content: "Is this message a prayer request, a question or feedback? 'Please keep my mom in your prayers this week.'" }],
});

console.log(completion.choices[0].message.content);
// ignored_params is a Gloo field, not in the SDK's types.
console.log('Not applied:', (completion as { ignored_params?: unknown }).ignored_params);
```

On Chat Completions you can also send the nested form, `"reasoning": {"effort": "low"}`. If you send both, `reasoning_effort` wins.

***

## Step 3: Read the reasoning back

Reasoning comes back only when the model reasoned. Claude 5 models decide how much each request needs and can skip reasoning on easy ones, so even with an effort set, an answer can arrive with no reasoning. Always treat it as optional.

Claude, Gemini and OpenAI models return the provider's **summary** of their reasoning, not the full [chain of thought](https://docs.gloo.com/ai-learning-center/gloo-ai-101/application-patterns.md#chain-of-thought-cot). Open-weight models such as DeepSeek, GLM and gpt-oss return their raw reasoning text.

### Chat Completions

Set any effort except `none`, and read `message.reasoning_content`. The OpenAI SDKs don't type that field, so read it with `getattr` in Python and a wider type in TypeScript:

```python Python theme={null}
completion = client.chat.completions.create(
    model="gloo-anthropic-claude-sonnet-5.5",
    reasoning_effort="medium",
    max_tokens=16384,
    messages=[{"role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"}],
)

message = completion.choices[0].message
reasoning = getattr(message, "reasoning_content", None)
if reasoning:
    print("Reasoning:", reasoning)
print("Answer:", message.content)
```

```typescript TypeScript theme={null}
const completion = await client.chat.completions.create({
  model: 'gloo-anthropic-claude-sonnet-5.5',
  reasoning_effort: 'medium',
  max_tokens: 16384,
  messages: [{ role: 'user', content: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?' }],
});

// reasoning_content is not in the SDK's types.
const message: { content: string | null; reasoning_content?: string | null } =
  completion.choices[0].message;
if (message.reasoning_content) console.log('Reasoning:', message.reasoning_content);
console.log('Answer:', message.content);
```

On a stream, reasoning arrives as `delta.reasoning_content` chunks before the first answer chunk.

### Responses

Add `"summary": "auto"` (or `concise` or `detailed`) to `reasoning`. An effort alone makes the model think but returns no summary. The summary is a `reasoning` item in `output[]`, before the `message` item:

```python Python theme={null}
response = client.responses.create(
    model="gloo-anthropic-claude-sonnet-5.5",
    reasoning={"effort": "medium", "summary": "auto"},
    max_output_tokens=16384,
    input="Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?",
)

for item in response.output:
    if item.type == "reasoning":
        print("Reasoning:", " ".join(part.text for part in item.summary))
print("Answer:", response.output_text)
```

```typescript TypeScript theme={null}
const response = await client.responses.create({
  model: 'gloo-anthropic-claude-sonnet-5.5',
  reasoning: { effort: 'medium', summary: 'auto' },
  max_output_tokens: 16384,
  input: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?',
});

for (const item of response.output) {
  if (item.type === 'reasoning') {
    console.log('Reasoning:', item.summary.map((part) => part.text).join(' '));
  }
}
console.log('Answer:', response.output_text);
```

On a stream, read the reasoning from `response.reasoning_summary_text.delta` events and the answer from `response.output_text.delta`. Models outside the Anthropic, Google and OpenAI families stream raw reasoning as `response.reasoning_text.delta` instead; handle both if you stream from more than one family. [What comes back](https://docs.gloo.com/api-guides/reasoning.md#what-comes-back) shows every event.

> [!WARNING]
>
> On the guarded endpoints, reasoning isn't checked by output moderation the way the answer is. If you show reasoning to your end users, review it yourself first, or don't show it. See [Reasoning is not moderated](https://docs.gloo.com/api-guides/reasoning.md#reasoning-is-not-moderated).

***

## Step 4: Size the output limit

On a reasoning model, the output limit holds the thinking **and** the answer. A limit sized for the answer alone, such as 500 or 1,024, can be used up by the thinking before the answer starts.

Pick a starting point from the effort you set and the answer you expect, then adjust it from the `usage` your requests report:

| Answer you expect | `low` effort | `medium` effort | `high`, `xhigh` or `max` effort |
| :- | :- | :- | :- |
| Short: a few sentences or a classification | 8,192 | 8,192 | 16,384 |
| Medium: a page or so | 8,192 | 16,384 | 32,768 |
| Long: a report, a long document or a lot of code | 16,384 | 32,768 | 65,536, or leave the limit unset |

These are starting points, not guarantees: the same model can reason for much longer on a hard question than on an easy one. With no effort set, size for the model's default level, which is often `medium` or `high`.

### What Gloo does with your limit

| You send | Chat Completions | Responses |
| :- | :- | :- |
| A limit, and the model is reasoning | A limit below 8,192 is raised to 8,192 so the model has room to think. A larger one is sent as you set it, up to the model's maximum output | Sent as you set it |
| No limit, and the model is reasoning | The model's maximum output, up to 65,536 tokens | For most reasoning models, the same. Claude models, Gemini 2.5 Flash and the Gemini Flash-Lite models use the model's default, so set a limit for long answers |
| A limit, and the model isn't reasoning | Sent as you set it | Sent as you set it |

The ceiling is the model's `max_output_tokens` in the catalog: thinking and answer together. You're billed only for the output tokens the model uses, not for the limit. [Set your output limit for reasoning models](https://docs.gloo.com/api-guides/reasoning.md#set-your-output-limit-for-reasoning-models) has the full rules, including how `none` changes them.

### Stream long generations

A large limit with a high effort can take minutes. A non-streamed request that runs past Gloo's response deadline fails with [`3007 / GATEWAY_DEADLINE`](https://docs.gloo.com/api-reference/general/errors#504-gateway-timeout). [Streaming](https://docs.gloo.com/ai-learning-center/gloo-ai-104/streaming-responses.md) avoids it and shows progress as it comes. If a stream fails partway, [Handling Streaming Failures](https://docs.gloo.com/best-practices/completions-streaming-failures.md) explains how to keep what the user has already seen and when to retry.

***

## Step 5: Detect and handle a cut-off

A response that reached the limit says so:

* **Chat Completions:** `finish_reason: "length"`, streamed or not.
* **Responses:** `status: "incomplete"` with `incomplete_details: {"reason": "max_output_tokens"}`. A non-streamed response can also carry a `suggestion` field that names the tokens used and the limit you sent.

When that comes with little or no answer text, the model spent the limit thinking. It's not an outage, and sending the same request again usually ends the same way. Fix it by **raising the limit** or **lowering the effort**.

`usage` shows where the tokens went. Where the model reports it, `usage.completion_tokens_details.reasoning_tokens` (Chat Completions) or `usage.output_tokens_details.reasoning_tokens` (Responses) is the reasoning part of the output tokens. It isn't reported everywhere: for Claude models it is `0` or missing on some endpoints, and their reasoning is still counted in the output tokens.

This helper checks every answer before it's used and, on a cut-off, tries once more with double the limit:

```python Python theme={null}
def ask(client, model, prompt, effort="medium", limit=8192, ceiling=65536):
    """Chat Completions request that refuses to hand back a cut-off answer."""
    # On a cut-off, try once more with double the limit (never above the model's maximum).
    for max_tokens in (limit, min(limit * 2, ceiling)):
        completion = client.chat.completions.create(
            model=model,
            reasoning_effort=effort,
            max_tokens=max_tokens,
            messages=[{"role": "user", "content": prompt}],
        )
        choice = completion.choices[0]
        usage = completion.usage  # can be missing
        details = getattr(usage, "completion_tokens_details", None)
        reasoning_tokens = getattr(details, "reasoning_tokens", None)
        output_tokens = usage.completion_tokens if usage else None
        print(f"limit={max_tokens} output={output_tokens} reasoning={reasoning_tokens}")

        if choice.finish_reason != "length":
            return choice.message.content
    raise RuntimeError(
        f"Answer cut off at max_tokens={max_tokens}. Lower the effort (now {effort!r}) or raise the limit."
    )
```

```typescript TypeScript theme={null}
async function ask(
  client: OpenAI,
  model: string,
  prompt: string,
  effort: 'low' | 'medium' | 'high' = 'medium',
  limit = 8192,
  ceiling = 65536,
): Promise<string | null> {
  // Chat Completions request that refuses to hand back a cut-off answer.
  // On a cut-off, try once more with double the limit (never above the model's maximum).
  const limits = [limit, Math.min(limit * 2, ceiling)];
  for (const maxTokens of limits) {
    const completion = await client.chat.completions.create({
      model,
      reasoning_effort: effort,
      max_tokens: maxTokens,
      messages: [{ role: 'user', content: prompt }],
    });
    const choice = completion.choices[0];
    const reasoningTokens = completion.usage?.completion_tokens_details?.reasoning_tokens;
    console.log(`limit=${maxTokens} output=${completion.usage?.completion_tokens} reasoning=${reasoningTokens}`);

    if (choice.finish_reason !== 'length') return choice.message.content;
  }
  throw new Error(
    `Answer cut off at max_tokens=${limits[1]}. Lower the effort (now '${effort}') or raise the limit.`,
  );
}
```

On Responses, check the same thing with `status` and `incomplete_details`:

```python Python theme={null}
response = client.responses.create(
    model="gloo-anthropic-claude-sonnet-5.5",
    reasoning={"effort": "high", "summary": "auto"},
    max_output_tokens=16384,
    input="Draft a one-page plan for a church food pantry's first year.",
)

details = response.incomplete_details
if response.status == "incomplete" and details and details.reason == "max_output_tokens":
    print("Cut off. Raise max_output_tokens or lower the effort.")
    usage_details = getattr(response.usage, "output_tokens_details", None)  # can be missing
    print("Reasoning tokens:", getattr(usage_details, "reasoning_tokens", None))
else:
    print(response.output_text)
```

```typescript TypeScript theme={null}
const plan = await client.responses.create({
  model: 'gloo-anthropic-claude-sonnet-5.5',
  reasoning: { effort: 'high', summary: 'auto' },
  max_output_tokens: 16384,
  input: "Draft a one-page plan for a church food pantry's first year.",
});

if (plan.status === 'incomplete' && plan.incomplete_details?.reason === 'max_output_tokens') {
  console.log('Cut off. Raise max_output_tokens or lower the effort.');
  console.log('Reasoning tokens:', plan.usage?.output_tokens_details?.reasoning_tokens);
} else {
  console.log(plan.output_text);
}
```

> [!TIP]
>
> Size the limit per task, not once for your whole app: a classification and a long report need very different limits. Start generous, look at `usage` across real requests, then tighten.

***

## Examples by endpoint

Each example sets an effort and reads the reasoning and the answer, non-streamed and streamed. Read `reasoning_content` with `getattr` in Python and a wider type in TypeScript. The Responses examples set `max_output_tokens`, because Claude models on Responses otherwise use the model's default limit (see [Step 4](#step-4-size-the-output-limit)). Parameters the SDKs don't know, such as the [grounding](https://docs.gloo.com/ai-learning-center/gloo-ai-103/the-generation-step.md#grounded-vs-ungrounded-generation) parameters, go in `extra_body` in Python and top-level with a cast in TypeScript. If your SDK version doesn't type a level you send (such as `max`), pass it in `extra_body` as well.

The Python and TypeScript snippets in each tab reuse the client created in that tab's first example.

> [tab] Completions
>
> [Completions guide](https://docs.gloo.com/api-guides/completions-v2.md) · [API reference](https://docs.gloo.com/api-reference/completions/v2.md)
>
> `POST /ai/v2/guarded/chat/completions`. The answer is moderated; the reasoning is not ([details](https://docs.gloo.com/api-guides/reasoning.md#reasoning-is-not-moderated)).
>
> ```bash cURL theme={null}
> curl -X POST 'https://platform.ai.gloo.com/ai/v2/guarded/chat/completions' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning_effort": "medium",
>     "messages": [
>       { "role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?" }
>     ]
>   }'
> ```
>
> ```python Python theme={null}
> import os
> from openai import OpenAI
>
> client = OpenAI(
>     api_key=os.environ["GLOO_API_KEY"],
>     base_url="https://platform.ai.gloo.com/ai/v2/guarded",
> )
>
> completion = client.chat.completions.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning_effort="medium",
>     messages=[{"role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"}],
> )
>
> message = completion.choices[0].message
> print("Reasoning:", getattr(message, "reasoning_content", None))
> print("Answer:", message.content)
> print("Not applied:", getattr(completion, "ignored_params", None))
> ```
>
> ```typescript TypeScript theme={null}
> import OpenAI from 'openai';
>
> const client = new OpenAI({
>   apiKey: process.env.GLOO_API_KEY,
>   baseURL: 'https://platform.ai.gloo.com/ai/v2/guarded',
> });
>
> const completion = await client.chat.completions.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning_effort: 'medium',
>   messages: [{ role: 'user', content: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?' }],
> });
>
> // reasoning_content is not in the SDK's types.
> const message: { content: string | null; reasoning_content?: string | null } =
>   completion.choices[0].message;
> console.log('Reasoning:', message.reasoning_content);
> console.log('Answer:', message.content);
> ```
>
> Streamed:
>
> ```bash cURL theme={null}
> curl -N -X POST 'https://platform.ai.gloo.com/ai/v2/guarded/chat/completions' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning_effort": "medium",
>     "stream": true,
>     "messages": [
>       { "role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?" }
>     ]
>   }'
> ```
>
> ```python Python theme={null}
> stream = client.chat.completions.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning_effort="medium",
>     stream=True,
>     messages=[{"role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"}],
> )
>
> for chunk in stream:
>     if not chunk.choices:
>         continue  # usage-only chunk
>     delta = chunk.choices[0].delta
>     reasoning = getattr(delta, "reasoning_content", None)
>     if reasoning:
>         print(reasoning, end="", flush=True)  # arrives before the answer
>     if delta.content:
>         print(delta.content, end="", flush=True)
> ```
>
> ```typescript TypeScript theme={null}
> const stream = await client.chat.completions.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning_effort: 'medium',
>   stream: true,
>   messages: [{ role: 'user', content: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?' }],
> });
>
> for await (const chunk of stream) {
>   const delta: { content?: string | null; reasoning_content?: string | null } | undefined =
>     chunk.choices[0]?.delta;
>   if (delta?.reasoning_content) process.stdout.write(delta.reasoning_content); // arrives first
>   if (delta?.content) process.stdout.write(delta.content);
> }
> ```

> [tab] Grounded Completions
>
> [Grounded Completions guide](https://docs.gloo.com/api-guides/grounded-completions.md) · [API reference](https://docs.gloo.com/api-reference/completions/v2/grounded.md)
>
> `POST /ai/v2/grounded/chat/completions`. Point the SDK at `https://platform.ai.gloo.com/ai/v2/grounded` and send the grounding parameters as extra fields. The answer is moderated; the reasoning is not.
>
> ```bash cURL theme={null}
> curl -X POST 'https://platform.ai.gloo.com/ai/v2/grounded/chat/completions' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning_effort": "medium",
>     "rag_publisher": "YourPublisherName",
>     "sources_limit": 3,
>     "messages": [
>       { "role": "user", "content": "What are practical ways to build stronger community in a local church?" }
>     ]
>   }'
> ```
>
> ```python Python theme={null}
> import os
> from openai import OpenAI
>
> client = OpenAI(
>     api_key=os.environ["GLOO_API_KEY"],
>     base_url="https://platform.ai.gloo.com/ai/v2/grounded",
> )
>
> completion = client.chat.completions.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning_effort="medium",
>     messages=[{"role": "user", "content": "What are practical ways to build stronger community in a local church?"}],
>     extra_body={"rag_publisher": "YourPublisherName", "sources_limit": 3},
> )
>
> message = completion.choices[0].message
> print("Reasoning:", getattr(message, "reasoning_content", None))
> print("Answer:", message.content)
> ```
>
> ```typescript TypeScript theme={null}
> import OpenAI from 'openai';
>
> const client = new OpenAI({
>   apiKey: process.env.GLOO_API_KEY,
>   baseURL: 'https://platform.ai.gloo.com/ai/v2/grounded',
> });
>
> const completion = await client.chat.completions.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning_effort: 'medium',
>   messages: [{ role: 'user', content: 'What are practical ways to build stronger community in a local church?' }],
>   // Grounding parameters are not in the SDK's types.
>   rag_publisher: 'YourPublisherName',
>   sources_limit: 3,
> } as OpenAI.Chat.ChatCompletionCreateParamsNonStreaming);
>
> const message: { content: string | null; reasoning_content?: string | null } =
>   completion.choices[0].message;
> console.log('Reasoning:', message.reasoning_content);
> console.log('Answer:', message.content);
> ```
>
> Streamed:
>
> ```bash cURL theme={null}
> curl -N -X POST 'https://platform.ai.gloo.com/ai/v2/grounded/chat/completions' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning_effort": "medium",
>     "stream": true,
>     "rag_publisher": "YourPublisherName",
>     "sources_limit": 3,
>     "messages": [
>       { "role": "user", "content": "What are practical ways to build stronger community in a local church?" }
>     ]
>   }'
> ```
>
> ```python Python theme={null}
> stream = client.chat.completions.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning_effort="medium",
>     stream=True,
>     messages=[{"role": "user", "content": "What are practical ways to build stronger community in a local church?"}],
>     extra_body={"rag_publisher": "YourPublisherName", "sources_limit": 3},
> )
>
> 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)
>     if delta.content:
>         print(delta.content, end="", flush=True)
> ```
>
> ```typescript TypeScript theme={null}
> const stream = await client.chat.completions.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning_effort: 'medium',
>   stream: true,
>   messages: [{ role: 'user', content: 'What are practical ways to build stronger community in a local church?' }],
>   rag_publisher: 'YourPublisherName',
>   sources_limit: 3,
> } as OpenAI.Chat.ChatCompletionCreateParamsStreaming);
>
> for await (const chunk of stream) {
>   const delta: { content?: string | null; reasoning_content?: string | null } | undefined =
>     chunk.choices[0]?.delta;
>   if (delta?.reasoning_content) process.stdout.write(delta.reasoning_content);
>   if (delta?.content) process.stdout.write(delta.content);
> }
> ```

> [tab] Direct Completions
>
> [Direct Completions guide](https://docs.gloo.com/api-guides/direct-completions.md) · [API reference](https://docs.gloo.com/api-reference/completions/direct.md)
>
> `POST /ai/v2/direct/chat/completions`. Nothing is moderated on direct endpoints.
>
> ```bash cURL theme={null}
> curl -X POST 'https://platform.ai.gloo.com/ai/v2/direct/chat/completions' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning_effort": "high",
>     "messages": [
>       { "role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?" }
>     ]
>   }'
> ```
>
> ```python Python theme={null}
> import os
> from openai import OpenAI
>
> client = OpenAI(
>     api_key=os.environ["GLOO_API_KEY"],
>     base_url="https://platform.ai.gloo.com/ai/v2/direct",
> )
>
> completion = client.chat.completions.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning_effort="high",
>     messages=[{"role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"}],
> )
>
> message = completion.choices[0].message
> print("Reasoning:", getattr(message, "reasoning_content", None))
> print("Answer:", message.content)
> ```
>
> ```typescript TypeScript theme={null}
> import OpenAI from 'openai';
>
> const client = new OpenAI({
>   apiKey: process.env.GLOO_API_KEY,
>   baseURL: 'https://platform.ai.gloo.com/ai/v2/direct',
> });
>
> const completion = await client.chat.completions.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning_effort: 'high',
>   messages: [{ role: 'user', content: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?' }],
> });
>
> const message: { content: string | null; reasoning_content?: string | null } =
>   completion.choices[0].message;
> console.log('Reasoning:', message.reasoning_content);
> console.log('Answer:', message.content);
> ```
>
> Streamed:
>
> ```bash cURL theme={null}
> curl -N -X POST 'https://platform.ai.gloo.com/ai/v2/direct/chat/completions' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning_effort": "high",
>     "stream": true,
>     "messages": [
>       { "role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?" }
>     ]
>   }'
> ```
>
> ```python Python theme={null}
> stream = client.chat.completions.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning_effort="high",
>     stream=True,
>     messages=[{"role": "user", "content": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"}],
> )
>
> 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)
>     if delta.content:
>         print(delta.content, end="", flush=True)
> ```
>
> ```typescript TypeScript theme={null}
> const stream = await client.chat.completions.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning_effort: 'high',
>   stream: true,
>   messages: [{ role: 'user', content: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?' }],
> });
>
> for await (const chunk of stream) {
>   const delta: { content?: string | null; reasoning_content?: string | null } | undefined =
>     chunk.choices[0]?.delta;
>   if (delta?.reasoning_content) process.stdout.write(delta.reasoning_content);
>   if (delta?.content) process.stdout.write(delta.content);
> }
> ```

> [tab] Responses
>
> [Responses guide](https://docs.gloo.com/api-guides/responses.md) · [API reference](https://docs.gloo.com/api-reference/responses/governed.md)
>
> `POST /ai/v2/guarded/responses`. The answer is moderated; the reasoning summary is not.
>
> ```bash cURL theme={null}
> curl -X POST 'https://platform.ai.gloo.com/ai/v2/guarded/responses' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning": { "effort": "medium", "summary": "auto" },
>     "max_output_tokens": 8192,
>     "input": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"
>   }'
> ```
>
> ```python Python theme={null}
> import os
> from openai import OpenAI
>
> client = OpenAI(
>     api_key=os.environ["GLOO_API_KEY"],
>     base_url="https://platform.ai.gloo.com/ai/v2/guarded",
> )
>
> response = client.responses.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning={"effort": "medium", "summary": "auto"},
>     max_output_tokens=8192,
>     input="Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?",
> )
>
> for item in response.output:
>     if item.type == "reasoning":
>         print("Reasoning:", " ".join(part.text for part in item.summary))
> print("Answer:", response.output_text)
> ```
>
> ```typescript TypeScript theme={null}
> import OpenAI from 'openai';
>
> const client = new OpenAI({
>   apiKey: process.env.GLOO_API_KEY,
>   baseURL: 'https://platform.ai.gloo.com/ai/v2/guarded',
> });
>
> const response = await client.responses.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning: { effort: 'medium', summary: 'auto' },
>   max_output_tokens: 8192,
>   input: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?',
> });
>
> for (const item of response.output) {
>   if (item.type === 'reasoning') {
>     console.log('Reasoning:', item.summary.map((part) => part.text).join(' '));
>   }
> }
> console.log('Answer:', response.output_text);
> ```
>
> Streamed:
>
> ```bash cURL theme={null}
> curl -N -X POST 'https://platform.ai.gloo.com/ai/v2/guarded/responses' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning": { "effort": "medium", "summary": "auto" },
>     "max_output_tokens": 8192,
>     "stream": true,
>     "input": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"
>   }'
> ```
>
> ```python Python theme={null}
> stream = client.responses.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning={"effort": "medium", "summary": "auto"},
>     max_output_tokens=8192,
>     stream=True,
>     input="Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?",
> )
>
> for event in stream:
>     if event.type == "response.reasoning_summary_text.delta":
>         print(event.delta, end="", flush=True)  # arrives before the answer
>     elif event.type == "response.output_text.delta":
>         print(event.delta, end="", flush=True)
> ```
>
> ```typescript TypeScript theme={null}
> const stream = await client.responses.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning: { effort: 'medium', summary: 'auto' },
>   max_output_tokens: 8192,
>   stream: true,
>   input: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?',
> });
>
> for await (const event of stream) {
>   if (event.type === 'response.reasoning_summary_text.delta') {
>     process.stdout.write(event.delta); // arrives before the answer
>   } else if (event.type === 'response.output_text.delta') {
>     process.stdout.write(event.delta);
>   }
> }
> ```

> [tab] Grounded Responses
>
> [Grounded Responses guide](https://docs.gloo.com/api-guides/grounded-responses.md) · [API reference](https://docs.gloo.com/api-reference/responses/grounded.md)
>
> `POST /ai/v1/grounded/responses`. Point the SDK at `https://platform.ai.gloo.com/ai/v1/grounded` and send the grounding parameters as extra fields.
>
> ```bash cURL theme={null}
> curl -X POST 'https://platform.ai.gloo.com/ai/v1/grounded/responses' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning": { "effort": "medium", "summary": "auto" },
>     "max_output_tokens": 8192,
>     "input": "What resources do you have on family counseling?",
>     "rag_publisher": "YourPublisherName",
>     "sources_limit": 3
>   }'
> ```
>
> ```python Python theme={null}
> import os
> from openai import OpenAI
>
> client = OpenAI(
>     api_key=os.environ["GLOO_API_KEY"],
>     base_url="https://platform.ai.gloo.com/ai/v1/grounded",
> )
>
> response = client.responses.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning={"effort": "medium", "summary": "auto"},
>     max_output_tokens=8192,
>     input="What resources do you have on family counseling?",
>     extra_body={"rag_publisher": "YourPublisherName", "sources_limit": 3},
> )
>
> for item in response.output:
>     if item.type == "reasoning":
>         print("Reasoning:", " ".join(part.text for part in item.summary))
> print("Answer:", response.output_text)
> ```
>
> ```typescript TypeScript theme={null}
> import OpenAI from 'openai';
>
> const client = new OpenAI({
>   apiKey: process.env.GLOO_API_KEY,
>   baseURL: 'https://platform.ai.gloo.com/ai/v1/grounded',
> });
>
> const response = await client.responses.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning: { effort: 'medium', summary: 'auto' },
>   max_output_tokens: 8192,
>   input: 'What resources do you have on family counseling?',
>   // Grounding parameters are not in the SDK's types.
>   rag_publisher: 'YourPublisherName',
>   sources_limit: 3,
> } as OpenAI.Responses.ResponseCreateParamsNonStreaming);
>
> for (const item of response.output) {
>   if (item.type === 'reasoning') {
>     console.log('Reasoning:', item.summary.map((part) => part.text).join(' '));
>   }
> }
> console.log('Answer:', response.output_text);
> ```
>
> Streamed:
>
> ```bash cURL theme={null}
> curl -N -X POST 'https://platform.ai.gloo.com/ai/v1/grounded/responses' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning": { "effort": "medium", "summary": "auto" },
>     "max_output_tokens": 8192,
>     "stream": true,
>     "input": "What resources do you have on family counseling?",
>     "rag_publisher": "YourPublisherName",
>     "sources_limit": 3
>   }'
> ```
>
> ```python Python theme={null}
> stream = client.responses.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning={"effort": "medium", "summary": "auto"},
>     max_output_tokens=8192,
>     stream=True,
>     input="What resources do you have on family counseling?",
>     extra_body={"rag_publisher": "YourPublisherName", "sources_limit": 3},
> )
>
> for event in stream:
>     if event.type == "response.reasoning_summary_text.delta":
>         print(event.delta, end="", flush=True)
>     elif event.type == "response.output_text.delta":
>         print(event.delta, end="", flush=True)
> ```
>
> ```typescript TypeScript theme={null}
> const stream = await client.responses.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning: { effort: 'medium', summary: 'auto' },
>   max_output_tokens: 8192,
>   stream: true,
>   input: 'What resources do you have on family counseling?',
>   rag_publisher: 'YourPublisherName',
>   sources_limit: 3,
> } as OpenAI.Responses.ResponseCreateParamsStreaming);
>
> for await (const event of stream) {
>   if (event.type === 'response.reasoning_summary_text.delta') {
>     process.stdout.write(event.delta);
>   } else if (event.type === 'response.output_text.delta') {
>     process.stdout.write(event.delta);
>   }
> }
> ```

> [tab] Direct Responses
>
> [Direct Responses guide](https://docs.gloo.com/api-guides/direct-responses.md) · [API reference](https://docs.gloo.com/api-reference/responses/v2.md)
>
> `POST /ai/v2/direct/responses`. On a stream, models outside the Anthropic, Google and OpenAI families return raw reasoning instead of a summary; see [Raw reasoning on Responses streams](https://docs.gloo.com/api-guides/reasoning.md#raw-reasoning-on-responses-streams).
>
> ```bash cURL theme={null}
> curl -X POST 'https://platform.ai.gloo.com/ai/v2/direct/responses' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning": { "effort": "high", "summary": "auto" },
>     "max_output_tokens": 16384,
>     "input": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"
>   }'
> ```
>
> ```python Python theme={null}
> import os
> from openai import OpenAI
>
> client = OpenAI(
>     api_key=os.environ["GLOO_API_KEY"],
>     base_url="https://platform.ai.gloo.com/ai/v2/direct",
> )
>
> response = client.responses.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning={"effort": "high", "summary": "auto"},
>     max_output_tokens=16384,
>     input="Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?",
> )
>
> for item in response.output:
>     if item.type == "reasoning":
>         print("Reasoning:", " ".join(part.text for part in item.summary))
> print("Answer:", response.output_text)
> ```
>
> ```typescript TypeScript theme={null}
> import OpenAI from 'openai';
>
> const client = new OpenAI({
>   apiKey: process.env.GLOO_API_KEY,
>   baseURL: 'https://platform.ai.gloo.com/ai/v2/direct',
> });
>
> const response = await client.responses.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning: { effort: 'high', summary: 'auto' },
>   max_output_tokens: 16384,
>   input: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?',
> });
>
> for (const item of response.output) {
>   if (item.type === 'reasoning') {
>     console.log('Reasoning:', item.summary.map((part) => part.text).join(' '));
>   }
> }
> console.log('Answer:', response.output_text);
> ```
>
> Streamed:
>
> ```bash cURL theme={null}
> curl -N -X POST 'https://platform.ai.gloo.com/ai/v2/direct/responses' \
>   -H "Authorization: Bearer $GLOO_API_KEY" \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "gloo-anthropic-claude-sonnet-5.5",
>     "reasoning": { "effort": "high", "summary": "auto" },
>     "max_output_tokens": 16384,
>     "stream": true,
>     "input": "Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?"
>   }'
> ```
>
> ```python Python theme={null}
> stream = client.responses.create(
>     model="gloo-anthropic-claude-sonnet-5.5",
>     reasoning={"effort": "high", "summary": "auto"},
>     max_output_tokens=16384,
>     stream=True,
>     input="Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?",
> )
>
> for event in stream:
>     # reasoning_text events carry raw reasoning (models outside the Anthropic, Google and OpenAI families).
>     if event.type in ("response.reasoning_summary_text.delta", "response.reasoning_text.delta"):
>         print(event.delta, end="", flush=True)
>     elif event.type == "response.output_text.delta":
>         print(event.delta, end="", flush=True)
> ```
>
> ```typescript TypeScript theme={null}
> const stream = await client.responses.create({
>   model: 'gloo-anthropic-claude-sonnet-5.5',
>   reasoning: { effort: 'high', summary: 'auto' },
>   max_output_tokens: 16384,
>   stream: true,
>   input: 'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?',
> });
>
> for await (const event of stream) {
>   // reasoning_text events carry raw reasoning (models outside the Anthropic, Google and OpenAI families).
>   if (
>     event.type === 'response.reasoning_summary_text.delta' ||
>     event.type === 'response.reasoning_text.delta'
>   ) {
>     process.stdout.write(event.delta);
>   } else if (event.type === 'response.output_text.delta') {
>     process.stdout.write(event.delta);
>   }
> }
> ```

### How the endpoints differ

| | Chat Completions endpoints | Responses endpoints |
| :- | :- | :- |
| Reasoning comes back when | An effort other than `none` is set | `reasoning.summary` is set |
| Open-weight models' raw reasoning | In `reasoning_content` | Streamed as `reasoning_text` content; a summary item when not streamed |
| Answer moderated | Completions and Grounded Completions: yes, reasoning is not. Direct: no | Responses: yes, the reasoning summary is not. Grounded and Direct: no |

The guide's [How the endpoints differ](https://docs.gloo.com/api-guides/reasoning.md#how-the-endpoints-differ) table also covers passing reasoning back across turns.

***

## Complete Example

This script puts the steps together for one model: it reads the model's levels from the catalog, picks the level to send, makes the request with a sized limit, and on a cut-off tries once more with double the limit before printing the answer.

```python Python theme={null}
import os

import requests
from openai import OpenAI

MODEL = "gloo-anthropic-claude-sonnet-5.5"
ORDER = ["none", "minimal", "low", "medium", "high", "xhigh", "max"]
# Starting limits from the sizing table, for a medium-length answer.
LIMITS = {"low": 8192, "medium": 16384, "high": 32768, "xhigh": 32768, "max": 32768}

client = OpenAI(
    api_key=os.environ["GLOO_API_KEY"],
    base_url="https://platform.ai.gloo.com/ai/v2/direct",
)


def catalog_entry(model_id: str) -> dict:
    models = requests.get("https://platform.ai.gloo.com/platform/v2/models").json()["data"]
    return next(m for m in models if m["id"] == model_id)


def pick_effort(model: dict, wanted: str) -> str | None:
    """The level to send for `wanted`, or None to send no effort."""
    reasoning = model.get("reasoning")
    if reasoning is None:
        return None
    levels = reasoning["effort_levels"]
    if wanted == "none":
        return "none" if "none" in levels else None
    graded = [level for level in levels if level != "none"]
    if not graded:
        return None
    return min(graded, key=lambda level: abs(ORDER.index(level) - ORDER.index(wanted)))


def ask(prompt: str, wanted_effort: str = "high") -> str:
    model = catalog_entry(MODEL)
    effort = pick_effort(model, wanted_effort)
    limit = LIMITS.get(effort or "medium", 16384)
    ceiling = model.get("max_output_tokens") or 65536

    # On a cut-off, try once more with double the limit (never above the model's maximum).
    for limit in (limit, min(limit * 2, ceiling)):
        params = {"model": MODEL, "max_tokens": limit, "messages": [{"role": "user", "content": prompt}]}
        if effort:
            params["reasoning_effort"] = effort
        completion = client.chat.completions.create(**params)

        choice = completion.choices[0]
        output_tokens = completion.usage.completion_tokens if completion.usage else None
        print(f"effort={effort} limit={limit} output_tokens={output_tokens}")
        ignored = getattr(completion, "ignored_params", None)
        if ignored:
            print("Not applied:", ignored)

        if choice.finish_reason != "length":
            reasoning = getattr(choice.message, "reasoning_content", None)
            if reasoning:
                print("Reasoning:", reasoning[:300], "...")
            return choice.message.content
    raise RuntimeError(f"Cut off at max_tokens={limit}; lower the effort.")


if __name__ == "__main__":
    print(ask("Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them "
              "and the rest attend one. About how many distinct people attend?"))
```

```typescript TypeScript theme={null}
import OpenAI from 'openai';

type ModelReasoning = {
  effort_levels: string[];
  returns_summary: boolean;
  thinks_by_default: boolean;
  can_disable: boolean;
};
type Model = { id: string; max_output_tokens?: number; reasoning: ModelReasoning | null };

const MODEL = 'gloo-anthropic-claude-sonnet-5.5';
const ORDER = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
// Starting limits from the sizing table, for a medium-length answer.
const LIMITS: Record<string, number> = { low: 8192, medium: 16384, high: 32768, xhigh: 32768, max: 32768 };

const client = new OpenAI({
  apiKey: process.env.GLOO_API_KEY,
  baseURL: 'https://platform.ai.gloo.com/ai/v2/direct',
});

async function catalogEntry(id: string): Promise<Model> {
  const res = await fetch('https://platform.ai.gloo.com/platform/v2/models');
  const { data } = (await res.json()) as { data: Model[] };
  return data.find((m) => m.id === id)!;
}

function pickEffort(model: Model, wanted: string): string | undefined {
  if (!model.reasoning) return undefined;
  const levels = model.reasoning.effort_levels;
  if (wanted === 'none') return levels.includes('none') ? 'none' : undefined;
  const graded = levels.filter((level) => level !== 'none');
  if (graded.length === 0) return undefined;
  const distance = (level: string) => Math.abs(ORDER.indexOf(level) - ORDER.indexOf(wanted));
  return graded.reduce((best, level) => (distance(level) < distance(best) ? level : best));
}

async function ask(prompt: string, wantedEffort = 'high'): Promise<string | null> {
  const model = await catalogEntry(MODEL);
  const effort = pickEffort(model, wantedEffort);
  const first = LIMITS[effort ?? 'medium'] ?? 16384;
  const ceiling = model.max_output_tokens ?? 65536;

  // On a cut-off, try once more with double the limit (never above the model's maximum).
  const limits = [first, Math.min(first * 2, ceiling)];
  for (const limit of limits) {
    const completion = await client.chat.completions.create({
      model: MODEL,
      max_tokens: limit,
      messages: [{ role: 'user', content: prompt }],
      // Levels such as 'max' may not be in your SDK version's types.
      ...(effort ? { reasoning_effort: effort as OpenAI.ReasoningEffort } : {}),
    });

    const choice = completion.choices[0];
    console.log(`effort=${effort} limit=${limit} output_tokens=${completion.usage?.completion_tokens}`);
    const ignored = (completion as { ignored_params?: unknown }).ignored_params;
    if (ignored) console.log('Not applied:', ignored);

    if (choice.finish_reason !== 'length') {
      const message: { content: string | null; reasoning_content?: string | null } = choice.message;
      if (message.reasoning_content) console.log('Reasoning:', message.reasoning_content.slice(0, 300), '...');
      return message.content;
    }
  }
  throw new Error(`Cut off at max_tokens=${limits[1]}; lower the effort.`);
}

async function main() {
  console.log(
    await ask(
      'Three Masses draw 140, 95 and 210 people. 15% of the people attend two of them and the rest attend one. About how many distinct people attend?',
    ),
  );
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
```

Run it:

```bash Python theme={null}
python reasoning.py
```

```bash TypeScript theme={null}
npx tsx reasoning.ts
```

### What You'll See

A line with the effort, limit and output tokens used, a short excerpt of the reasoning when the model returned some, and the answer: about 387 distinct people.

***

## Troubleshooting

**The answer is empty or stops mid-sentence**
: Check `finish_reason` (Chat Completions) or `status` (Responses). `"length"` or `"incomplete"` with `max_output_tokens` means the model spent the limit thinking. Raise the limit or lower the effort; resending the same request usually ends the same way. See [Step 5](#step-5-detect-and-handle-a-cut-off).

**My `max_tokens` of 1,024 came back as a bigger number of output tokens**
: On Chat Completions, a reasoning model's limit below 8,192 is raised to 8,192 so it has room to think. You're billed only for the tokens the model uses.

**No reasoning comes back**
: On Responses, add `"summary": "auto"` to `reasoning`; an effort alone returns no summary. On Chat Completions, set an effort other than `none`. Claude 5 models can also skip reasoning on easy requests, and models with `returns_summary: false` (such as Gemma) don't guarantee it.

**`ignored_params` lists my effort**
: The model doesn't list that level, so it ran at the nearest level below it that it does list. Send a level from the model's `effort_levels` ([Step 1](#step-1-find-the-levels-a-model-takes)).

**`none` didn't turn reasoning off, and I'm still billed for reasoning**
: Only models with `can_disable: true` can turn it off. On others, such as Claude 5 and Gemini 2.5 Pro, `none` runs at the model's lowest level and is listed in `ignored_params`. Use a model that can disable reasoning, or accept the lowest level.

**Claude 4 models ignore the effort**
: Claude 4 models such as Haiku 4.5, Sonnet 4.6 and Opus 4.5 to 4.8 don't reason on these endpoints. To use their thinking, call them on [Anthropic Messages](https://docs.gloo.com/tutorials/anthropic-messages.md#step-4-see-claudes-thinking).

**`reasoning_tokens` is `0` but output tokens are high**
: For Claude models, `reasoning_tokens` is `0` or missing on some endpoints. Their reasoning is still counted in the output tokens.

**A long non-streamed request fails with `3007 / GATEWAY_DEADLINE`**
: It ran past Gloo's response deadline. Stream it instead.

**The streamed reasoning seems to repeat itself**
: If a provider fails while the model is still reasoning, Gloo can retry on another provider, and the model reasons again from the start. On Chat streams both attempts' reasoning is joined; on Responses streams the first reasoning item closes as `incomplete` and a new one opens. See [When a provider fails mid-reasoning](https://docs.gloo.com/api-guides/reasoning.md#when-a-provider-fails-mid-reasoning).

**`422` when I send reasoning back in an assistant message**
: Completions and Grounded Completions refuse `thinking` parts in assistant `content`. Send earlier assistant turns as text; the model reasons afresh each turn.

**TypeScript says `'max'` isn't a valid `reasoning_effort`**
: Older `openai` SDK versions don't type that level. Upgrade the SDK, or cast it.

***

## Next Steps

> [card] [Reasoning guide](https://docs.gloo.com/api-guides/reasoning.md)
>
> Defaults, effort rules, output limits and behavior by model family.

> [card] [Model catalog reference](https://docs.gloo.com/api-reference/get-models/v2.md)
>
> `GET /platform/v2/models`, with each model's `reasoning` object.

> [card] [Handling Streaming Failures](https://docs.gloo.com/best-practices/completions-streaming-failures.md)
>
> Retry, continue or surface a stream that fails partway.

> [card] [Models That Think](https://docs.gloo.com/ai-learning-center/gloo-ai-102/models-that-think.md)
>
> What reasoning is, and when more effort pays off.

> [card] [Direct Responses](https://docs.gloo.com/api-guides/direct-responses.md)
>
> Every model in the catalog with one request shape.

> [card] [Anthropic Messages tutorial](https://docs.gloo.com/tutorials/anthropic-messages.md)
>
> Claude's own thinking settings through the Anthropic SDK.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.