# How to Configure Thinking and Reasoning Options in pi-ai

> Learn how to configure thinking and reasoning options in pi-ai. Set the reasoning property in SimpleStreamOptions to control AI thought processes and enhance model capabilities.

- Repository: [Mario Zechner/pi-mono](https://github.com/badlogic/pi-mono)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Configure thinking and reasoning in pi-ai by setting the `reasoning` property in `SimpleStreamOptions` to a `ThinkingLevel` value (`"minimal"` through `"xhigh"`) while ensuring your selected model has `reasoning: true` in its definition.**

The `pi-ai` library from the `badlogic/pi-mono` repository provides a unified interface for streaming LLM responses with configurable reasoning capabilities. Understanding how to configure thinking and reasoning options allows you to control the depth of chain-of-thought processing in supported models, balancing response quality against token usage and latency.

## Understanding Thinking Levels and Budgets

The library defines reasoning configuration through two primary interfaces in [`packages/ai/src/types.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/types.ts).

**ThinkingLevel** controls the amount of chain-of-thought the model emits. Valid values are `"minimal"`, `"low"`, `"medium"`, `"high"`, and `"xhigh"`.

**ThinkingBudgets** provides token limits for each level, useful for token-based providers like OpenAI or Anthropic. This is a partial map where you can specify budgets for specific levels (e.g., `{ high: 1200, xhigh: 2000 }`).

**Model Support** is determined by the `reasoning` boolean flag on the `Model` type. Only models with `reasoning: true` (defined in [`packages/ai/src/models.generated.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/models.generated.ts)) will accept thinking options.

## Configuring Reasoning in Stream Options

### Basic Configuration with SimpleStreamOptions

To enable reasoning, pass the `reasoning` property when calling `streamSimple` or `completeSimple` from [`packages/ai/src/stream.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/stream.ts).

```typescript
import { streamSimple, models, type SimpleStreamOptions } from "@mariozechner/pi-ai";

const model = models.find(m => m.id === "anthropic-sonnet-3.5-thinking");
if (!model || !model.reasoning) throw new Error("Model not found or doesn't support reasoning");

const context = {
  systemPrompt: "You are a helpful assistant.",
  messages: [
    { role: "user", content: "Explain quantum entanglement.", timestamp: Date.now() }
  ],
};

const options: SimpleStreamOptions = {
  reasoning: "high",
  maxTokens: 1024,
  temperature: 0.7,
};

const stream = streamSimple(model, context, options);

stream.on("thinking_start", e => console.log("🧠 Thinking started"));
stream.on("thinking_delta", e => process.stdout.write(e.delta));
stream.on("thinking_end", e => console.log("\n🧠 Thinking complete"));
stream.on("text_delta", e => process.stdout.write(e.delta));
stream.on("done", e => console.log("\n✅ Stream finished"));

```

*Source:* `SimpleStreamOptions` definition – [`packages/ai/src/types.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/types.ts)  
`streamSimple` implementation – [`packages/ai/src/stream.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/stream.ts)

### Setting Custom Thinking Budgets

For token-based providers, constrain reasoning tokens by providing `thinkingBudgets`:

```typescript
const options: SimpleStreamOptions = {
  reasoning: "xhigh",
  thinkingBudgets: {
    high: 800,
    xhigh: 1500,
  },
  maxTokens: 2048,
};

const stream = streamSimple(model, context, options);

```

*Source:* `ThinkingBudgets` type – [`packages/ai/src/types.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/types.ts)

## Selecting Compatible Models

Before configuring reasoning, verify the model supports it by checking the `reasoning` boolean in the model definition:

```typescript
import { models } from "@mariozechner/pi-ai";

const reasoningModels = models.filter(m => m.reasoning === true);
console.log("Available reasoning models:", reasoningModels.map(m => m.id));

// Example: selecting a specific model
const model = models.find(m => m.id === "claude-sonnet-4-5-thinking");
if (model?.reasoning) {
  // Safe to use reasoning options
}

```

*Source:* `Model.reasoning` flag – [`packages/ai/src/types.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/types.ts)  
Model definitions – [`packages/ai/src/models.generated.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/models.generated.ts)

## Web UI Integration

### Message Editor Component

The Web UI exposes reasoning controls through the `<message-editor>` component in [`packages/web-ui/src/components/MessageEditor.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/web-ui/src/components/MessageEditor.ts):

```typescript
// In packages/web-ui/src/components/MessageEditor.ts
@customElement("message-editor")
export class MessageEditor extends LitElement {
  @property() thinkingLevel: ThinkingLevel = "off";

  private _modelSupportsThinking(model?: Model) {
    return model?.reasoning === true;
  }

  private _onThinkingLevelChange(e: Event) {
    this.thinkingLevel = (e.target as HTMLSelectElement).value as ThinkingLevel;
    // Value passed to streamSimple via options.reasoning
  }
}

```

*Source:* `thinkingLevel` property – [`packages/web-ui/src/components/MessageEditor.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/web-ui/src/components/MessageEditor.ts)

### Persistent Storage

User-selected thinking levels persist across sessions via the storage schema defined in [`packages/web-ui/src/storage/types.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/web-ui/src/storage/types.ts):

```typescript
// packages/web-ui/src/storage/types.ts
interface SessionConfig {
  thinkingLevel: ThinkingLevel;  // "off" | "minimal" | "low" | "medium" | "high" | "xhigh"
  // ... other config
}

```

*Source:* `thinkingLevel` field – [`packages/web-ui/src/storage/types.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/web-ui/src/storage/types.ts)

## Summary

- **ThinkingLevel** controls reasoning depth via `"minimal"` to `"xhigh"` values in `SimpleStreamOptions.reasoning`.
- **ThinkingBudgets** optionally caps token usage per level through `SimpleStreamOptions.thinkingBudgets`.
- **Model Compatibility** requires checking `model.reasoning === true` before passing reasoning options.
- **Entry Points** are `streamSimple` and `completeSimple` in [`packages/ai/src/stream.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/stream.ts).
- **Web UI** exposes these controls via the `thinkingLevel` property in `MessageEditor` with persistence in session storage.

## Frequently Asked Questions

### What happens if I configure reasoning on a model that does not support it?

If the model's `reasoning` property is `false`, the library silently ignores the `reasoning` option in `SimpleStreamOptions`. The stream will not emit `<thinking>` blocks, and the request proceeds as a standard completion without chain-of-thought processing.

### How do token budgets interact with non-token-based providers?

Token budgets defined in `thinkingBudgets` are only respected by token-based providers such as OpenAI or Anthropic. Local or non-token providers ignore the budget map and rely solely on the qualitative `thinkingLevel` value to determine reasoning depth.

### Can I change the thinking level dynamically during a conversation?

Yes. Since `thinkingLevel` is persisted in the session configuration ([`packages/web-ui/src/storage/types.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/web-ui/src/storage/types.ts)) and passed fresh to each `streamSimple` call via `SimpleStreamOptions`, you can adjust the level between messages. The Web UI's `MessageEditor` component updates this value via the `_onThinkingLevelChange` handler before each new stream.

### Where does the thinking output appear in the response stream?

Reasoning content emits as discrete events through the stream event listeners. Specifically, `thinking_start`, `thinking_delta`, and `thinking_end` events carry the chain-of-thought content, while `text_delta` carries the final response. This separation allows UI components to render thinking blocks distinctly from final answers.