Documentation
¶
Overview ¶
Package trace records what a turn actually did.
Factor already knows all of this and keeps none of it. Phase changes go to one live watcher for the status line and are dropped; the registry runs every tool and remembers only the ones that panicked; the meter sees every model call and stores two integers. So "it answered as the wrong person", "it went quiet for ninety seconds" and "why did that turn cost so much" are reconstructed from memory rather than read.
A trace is the trajectory: what came in, which models answered, which tools ran and for how long, what they cost, and how the turn ended. It is the record three things need — a person debugging, the control bands watching for drift, and any eval built from real work rather than imagination.
It stays deliberately small. This is a single-user agent on the user's own machine, so the trace is a local file with a retention limit, it never leaves the box, and it records what a tool was called rather than what was said to it unless asked. Tool arguments hold the user's file paths, their searches and the things they asked to be remembered; the questions this record exists to answer need the shape of a turn, not its contents.
Index ¶
Constants ¶
const ( EventFailover = "failover" EventCompaction = "compaction" EventSteering = "steering" EventBudget = "budget" EventOverflow = "overflow" EventBargeIn = "barge_in" EventForget = "forget" EventAsk = "ask" )
Event kinds. Named rather than free text because the control bands count them and a typo would read as a metric that never fires.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
Enabled bool
// RecordArgs adds argument values to each tool call. Off by default: the
// shape of a turn answers the questions traces exist for, and the values
// are the user's paths, searches and secrets-adjacent text.
RecordArgs bool
KeepDays int
}
Config is what the user gets to decide.
type Event ¶
type Event struct {
At float64 `json:"at_s"`
Kind string `json:"kind"`
Detail string `json:"detail,omitempty"`
}
Event is something that happened to the turn rather than in it: a provider failover, a compaction, a message steered in mid-flight, a budget refusal, a user correcting the answer.
type ModelCall ¶
type ModelCall struct {
Model string `json:"model"`
Input int `json:"input"`
Cached int `json:"cached,omitempty"`
Written int `json:"cache_write,omitempty"`
Output int `json:"output"`
USD float64 `json:"usd,omitempty"`
Duration float64 `json:"duration_s,omitempty"`
}
ModelCall is one provider round trip.
type Record ¶
type Record struct {
ID string `json:"id"`
Started time.Time `json:"started"`
Session string `json:"session"`
Channel string `json:"channel"`
Trigger string `json:"trigger,omitempty"` // user | cron | job | heartbeat
Speaker string `json:"speaker,omitempty"`
Duration float64 `json:"duration_s"`
Models []ModelCall `json:"models,omitempty"`
Tools []ToolCall `json:"tools,omitempty"`
Events []Event `json:"events,omitempty"`
// Outcome is how the turn ended: "ok", "error", "interrupted", "budget".
Outcome string `json:"outcome"`
Error string `json:"error,omitempty"`
InputTokens int `json:"input_tokens,omitempty"`
CachedTokens int `json:"cached_input_tokens,omitempty"`
OutputTokens int `json:"output_tokens,omitempty"`
USD float64 `json:"usd,omitempty"`
}
Record is one turn, written as a single JSON line when the turn ends.
func Since ¶
Since reads the records written on or after t, oldest first.
The reader is deliberately forgiving: a half-written final line is what a crash mid-write leaves behind, and a watcher that refused to read anything because of it would go blind exactly when something has gone wrong.
func (Record) CacheHitRate ¶
CacheHitRate is the share of this turn's input that was served from cache.
func (Record) Failed ¶
Failed reports whether the turn ended in something that needs looking at. An interruption does not: the user talked over the answer, which is the system working.
func (Record) ToolErrors ¶
ToolErrors counts the tool calls that came back as failures.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder writes turn records and prunes old ones. Every method is safe on a nil receiver, so the loop calls them unconditionally and a disabled tracer costs a nil check.
func NewRecorder ¶
NewRecorder opens the trace directory, or returns nil when tracing is off. filter is the same secret scrubber the tool registry uses; nil means none.
func (*Recorder) Charge ¶
Charge attributes one priced model call to the turn running on a session. Calls with no turn open — an idle compaction, an induction verdict — are written as records of their own, because spend nobody asked for is exactly the spend worth being able to find.
func (*Recorder) Event ¶
Event records something against whatever turn is running on a session, for callers that know the session but not the turn — the provider chain failing over, a channel reporting that the user talked over the answer. Nothing is recorded when no turn is open: an event with no turn to belong to is noise.
type ToolCall ¶
type ToolCall struct {
Name string `json:"name"`
Duration float64 `json:"duration_s"`
Bytes int `json:"bytes"`
Error bool `json:"error,omitempty"`
ArgKeys []string `json:"arg_keys,omitempty"`
// Args is present only when trace.record_args is on. It holds the
// arguments after the same secret filter tool results pass through,
// bounded, because a browser call can carry a page of them.
Args string `json:"args,omitempty"`
}
ToolCall is one tool execution: what ran, for how long, how much it returned, and whether it failed.