Files
playchat/docs/api.md
T
colcaroliandClaude Opus 5 0e58a07af4 Pin notes to a chat, and import notes from files
Two additions to the shared notes, both about getting content in and out of
the prompt.

Per-chat pinning. Notes normally reach the model as a title list only, and
the body arrives just if the model decides to call read_note. A 📌 on the tab
now puts a page's full body in the system prompt for the chat that is open,
every turn, no tool round trip. The selection is per session (new table
session_note_pages, unique on session_id + page_id), so different
conversations carry different pages. The title list marks pinned pages so the
model does not read them twice, and bodies are capped per page by
SESSION_NOTES_CONTENT_LIMIT.

A new chat has no session row until its first message, so the front end
cannot PUT the selection yet. POST /chat/stream therefore accepts
note_page_ids and applies it before building the context, which makes the
pins count from the very first turn.

File import. Dropping .md/.txt on the editor - or picking them with the new
⬆ button - creates pages, and a .zip is unpacked into one page per text
entry with the first path component as the section. That is exactly the
shape the export writes, so a downloaded zip round-trips back in. Titles that
collide get a " (2)" suffix rather than overwriting, since restoring a backup
into a live workspace should not destroy anything. UploadFile needs
python-multipart, added to the lockfile.

Toasts grew a kind: the import success message was rendering in the red error
toast, as was the existing session-import message. Both are green now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 16:04:41 +09:00

9.5 KiB
Raw Blame History

API リファレンス

ベースURL: http://localhost:8000
Swagger UI: http://localhost:8000/docs


チャット

POST /chat/stream

SSE でストリーミング応答を返す。

リクエストボディ

{
  "message": "こんにちは",
  "session_id": "uuid(省略時は新規セッション作成)",
  "model": "gpt-5.4(省略時は DEFAULT_MODEL)",
  "note_page_ids": [1, 2]
}

note_page_ids は「このセッションで本文をシステムプロンプトに載せる共有ノートのページ ID」。 渡すとコンテキスト組み立ての前にサーバー側の指定を丸ごと置き換える(PUT /chat/sessions/{id}/notes と同じ内容)。 新規セッションではフロントがまだ session_id を持てないため、初回はここでしか指定できない。省略すると現在の指定を変更しない。

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 降順で返す。

レスポンス

[
  {
    "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 に渡す コンテキストには入らないが、履歴としては残っている。

レスポンス

[
  { "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": [] }
]

GET /chat/sessions/{session_id}/notes

このセッションで本文をシステムプロンプトに載せるよう指定された共有ノートのページ ID を返す。 順序はノートのタブ順(position)。セッションが無ければ 404。

レスポンス

{ "page_ids": [1, 2] }

PUT /chat/sessions/{session_id}/notes

指定を丸ごと置き換える。存在しないページ ID は黙って捨てられ、実際に紐付いた ID が返る。 空配列を渡すと全解除。セッションが無ければ 404。

リクエスト

{ "page_ids": [1, 2] }

DELETE /chat/sessions/{session_id}

セッションと紐づくメッセージ・メモリをすべて削除。204 No Content を返す。


メモリ

GET /memory/{session_id}

セッションの長期メモリ(要約)一覧を created_at 昇順で返す。

レスポンス

[
  {
    "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 のページは常に連続する(隣接=同一グループとして扱われる)。

レスポンス

[
  {
    "id": 1,
    "title": "買い物リスト",
    "content": "- 牛乳
- 卵",
    "position": 0,
    "section": "生活",
    "updated_at": "2026-08-19T12:00:00"
  }
]

POST /api/notes

ページを新規作成し、201 Created で作成したページを返す。 section を指定すると、作成直後にそのセクションの末尾へ配置される(他ページの position も再採番される)。

リクエスト

{ "title": "新しいページ", "content": "", "section": null }

PATCH /api/notes/{page_id}

ページのタイトル・本文・セクションを更新する。指定したフィールドだけが変更される。 タイトルを空文字にしようとした場合は 400、ページが存在しない場合は 404 を返す。

section はリクエストボディにキーとして含まれているかどうかで判定する(null を明示的に送るとグループ解除)。 セクションを変更すると、そのページは新しいセクションの末尾(該当ページが無ければ末尾)へ移動し、 全ページの position が振り直される。

リクエスト

{ "content": "更新後の本文", "section": "生活" }

POST /api/notes/sections/rename

old_name セクションに属する全ページを new_name セクションへ一括で付け替える。 new_name を空文字/未指定にすると、そのセクションの全ページのグループを解除する。 ページの並び順(position)は変更しない。

リクエスト

{ "old_name": "生活", "new_name": "生活雑貨" }

レスポンス

{ "renamed": 2 }

POST /api/notes/reorder

タブのドラッグ&ドロップによる並べ替えを反映する。order には全ページIDをドラッグ後の 新しい順序で渡す(サーバー側では計算しない。フロントが store.pages から算出する)。 page_id のページには先に section を設定してから、order の順に position を振り直す。 別セクションのタブの前後にドロップした場合は、そのセクションへの移動も兼ねる。

order が現在の全ページIDの集合と一致しない場合は 400 を返す。

リクエスト

{ "page_id": 3, "section": "生活", "order": [1, 3, 2, 4] }

レスポンス

{ "reordered": 4 }

DELETE /api/notes/{page_id}

ページを削除。204 No Content を返す。


POST /api/notes/import

multipart/form-data の files でアップロードされたファイルをページとして取り込み、 201 Created で作成したページの配列を返す。

拡張子 取り込み方
.md / .markdown / .txt / .text 1 ファイル 1 ページ。タイトルはファイル名から拡張子と 01_ 形式の通し番号を落としたもの
.zip 中のテキストファイルを 1 つずつページにする。第 1 階層のフォルダ名がセクションになる(GET /api/notes/export の ZIP をそのまま戻せる)
  • 既存ページとタイトルが衝突したら タイトル (2) のように連番を足して別ページとして作る(上書きはしない)
  • 文字コードは UTF-8 (BOM 可) → CP932 の順に試す
  • 対象外の拡張子・NOTES_IMPORT_MAX_BYTES(既定 2,000,000)超のファイル・NOTES_IMPORT_MAX_PAGES(既定 100)を超えた分・壊れた ZIP は黙って飛ばす
  • 1 ページも取り込めなかった場合のみ 400

GET /api/notes/export

全ページを ZIP でダウンロードさせる(Content-Disposition: attachment)。 1 ページ 1 ファイルの Markdown で、本文はそのまま。エントリ名は <セクション>/<通し番号>_<タイトル>.md (セクション無しのページは ZIP 直下)。通し番号は position 順なのでタブの並びが残る。 ファイル名は notes_YYYYMMDD.zip。


GET /api/notes/{page_id}/export

1 ページを Markdown ファイルとしてダウンロードさせる。本文はそのままで、ファイル名がタイトルになる。 ページが存在しない場合は 404。

日本語タイトルを扱うため Content-Disposition には RFC 5987 形式を併記する:

attachment; filename="______.md"; filename*=UTF-8''%E8%B2%B7%E3%81%84%E7%89%A9%E3%83%AA%E3%82%B9%E3%83%88.md

ファイル名に使えない文字(\ / : * ? " < > | と制御文字)は _ に置き換え、80 文字で切る。


ログ

GET /logs

LLM API 呼び出しログを created_at 降順で返す。

クエリパラメーター

パラメーター 型 説明
session_id string セッションで絞り込み
status ok / error 成功・失敗で絞り込み
limit int (≤500) 最大件数(デフォルト 100)
offset int スキップ件数

レスポンス

[
  {
    "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

{ "status": "ok" }