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>
232 lines
6.4 KiB
Markdown
232 lines
6.4 KiB
Markdown
# API リファレンス
|
||
|
||
ベースURL: `http://localhost:8000`
|
||
Swagger UI: `http://localhost:8000/docs`
|
||
|
||
---
|
||
|
||
## チャット
|
||
|
||
### POST /chat/stream
|
||
SSE でストリーミング応答を返す。
|
||
|
||
**リクエストボディ**
|
||
```json
|
||
{
|
||
"message": "こんにちは",
|
||
"session_id": "uuid(省略時は新規セッション作成)",
|
||
"model": "gpt-5.4(省略時は DEFAULT_MODEL)"
|
||
}
|
||
```
|
||
|
||
**SSE イベント**
|
||
| type | value | タイミング |
|
||
|---|---|---|
|
||
| `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` | エラーメッセージ | 例外発生時 |
|
||
|
||
---
|
||
|
||
### GET /chat/sessions
|
||
セッション一覧を `updated_at` 降順で返す。
|
||
|
||
**レスポンス**
|
||
```json
|
||
[
|
||
{
|
||
"id": "uuid",
|
||
"title": "セッションタイトル",
|
||
"model": "gpt-5.4",
|
||
"updated_at": "2026-06-01T12:00:00"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### GET /chat/sessions/{session_id}/messages
|
||
セッションのメッセージ一覧を `created_at` 昇順で返す。要約に取り込まれた(アーカイブ済みの)
|
||
メッセージも含めて返し、`archived_at` で区別する。アーカイブ済みのものは LLM に渡す
|
||
コンテキストには入らないが、履歴としては残っている。
|
||
|
||
**レスポンス**
|
||
```json
|
||
[
|
||
{ "id": 1, "role": "user", "content": "...", "created_at": "...", "archived_at": "2026-08-20T04:00:00+00:00", "tags": [] },
|
||
{ "id": 2, "role": "assistant", "content": "...", "created_at": "...", "archived_at": null, "tags": [] }
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### DELETE /chat/sessions/{session_id}
|
||
セッションと紐づくメッセージ・メモリをすべて削除。`204 No Content` を返す。
|
||
|
||
---
|
||
|
||
## メモリ
|
||
|
||
### GET /memory/{session_id}
|
||
セッションの長期メモリ(要約)一覧を `created_at` 昇順で返す。
|
||
|
||
**レスポンス**
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"session_id": "uuid",
|
||
"summary": "要約テキスト",
|
||
"created_at": "2026-06-01T12:00:00"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### DELETE /memory/{memory_id}
|
||
特定のメモリエントリを削除。`204 No Content` を返す。
|
||
|
||
---
|
||
|
||
## 共有ノート
|
||
|
||
ユーザーと AI が共同編集するノート。ページのタイトル一覧は毎回システムプロンプトに注入され、
|
||
本文は AI が `read_note` / `write_note` ツールで読み書きする。
|
||
`section` を設定すると、同じセクション名のページはタブバー上で常に連続してグループ表示される。
|
||
|
||
### GET /api/notes
|
||
全ページを `position` 昇順で返す。同じ `section` のページは常に連続する(隣接=同一グループとして扱われる)。
|
||
|
||
**レスポンス**
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"title": "買い物リスト",
|
||
"content": "- 牛乳
|
||
- 卵",
|
||
"position": 0,
|
||
"section": "生活",
|
||
"updated_at": "2026-08-19T12:00:00"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### POST /api/notes
|
||
ページを新規作成し、`201 Created` で作成したページを返す。
|
||
`section` を指定すると、作成直後にそのセクションの末尾へ配置される(他ページの `position` も再採番される)。
|
||
|
||
**リクエスト**
|
||
```json
|
||
{ "title": "新しいページ", "content": "", "section": null }
|
||
```
|
||
|
||
---
|
||
|
||
### PATCH /api/notes/{page_id}
|
||
ページのタイトル・本文・セクションを更新する。指定したフィールドだけが変更される。
|
||
タイトルを空文字にしようとした場合は `400`、ページが存在しない場合は `404` を返す。
|
||
|
||
`section` はリクエストボディに**キーとして含まれているかどうか**で判定する(`null` を明示的に送るとグループ解除)。
|
||
セクションを変更すると、そのページは新しいセクションの末尾(該当ページが無ければ末尾)へ移動し、
|
||
全ページの `position` が振り直される。
|
||
|
||
**リクエスト**
|
||
```json
|
||
{ "content": "更新後の本文", "section": "生活" }
|
||
```
|
||
|
||
---
|
||
|
||
### POST /api/notes/sections/rename
|
||
`old_name` セクションに属する全ページを `new_name` セクションへ一括で付け替える。
|
||
`new_name` を空文字/未指定にすると、そのセクションの全ページのグループを解除する。
|
||
ページの並び順(`position`)は変更しない。
|
||
|
||
**リクエスト**
|
||
```json
|
||
{ "old_name": "生活", "new_name": "生活雑貨" }
|
||
```
|
||
|
||
**レスポンス**
|
||
```json
|
||
{ "renamed": 2 }
|
||
```
|
||
|
||
---
|
||
|
||
### POST /api/notes/reorder
|
||
タブのドラッグ&ドロップによる並べ替えを反映する。`order` には**全ページID**をドラッグ後の
|
||
新しい順序で渡す(サーバー側では計算しない。フロントが `store.pages` から算出する)。
|
||
`page_id` のページには先に `section` を設定してから、`order` の順に `position` を振り直す。
|
||
別セクションのタブの前後にドロップした場合は、そのセクションへの移動も兼ねる。
|
||
|
||
`order` が現在の全ページIDの集合と一致しない場合は `400` を返す。
|
||
|
||
**リクエスト**
|
||
```json
|
||
{ "page_id": 3, "section": "生活", "order": [1, 3, 2, 4] }
|
||
```
|
||
|
||
**レスポンス**
|
||
```json
|
||
{ "reordered": 4 }
|
||
```
|
||
|
||
---
|
||
|
||
### DELETE /api/notes/{page_id}
|
||
ページを削除。`204 No Content` を返す。
|
||
|
||
---
|
||
|
||
## ログ
|
||
|
||
### GET /logs
|
||
LLM API 呼び出しログを `created_at` 降順で返す。
|
||
|
||
**クエリパラメーター**
|
||
| パラメーター | 型 | 説明 |
|
||
|---|---|---|
|
||
| `session_id` | string | セッションで絞り込み |
|
||
| `status` | `ok` / `error` | 成功・失敗で絞り込み |
|
||
| `limit` | int (≤500) | 最大件数(デフォルト 100) |
|
||
| `offset` | int | スキップ件数 |
|
||
|
||
**レスポンス**
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"session_id": "uuid",
|
||
"model": "gpt-5.4",
|
||
"request_json": "[{\"role\":\"user\",\"content\":\"...\"}]",
|
||
"response_text": "...",
|
||
"error": null,
|
||
"latency_ms": 832.5,
|
||
"created_at": "2026-06-01T12:00:00"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### DELETE /logs/{log_id}
|
||
指定ログを削除。`204 No Content` を返す。
|
||
|
||
---
|
||
|
||
## ヘルスチェック
|
||
|
||
### GET /health
|
||
```json
|
||
{ "status": "ok" }
|
||
```
|