Documentation
¶
Overview ¶
Package llmstats provides a durable sink for per-call LLM metrics. It implements llm.StatsSink by appending one JSON record per call to a local JSONL file (.sdd/stats/llm.jsonl), so prompt-cache effectiveness and token usage become measurable over time without an external service (d-tac-zis).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type FileSink ¶
type FileSink struct {
// contains filtered or unexported fields
}
FileSink appends one JSON record per LLM call to a JSONL file. Safe for concurrent use — batch operations record from multiple goroutines.
func NewFileSink ¶
NewFileSink returns a sink that appends to <dir>/llm.jsonl, creating dir if it does not exist.
func (*FileSink) RecordCall ¶
RecordCall appends one JSON line for the call. Errors are swallowed: stats collection is best-effort and must never break a capture or summarize. The on-disk shape is Record, shared with the reader (reader.go).
type Reader ¶ added in v0.9.0
type Reader struct {
// contains filtered or unexported fields
}
Reader reads the per-call stats sink written by FileSink.
func NewReader ¶ added in v0.9.0
NewReader returns a reader over <dir>/llm.jsonl — the same path FileSink writes, so the dir argument matches what NewFileSink was given.
func (*Reader) Path ¶ added in v0.9.0
Path returns the sink file path, for display in the stats header.
func (*Reader) Read ¶ added in v0.9.0
func (r *Reader) Read() ([]model.StatsRecord, error)
Read returns every parseable record in the sink, in file order. An absent sink yields an empty slice and no error — that is the "no stats recorded yet" case, not a failure. Malformed lines are skipped rather than aborting the read: stats collection is best-effort, so one bad line must not blank the whole report.
type Record ¶ added in v0.9.0
type Record struct {
Timestamp string `json:"ts"`
Op string `json:"op"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
Items int `json:"items,omitempty"`
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
CacheReadTokens int `json:"cache_read_tokens"`
CacheCreateTokens int `json:"cache_create_tokens"`
DurationMS int64 `json:"duration_ms"`
}
Record is the on-disk JSONL shape, shared by the writer (FileSink) and the Reader so the wire format has a single definition. Cost is intentionally absent: the active provider path reports tokens and the cache breakdown but no dollar cost, and we keep no pricing table (d-tac-zis).