Show tool and web search activity in the chat UI
Tool calls were invisible until the answer arrived: the only trace was the sources list, and only for web searches. Surface them as they happen instead. Add llm.stream_tool_calls(), an incremental version of resolve_tool_calls() that yields a start and a completion event per tool call and ends with a "resolved" event carrying the previous return value. resolve_tool_calls() is kept as a thin wrapper that drops the progress events. api/chat.py relays those events as a new SSE "tool" event, and wraps the search-summary/knowledge-save step in one of the same shape, since it is a noticeable pause of its own. On the front end, ToolActivityList.vue renders one row per tool above the assistant bubble (label, target, spinner while running, short result when done). The store merges events by id and marks anything still running as interrupted when the stream ends early. Display only - not persisted, so the rows disappear when the session is reloaded, same as sources. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,8 @@ SSE でストリーミング応答を返す。
|
||||
|---|---|---|
|
||||
| `session_id` | セッション UUID | 新規セッション作成直後 |
|
||||
| `chunk` | テキスト断片 | LLM トークン生成ごと |
|
||||
| `tool` | `{id, name, label, detail, status, result}` | ツール実行の開始時 (`status: running`) と完了時 (`status: done`)。同じ `id` で 2 回届くのでフロントはマージして表示する |
|
||||
| `sources` | `[{title, url}]` | Web 検索した時のみ(出典一覧) |
|
||||
| `notes_updated` | — | AI が共有ノートを書き換えた直後 |
|
||||
| `done` | — | 正常完了 |
|
||||
| `error` | エラーメッセージ | 例外発生時 |
|
||||
|
||||
@@ -146,6 +146,7 @@ LLM に渡すコンテキストは `memory_kb/memory.py` の `get_context_messag
|
||||
|
||||
```
|
||||
data: {"type":"session_id","value":"<uuid>"} # 新規セッション時のみ
|
||||
data: {"type":"tool","value":{...}} # ツール実行ごとに開始・完了の2回
|
||||
data: {"type":"notes_updated"} # AIが共有ノートを更新した時のみ
|
||||
data: {"type":"sources","value":[{...}]} # Web検索した時のみ(出典の title/url)
|
||||
data: {"type":"chunk","value":"テキスト断片"} # 0回以上
|
||||
@@ -153,6 +154,13 @@ data: {"type":"done"} # 正常完了
|
||||
data: {"type":"error","value":"エラー内容"} # 例外発生時
|
||||
```
|
||||
|
||||
`tool` イベントは `llm.stream_tool_calls()`(`resolve_tool_calls()` の逐次版)が yield する進行状況を
|
||||
そのまま中継したもので、`{id, name, label, detail, status}` で始まり、同じ `id` の
|
||||
`{id, status:"done", result}` で終わる。検索結果の要約・蓄積知識への保存も
|
||||
`id: "save_knowledge"` の同形式のイベントとして送る。フロント(`ToolActivityList.vue`)は
|
||||
`id` をキーにマージし、アシスタントバブルの上に実行中スピナーつきで一覧表示する。
|
||||
表示専用なので DB には保存せず、履歴を読み直すと消える。
|
||||
|
||||
バッファリング防止のため `Cache-Control: no-cache` と `X-Accel-Buffering: no` を付与する。
|
||||
|
||||
---
|
||||
|
||||
+4
-1
@@ -46,11 +46,14 @@ Discord 連携は [discord-bot.md](discord-bot.md)、ノートの操作方法は
|
||||
## 1. チャット
|
||||
|
||||
- `POST /chat/stream` が SSE を返し、フロントは fetch + ReadableStream で受けて 1 文字ずつ描画する。
|
||||
イベントは `session_id` / `chunk` / `sources` / `notes_updated` / `done` / `error` の 6 種。
|
||||
イベントは `session_id` / `chunk` / `tool` / `sources` / `notes_updated` / `done` / `error` の 7 種。
|
||||
- アシスタントの発言は `marked` + `DOMPurify` で描画する。ストリーミング途中で閉じていないコードフェンスは
|
||||
自動で補完してから描画するので、生成中でもレイアウトが崩れない。
|
||||
- セッションは左サイドバーの一覧から切り替え・削除できる。タイトルは最初の発言の先頭 40 文字。
|
||||
- Web 検索が走った応答には出典(`sources`)が、各メッセージには自動生成されたタグが添えて表示される。
|
||||
- Web 検索・ノートの読み書き・蓄積知識の読み出しといったツール実行は、バブルの上に
|
||||
「🔍 Web検索 / クエリ / ✓ 5件の結果」の形でリアルタイムに表示される(`ToolActivityList.vue`)。
|
||||
実行中はスピナーが回り、応答が途中で切れたツールは「⚠ 中断されました」になる。表示専用で永続化はしない。
|
||||
- 失敗時は赤いバブルとトースト通知の両方で知らせる。
|
||||
|
||||
## 2. セッションの書き出し / 取り込み
|
||||
|
||||
Reference in New Issue
Block a user