KDS
Chatbot

Reasoning

Surface colapsável de "pensamento" do modelo — auto-abre durante streaming, fecha quando termina.

Overview

Reasoning mostra o chain-of-thought do modelo — o pensamento interno que precede a resposta final. Construído sobre Collapsible. O comportamento crítico: quando isStreaming é true, o componente abre automaticamente — o usuário vê tokens de raciocínio formando em tempo real. Quando isStreaming vira false, fecha sozinho (a menos que o usuário tenha aberto manualmente).

Use pra modelos com modos extended thinking (Claude com extended_thinking, o1, etc) ou qualquer agente que separe planning de execution.

Preview
Reasoning collapsado — clique pra ver o pensamento.

Vou primeiro enumerar os tokens primitivos do KDS.

A camada base usa Tailwind v4 com namespace nativo --color-*. Cada paleta cobre 50→950, e os tokens semânticos referenciam essas variáveis.

Depois mapeio os 4 temas pra cada token semântico — primary, foreground, border, etc.

Anatomy

<Reasoning isStreaming={...}>
  ├─ <ReasoningTrigger />     ← header com chevron e label "Reasoning"
  └─ <ReasoningContent>       ← Streamdown (markdown live)
       {tokens}
</Reasoning>

Usage

"use client";

import { Reasoning, ReasoningContent, ReasoningTrigger } from "@kalvner/kds/ai/reasoning";

export function ThinkingBlock({ thoughts, isStreaming }) {
  return (
    <Reasoning isStreaming={isStreaming}>
      <ReasoningTrigger />
      <ReasoningContent>{thoughts}</ReasoningContent>
    </Reasoning>
  );
}

Props

Reasoning (Root)

PropTipoDefaultDescrição
isStreamingbooleanfalseQuando true, força open=true automaticamente. Quando muda pra false, fecha (a menos que o user tenha clicado pra abrir manualmente).
open / defaultOpenbooleanControle externo (override do auto-behavior).
onOpenChange(open: boolean) => voidCallback quando o user clica trigger.
durationnumber1Segundos para a animação de "pensando" no trigger.
classNamestringOverride.

Aceita os outros props de Collapsible.

ReasoningTrigger

PropTipoDefaultDescrição
titlestring'Reasoning' (ou 'Thought for X seconds' quando isStreaming muda)Texto do header.
classNamestringOverride.

Mostra BrainIcon + title + ChevronDownIcon. Durante streaming exibe duração; ao parar, mostra "Thought for Xs".

ReasoningContent

PropTipoDefaultDescrição
childrenstringMarkdown. Renderiza via Streamdown (memoizado).
classNamestringOverride; padrão tem text-muted-foreground + animações de slide.

Subcomponents

  • Reasoning — provider que gerencia o estado open/closed em resposta ao isStreaming. Sempre o root.
  • ReasoningTrigger — header clicável com chevron + label dinâmico. Usa useReasoning() interno pra ler isStreaming e ajustar o texto.
  • ReasoningContent — região revelada. Renderiza markdown via Streamdown — perfeito pra streams de tokens.

useReasoning()

Hook retorna { isStreaming: boolean }. Use pra montar triggers custom (ex.: trigger que mostra avatar do modelo durante thinking):

import { useReasoning } from "@kalvner/kds/ai/reasoning";

function CustomTrigger() {
  const { isStreaming } = useReasoning();
  return <button>{isStreaming ? "🧠 thinking" : "Show reasoning"}</button>;
}

States

StateComportamento
isStreaming=trueAbre automaticamente, conteúdo aparece. Trigger mostra duration crescente.
isStreaming=false (após streaming)Fecha auto após pequeno delay; trigger mostra "Thought for Xs".
open (controlado)Override total — ignora isStreaming.
User clica triggeronOpenChange dispara; isStreaming já não controla.

Composition

Dentro de Message

<Message from="assistant">
  <MessageContent>
    <Reasoning isStreaming={status === "thinking"}>
      <ReasoningTrigger />
      <ReasoningContent>{thinkingTokens}</ReasoningContent>
    </Reasoning>
    <MessageResponse>{responseTokens}</MessageResponse>
  </MessageContent>
</Message>

Com botão custom (sem padrão Trigger)

<Reasoning isStreaming>
  <CollapsibleTrigger asChild>
    <Button variant="link">🧠 Ver pensamento</Button>
  </CollapsibleTrigger>
  <ReasoningContent>{tokens}</ReasoningContent>
</Reasoning>

When to use

  • Modelos que separam reasoning de output (Claude extended_thinking, o1).
  • Agentes que querem mostrar planning antes da execução.
  • Debug UI pra fluxos de IA — ver o que o modelo "pensou".

When not to use

  • Mensagens regulares — use Message + MessageResponse direto.
  • Tool calls — use Tool; reasoning é raciocínio interno, não execução.
  • Conteúdo crítico que deve estar sempre visível — Reasoning fecha por padrão.

Best practices

  • Sempre passe isStreaming. Sem isso, o componente vira só Collapsible regular — perde o auto-open/close characteristic.
  • Antes do MessageResponse. Reasoning vem antes do texto final pra criar o ritmo "pensei → respondi".
  • Não persista thinking entre regenerações. Quando o user clica regenerate, descarta o reasoning anterior — confunde manter.

Accessibility

ConcernComportamento
RolesTrigger é <button> com aria-expanded. Content é <region>.
KeyboardSpace/Enter alterna o trigger.
FocusTrigger herda focus-visible:ring-[3px].
Live regionContent tem animação slide-in/out que respeita prefers-reduced-motion.
Screen readerAnuncia "expandido"/"recolhido" no toggle.
  • ChainOfThought — versão estruturada com steps numerados.
  • Tool — execução visível (não pensamento).
  • Message — wrapper dentro do qual Reasoning vive.

On this page