From 31d5de1a377d17c63f78a6ead052dc4d310c75a1 Mon Sep 17 00:00:00 2001 From: Hare Date: Tue, 28 Apr 2026 16:07:41 +0900 Subject: [PATCH] =?UTF-8?q?Thinking=E3=81=AETUI=E8=A1=A8=E7=A4=BA=E3=81=AE?= =?UTF-8?q?=E3=83=81=E3=82=B1=E3=83=83=E3=83=88=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 2 +- tickets/tui-thinking-display.md | 89 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tickets/tui-thinking-display.md diff --git a/TODO.md b/TODO.md index 86938c0e..ac94e92f 100644 --- a/TODO.md +++ b/TODO.md @@ -20,4 +20,4 @@ - [ ] 使用頻度メトリクス + Knowledge 化候補レポート → [tickets/memory-usage-metrics.md](tickets/memory-usage-metrics.md) - [ ] GC(定期再評価) → [tickets/memory-gc.md](tickets/memory-gc.md) - ワークスペースのメモリーをLintするヘッドレスCLI -- Thinking中のTUI上での表示: 内容の公開/非公開両対応 +- [ ] Thinking ブロックの TUI 表示 → [tickets/tui-thinking-display.md](tickets/tui-thinking-display.md) diff --git a/tickets/tui-thinking-display.md b/tickets/tui-thinking-display.md new file mode 100644 index 00000000..21850e6f --- /dev/null +++ b/tickets/tui-thinking-display.md @@ -0,0 +1,89 @@ +# Thinking ブロックの TUI 表示 + +## 背景 + +Reasoning(extended thinking)系の対応は llm-worker レイヤまで降りている: + +- Anthropic の `thinking`、OpenAI Responses の `reasoning_text` / `reasoning_summary_text`、Gemini の thinking はいずれも `DeltaContent::Thinking(String)` に正規化され、Timeline 層には `ThinkingBlockKind` の Start / Delta / Stop が流れている +- history 上は `Item::Reasoning { text, summary, encrypted_content, ... }` として保持され、session-store にも persist されている + +ところが上位層への通り道が無い: + +- `Worker` の closure 公開 API には `on_thinking_block` が無い(`on_text_block` / `on_tool_use_block` のみ) +- `protocol::Event` に Thinking 系イベントが無い +- pod controller でブリッジしていない +- TUI に Thinking 用ブロックが無い + +結果として、provider が thinking 平文を返していても TUI からは「無音で時間が過ぎる」状態になる。実行中であることが見えず、終わった後に「どれくらい考えていたか」も残らない。 + +provider ごとに本文を流せるかは異なる: + +- **Anthropic**: extended thinking は平文で流れる +- **OpenAI Responses**: モデル / 設定によって本文(`reasoning_text`)が流れない場合がある。`reasoning_summary` は流れることがある +- **Gemini**: thinking は流れるが provider 設定依存 + +「平文があれば流す。無くても thinking 中であることは見せる」というのが基本方針。 + +## 方針 + +- llm-worker → protocol → pod controller → TUI に Thinking の通り道を作る +- worker は `DeltaContent::Thinking` 由来の本文をそのまま渡す。本文を出さない provider のときは Delta が来ないだけで Start / Done は届く +- TUI は実行中とその後の両方を残す: + - **実行中**: `Thinking... (Xs)` ヘッダ + 本文があれば直近 1 行のライブ表示 + - **終了後**: `Thought for Xs` として履歴に残す。`detail` モードでは累積本文を展開 +- token 数表記は当面入れない(`UsageEvent` に reasoning 分離が無く、別チケットで `Usage` を拡張するまで保留) + +## 要件 + +### Worker API + +- `Worker::on_thinking_block(setup)` を `on_text_block` と対称に追加。setup は per-block で 1 回呼ばれ、`block.on_delta(|text|)` / `block.on_stop(|full_text|)` を登録できる + +### Protocol + +- `Event::ThinkingStart`、`Event::ThinkingDelta { text }`、`Event::ThinkingDone { text }` を追加(`text` には完成形を載せる、`TextDone` と同じ流儀) +- 本文を返さない provider では Delta が 0 件のまま Start → Done が届く(破綻しない) +- 1 turn に複数の thinking block が来る可能性がある(provider 都合)。各ブロックは独立して扱う + +### Pod Controller + +- `worker.on_thinking_block` で上記 3 イベントに変換して `event_tx` に流す + +### TUI + +- 新ブロック種別 `Block::Thinking` を持つ +- 実行中は以下のように表示: + ``` + Thinking... (10s) + <累積本文の末尾を 1 行に切り詰めたもの> + ``` +- 終了後は `Thought for 12s` を残す。`detail` モードでは本文をそのまま展開して読める +- `overview` モードは 1 行(例: `Thought for 12s`) +- 経過時間表示のため、Thinking ブロックが実行中の間は再描画が定期的に走る必要がある(粒度は 1Hz 程度で十分) +- ライブ 1 行の選び方は「累積テキストの最後の改行以降を取り、表示幅で切り詰める」を MVP とする +- 同一 turn 内の複数 thinking block は別ブロックとして表示される + +### イベント欠落耐性 + +- `ThinkingDone` が来ないまま `TurnEnd` が来た場合、対応する `Block::Thinking` は経過時間を凍結した状態で履歴に残す。`ToolCall` 側の `Incomplete` と同じ思想 + +### History 再生 + +- `Event::History` の `Item::Reasoning { text, ... }` を `Block::Thinking { text, finished: true }` として復元する(経過時間は持たないので `Thought` のみで時間表示は省く) + +## 範囲外 + +- `UsageEvent` の reasoning_tokens 分離。Anthropic は `output_tokens` に thinking を含み、OpenAI Responses は `output_tokens_details.reasoning_tokens` を別途返すが、現状の `UsageEvent` ではそれを分離していない。本チケットでは token 数表示そのものを行わない +- Anthropic Redacted Thinking(暗号化 blob)の表示。現状 plaintext 経路のみ流れる +- Thinking 本文の Markdown レンダリング / シンタックスハイライト +- thinking を context から prune する話(別軸) + +## 完了条件 + +- Anthropic で extended thinking を有効にした session で「Thinking... (Xs) + 本文 1 行」が live で見え、終了後に `Thought for Xs` として履歴に残る +- 本文を流さない provider 設定でも `Thinking...` ヘッダが表示され、終了後に `Thought for ...` が残る +- `detail` モードで thinking 本文が全行展開できる +- 同一 turn に複数 thinking block が来てもそれぞれ独立に表示される +- `Event::History` 再生で過去の thinking が `Block::Thinking { finished: true }` として復元される +- `ThinkingDone` 欠落でも panic せず、Incomplete 相当の表示で残る +- 既存のテキスト / ツール / notification / compact 表示が壊れない