# Reasoning assistant

A multi-turn engineering assistant that shows its step-by-step reasoning in a collapsible block before every answer — the "thinking" UX, powered by kai-chat.

An assistant that works a problem out loud. Each assistant turn opens with a collapsible reasoning block — the model’s working — followed by a concise answer. Here it debugs a real p99 latency regression across a few follow-up turns. Expand any "Thought for…" block to read the reasoning, then ask your own follow-up.

## How it works

Reasoning lives in the message's `parts` array, not in a separate element. Add a `{ type: 'reasoning', text, label? }` part ahead of the `{ type: 'text', … }` part; `<kai-chat>` renders it as a collapsible block above the answer. The `label` is the trigger text (defaults to `Reasoning`) and `text` is rendered as markdown.

```html
<kai-chat id="chat" chat-title="Senior engineer assistant"></kai-chat>

<script type="module">

  await customElements.whenDefined('kai-chat');

  const chat = document.getElementById('chat');
  chat.messages = [];

  chat.addEventListener('kai-submit', async (e) => {
    const prompt = e.detail.value;
    const aId = crypto.randomUUID();

    chat.messages = [
      ...chat.messages,
      { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: prompt }] },
      { id: aId, role: 'assistant', parts: [] },
    ];
    chat.loading = true; // shows the thinking indicator

    // stream the final answer token-by-token
    const { reply, reasoning } = await askAssistant(prompt);
    let answer = '';
    for await (const token of reply) {
      answer += token;
      chat.messages = chat.messages.map((m) =>
        m.id === aId ? { ...m, parts: [{ type: 'text', text: answer }] } : m,
      );
    }

    // attach the reasoning part (ahead of the text part) once the answer is complete
    chat.messages = chat.messages.map((m) =>
      m.id === aId
        ? { ...m, parts: [{ type: 'reasoning', ...reasoning }, ...m.parts], actions: ['copy', 'like', 'dislike'] }
        : m,
    );
    chat.loading = false;
  });
</script>
```

**`reasoning` part shape** — `{ type: 'reasoning', text, label? }`:

| field | type | notes |
|---|---|---|
| `text` | `string` | The reasoning body, rendered as markdown |
| `label` | `string?` | Trigger text — defaults to `Reasoning` (e.g. `Thought for 4s`) |

Attaching the reasoning part only after the answer finishes keeps the thinking indicator clean while tokens stream, then reveals the working in one step. The block stays collapsed by default, so readers who want the answer see it first and can open the reasoning on demand.

## Next steps

- **[Agentic assistant](/examples/agentic-assistant/)** — the same reasoning block paired with live tool calls per turn.
- **[`kai-chat` reference](/components/chat/)** — the full `messages` schema, `loading`, and the `kai-submit` event.
- **[Streaming recipe](/guides/recipes/streaming/)** — driving the token loop from a real backend.
- **[Drop-in chat](/examples/drop-in-chat/)** — the baseline streaming loop this example builds on.
