# US-025 — Model usage statistics: rolling RPM windows + SQLite persistence Status: done Priority: Medium Stage: Implemented ## Context Trackers need per-model request-rate data to drive smart shard assignment (US-026) and to give operators visibility into what the network is actually serving. Stats must survive tracker restarts and should be shareable across a tracker cluster. ## Design - `_RollingCounter`: circular-bucket counter, epoch-indexed, stale buckets auto-reset - Three windows: 60×1-min buckets (last hour), 24×1-hr (last day), 30×1-day (last month) - `_StatsCollector`: `record_request()`, `get_local_rpms()`, `merge_peer_rpms()`, `get_combined_stats()` - SQLite persistence via `--stats-db PATH` - Gossip: each tracker keeps its own slice; merge is additive (not averaged) ## Acceptance criteria - [ ] `_RollingCounter` passes unit tests (record, rpm, stale bucket reset) - [ ] `_StatsCollector` accumulates and merges peer slices additively - [ ] SQLite round-trip: buckets saved and restored across restart - [ ] `GET /v1/stats` returns combined stats JSON - [ ] `POST /v1/stats/gossip` accepts peer slice and merges - [ ] `_handle_proxy_chat` calls `stats.record_request()` after model is resolved - [ ] `--stats-db PATH` CLI flag added to tracker - [ ] 6 unit tests pass; `python -m pytest` passes