> ## Documentation Index
> Fetch the complete documentation index at: https://thethirdpenco-feat-tool-lifecycle-events.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Thinking

> Control reasoning depth and receive provider thinking output safely

ai-query treats thinking as two separate concerns:

* `reasoning=...` controls how much reasoning a model should use
* `event_stream` emits provider reasoning output during streaming when the provider exposes it

`on_reasoning_event=...` remains available as a callback observer. Thinking
output is not mixed into `text_stream`, and not every provider exposes the same
kind of data.

## 1. Control Thinking Depth

Use the top-level `reasoning` parameter for portable reasoning controls:

```python theme={null}
from ai_query import generate_text
from ai_query.providers import openai, google

openai_result = await generate_text(
    model=openai("gpt-5.4"),
    prompt="Solve this carefully.",
    reasoning={"effort": "high"},
)

google_result = await generate_text(
    model=google("gemini-2.5-pro"),
    prompt="Solve this carefully.",
    reasoning={"budget": 1024},
)
```

Current normalized support:

| Provider                   | Normalized control                                          |
| -------------------------- | ----------------------------------------------------------- |
| OpenAI / OpenAI-compatible | `reasoning={"effort": ...}`                                 |
| Google                     | `reasoning={"budget": ...}`                                 |
| Anthropic                  | `reasoning={"effort": ...}` and `reasoning={"budget": ...}` |

If you need provider-specific options like Gemini `include_thoughts` or Anthropic visibility controls, keep using `provider_options`.

## 2. Receive Thinking Output While Streaming

Use the typed event stream to receive provider reasoning output in order with
text and lifecycle events:

```python theme={null}
from ai_query import stream_text
from ai_query.providers import google

result = stream_text(
    model=google("gemini-2.5-pro"),
    prompt="Explain binary search.",
    provider_options={
        "google": {
            "thinking_config": {
                "thinking_budget": 1024,
                "include_thoughts": True,
            }
        }
    },
)

async for event in result.event_stream:
    if event.type.startswith("reasoning."):
        reasoning = event.event
        print(f"[{reasoning.provider}:{reasoning.kind}]", reasoning.text or reasoning.data)
    elif event.type == "text.delta":
        print(event.text, end="", flush=True)
```

`on_reasoning_event` can still observe the same reasoning events without
consuming the typed stream. `text_stream` still contains only assistant-visible
text.

## ReasoningEvent

Streaming thinking output is delivered as `ReasoningEvent` objects:

```python theme={null}
class ReasoningEvent:
    kind: Literal["summary", "delta", "signature", "state"]
    provider: str
    text: str | None
    data: dict[str, Any]
```

Typical meanings:

| Kind        | Meaning                                 |
| ----------- | --------------------------------------- |
| `delta`     | Incremental thinking text               |
| `summary`   | Provider-generated summary of reasoning |
| `signature` | Continuity or verification metadata     |
| `state`     | Opaque provider reasoning state         |

`summary` and `delta` are both delivered through the same typed stream. They
remain reasoning events, not assistant text, so `text_stream`, `Agent.stream()`,
and `Agent.chat()` do not mix them into the user-visible answer.

## Provider Differences

Thinking output is not portable in the same way as assistant text.

### OpenAI and OpenAI-compatible

* normalized control: `reasoning.effort`
* output events depend on what the provider actually streams
* ai-query currently looks for OpenAI-compatible fields such as:
  * `delta.reasoning_content`
  * `delta.reasoning`
  * `delta.thinking`
  * `delta.thought`
  * `delta.reasoning_summary`
* Responses-style reasoning summaries are emitted as `reasoning.summary`

If a provider only streams final assistant text, `on_reasoning_event` will not fire.

### Google

* normalized control: `reasoning.budget`
* visible thought output is provider-specific and should be enabled with:

```python theme={null}
provider_options={
    "google": {
        "thinking_config": {
            "thinking_budget": 1024,
            "include_thoughts": True,
        }
    }
}
```

* ai-query emits `reasoning.delta` for Gemini thought text and
  `reasoning.signature` for thought signatures

### Anthropic

* normalized control: `reasoning.effort` and `reasoning.budget`
* ai-query emits `reasoning.delta` for streaming thinking text and
  `reasoning.signature` for signatures when Claude returns them

## Agents

Agents support the same split between reasoning control and thinking output:

```python theme={null}
from ai_query import Agent
from ai_query.providers import google

def on_reasoning(event):
    print(event.kind, event.text or event.data)

agent = Agent(
    "assistant",
    model=google("gemini-2.5-pro"),
    reasoning={"budget": 1024},
    on_reasoning_event=on_reasoning,
)
```

When an agent receives a `ReasoningEvent`, it also emits an agent event:

```python theme={null}
await agent.emit("reasoning", {
    "kind": event.kind,
    "provider": event.provider,
    "text": event.text,
    "data": event.data,
})
```

That means thinking output can flow through local callbacks and agent transports such as SSE/WebSocket. The agent's `chat()` and `stream()` methods still return/yield only assistant text.

## Important Guidance

* Do not assume every reasoning-capable model exposes visible thinking output.
* Do not mix reasoning events into user-facing assistant text by default.
* Render `summary` and `delta` through the same reasoning UI channel unless your
  product intentionally separates summaries from raw thinking text.
* Treat `signature` and `state` events as provider metadata, not natural-language output.
* Prefer summaries or explicit UI affordances if you choose to display thinking.

## See Also

* [`Generating Text`](/core/generate-text)
* [`Streaming`](/core/streaming)
* [`stream_text`](/reference/stream-text)
* [`Results`](/reference/types/results)
