// Package stats defines the types and helpers for OpenAI-compatible token // usage statistics recorded by the zhanlu proxy. The concrete SQLite-backed // recorder lives in internal/store; this package is dependency-free so it can // be referenced by both store and server without import cycles. package stats import "time" // Record is a single chat-completion usage observation. type Record struct { Ts time.Time `json:"ts"` Model string `json:"model"` Stream bool `json:"stream"` PromptTokens int `json:"prompt_tokens"` CompletionTokens int `json:"completion_tokens"` TotalTokens int `json:"total_tokens"` ReasoningTokens int `json:"reasoning_tokens"` CachedTokens int `json:"cached_tokens"` Status string `json:"status"` // "success" | "upstream_error" LatencyMs int64 `json:"latency_ms"` } // Query filters the recorded stats. Zero-value time fields mean unbounded on // that end; empty Model means all models. Limit caps the recent-records list // (0 = default). Stream filters by streaming mode (nil = both). type Query struct { Since time.Time Until time.Time Model string Limit int Stream *bool } // Totals aggregates request counts and token sums over a filtered set. type Totals struct { Requests int `json:"requests"` SuccessRequests int `json:"success_requests"` ErrorRequests int `json:"error_requests"` PromptTokens int64 `json:"prompt_tokens"` CompletionTokens int64 `json:"completion_tokens"` TotalTokens int64 `json:"total_tokens"` ReasoningTokens int64 `json:"reasoning_tokens"` CachedTokens int64 `json:"cached_tokens"` CacheRate float64 `json:"cache_rate"` // cached_tokens / prompt_tokens, 0..1 } // ModelStat is a per-model aggregation row. type ModelStat struct { Model string `json:"model"` Requests int `json:"requests"` PromptTokens int64 `json:"prompt_tokens"` CompletionTokens int64 `json:"completion_tokens"` TotalTokens int64 `json:"total_tokens"` CachedTokens int64 `json:"cached_tokens"` } // DayStat is a per-day aggregation row (server-local time, YYYY-MM-DD). type DayStat struct { Day string `json:"day"` Requests int `json:"requests"` PromptTokens int64 `json:"prompt_tokens"` CompletionTokens int64 `json:"completion_tokens"` TotalTokens int64 `json:"total_tokens"` CachedTokens int64 `json:"cached_tokens"` } // Summary is the full result returned by a Recorder's Stats query. type Summary struct { Totals Totals `json:"totals"` PerModel []ModelStat `json:"per_model"` Daily []DayStat `json:"daily"` Recent []Record `json:"recent"` } // Recorder persists and queries usage statistics. The concrete implementation // lives in internal/store; the interface is declared here so server code can // depend on the contract and tests can inject fakes. type Recorder interface { Record(r Record) error Stats(q Query) (*Summary, error) Reset() error Close() error }