2.8 KiB
2.8 KiB
usage-history レビュー
要件の充足
チケットが定義した要件は全て達成されている:
- LogEntry::LlmUsage: session-store のハッシュチェーンに乗る variant として追加。
history_len/input_total_tokens/cache_read_tokens/cache_write_tokens/output_tokensの5フィールド - RestoredState.usage_history:
collect_stateの replay でVec<UsageRecord>に時系列順で積まれる。history の構築には影響しない - save_usage:
append_entry経由でハッシュチェーンに接続 - 既存ログ互換:
LlmUsageentry が無い既存ログを読んでもusage_historyが空になるだけで壊れない - 1リクエスト = 1 entry: Timeline の
pending_usage+flush_usage()で複数 Usage event を集約し、handler には1度だけ発火
アーキテクチャ
レイヤー分担が明確で、各層の責務が逸脱していない:
| レイヤー | 責務 |
|---|---|
| scheme (anthropic) | raw → 占有量への正規化。input_tokens + cache_read + cache_creation |
| Timeline | 1リクエスト内の複数 Usage event をフィールド単位 latest-non-None でマージ。flush_usage() で1度だけ発火 |
| Worker | ストリーム完了・エラー・キャンセルの全パスで flush_usage() を呼ぶ |
| UsageTracker (pod) | note_request(history_len) と record_usage(event) のペアリング。drain で Pod に渡す |
| Pod::persist_turn | drain した records を save_usage で session-store に書き出し |
指摘と対処
1. UsageEvent の doc comment(対処済み)
UsageEvent.input_tokens が「占有量(プロンプト全長、キャッシュ込み)」を意味することが
struct と各フィールドの doc comment に明記された。scheme 層での正規化規約も記載済み。
2. save_usage の引数が多い(非ブロッカー、未対処)
8引数。UsageRecord を直接受け取れば drain() の結果をそのまま渡せてシグネチャがきれいになるが、
他の save_* 関数がフラットな引数を取るパターンと一貫しているため、統一性の観点では現状でも妥当。
将来フィールドが増えた時点でまとめて UsageRecord 受け取りに変えればよい。
テスト
replay_llm_usage_appends_to_usage_history: 複数 LlmUsage entry の replay で usage_history が正しく積まれ、history.len() に影響しないreplay_without_llm_usage_keeps_usage_history_empty: 既存ログ互換llm_usage_entry_round_trip_via_json: serde 往復test_convert_usage_includes_cache_in_input_total: Anthropic の占有量正規化test_usage_aggregation_and_flush: Timeline の集約 + flush- UsageTracker: ペアリング、drain、未ペアの drop、複数リクエスト
判定
承認。