AI thinking state

mandatory

Collapsible "Thinking..." block for reasoning models (Claude extended thinking, DeepSeek R1, OpenAI o-family). Separates chain-of-thought from final response. Hosted by chat-message-bubble (#105) and agent-step-trace (#108).

Spec — AI thinking state

Hosted by chat-message-bubble.kmd e agent-step-trace.kmd. Streams via streaming-text.kmd (separate stream channel reasoning).

Princípios

  1. *rovider abstraction*— gateway expõe reasoning_content separated from content; SDK detecta provider.
  2. *ollapsed by default*— pra reduzir noise; user expande quando quer detalhes.
  3. *uto-close on stream end*— opcional, configurable; default close.
  4. *reserve manual expand*— user expandeu → não auto-close.

R1 — Anatomia

┌───────────────────────────────────────────┐
│ 💭 Thinking · 4.2s            [▼]         │  ← collapsed (default)
└───────────────────────────────────────────┘

┌───────────────────────────────────────────┐
│ 💭 Thinking · 4.2s · 234 words   [▲]      │  ← expanded
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━        │
│ "I need to find recent docs about         │
│ Koder Stack architecture. The user is    │
│ asking about the Foundation layer..."    │
│                                           │
│ [reasoning trace continues...]            │
└───────────────────────────────────────────┘

Slots:

Slot Content
Icon 💭 (thinking) or ⚙ (running animated)
Label "Thinking..." (streaming) → "Thought for {duration}" (complete)
Duration live timer during stream; final on complete
Word count when complete (optional)
Toggle expand/collapse chevron
Body full reasoning text (when expanded)

R2 — States

State Visual
streaming "Thinking..." + breathing dot + live timer
complete "Thought for 4.2s" + chevron + word count
collapsed header only
expanded header + body

R3 — Provider abstraction

Gateway response includes optional reasoning_content:

{
  "content": [{"type": "text", "text": "..."}],
  "reasoning_content": "I need to think about this...",
  "reasoning_meta": {
    "model_supports": true,
    "duration_ms": 4200,
    "tokens": 234
  }
}

If reasoning_content is null/missing: do NOT render thinking block.

Provider support:

Provider Native reasoning_content?
Anthropic (Claude extended thinking) Yes
OpenAI (o-family) Yes (different field, gateway translates)
DeepSeek (R1) Yes
Google (Gemini reasoning) Yes
Self-hosted Koder LLM Optional

Gateway translates provider-specific to canonical shape above.

R4 — Auto-close

On done event of reasoning stream:

  • If auto_close: true (default): collapse animated (smooth).
  • If user already expanded manually: preserve expanded state.

User preference: stored per (koder_user_id, workspace_id).

R5 — Multiple thinking blocks (accordion)

Agent runs with reasoning per step (cross-link #108):

[Step 1] ▼ Thinking · 2s
[Step 2] ▶ Thinking · 1.5s
[Step 3] ▼ Thinking · 3s

Independent collapse/expand state per block.

R6 — Surface bindings

Surface API
Flutter KoderThinkingBlock({required content, duration, autoClose}) em koder_kit/lib/src/ai/thinking_block.dart
Web <koder-thinking-block>
Compose/SwiftUI futuro
CLI / TUI Plain: prefix [think] ... indented; toggle via key shortcut

R7 — Acessibilidade

  • Block: <details><summary> semantically (Web); equivalente Flutter.
  • Toggle: keyboard Enter/Space.
  • aria-live="polite" during streaming.
  • Reduced-motion: no smooth collapse; instant.

R8 — i18n

Key en-US pt-BR
ai.thinking.streaming "Thinking..." "Pensando..."
ai.thinking.complete "Thought for {duration}" "Pensou por {duration}"
ai.thinking.words "{n} words" "{n} palavras"

T-suite

  • *1*Mount streaming: response has reasoning_content streaming → block visible with "Thinking..." + breathing dot.
  • *2*Done: stream complete → label becomes "Thought for 4.2s"; collapse animated.
  • *3*No reasoning_content: response without field → block NOT rendered.
  • *4*Manual expand: user clicks → expand; subsequent done does NOT collapse.
  • *5*Multiple blocks (agent steps): each independent.
  • *6*Reduced-motion: collapse instant.
  • *7*Provider abstraction: OpenAI o-family + Claude responses both render correctly.

Source: ../home/koder/dev/koder/meta/docs/stack/specs/ai-ui/thinking-state.kmd