telemetry

package
v0.158.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 43 Imported by: 0

Documentation

Overview

Package telemetry records and exports the CLI's OpenTelemetry metrics and traces.

Metrics: tool outcomes, token usage, and sessions, recorded into OTel SDK instruments named per the GenAI semantic conventions and infer-action's exporter, so they line up with the gateway's OTLP ingest and existing dashboards.

Traces: one root span per session, child spans for each LLM turn and each tool call. No prompt/response content is recorded.

Both signals share the same resource and OTLP endpoint/headers config. Local file export is always attempted; OTLP/HTTP export is opt-in via an endpoint (config or OTEL_EXPORTER_OTLP_ENDPOINT). Metrics use delta temporality (required by the gateway ingest, and what makes the local files trivially summable by `infer stats`).

Index

Constants

View Source
const (
	ExecInteractive = "interactive"
	ExecHeadless    = "headless"
	ExecDaemon      = "daemon"
)

Execution modes (resource attribute infer.execution.mode).

View Source
const (
	ToolSuccess  = "success"
	ToolError    = "error"
	ToolRejected = "rejected"

	ErrTypeTool = "tool_error"
)

Tool outcomes (attribute infer.tool.outcome; error.type on non-success).

View Source
const (
	RunSuccess      = "success"
	RunFailed       = "failed"
	RunStoppedEarly = "stopped_early"
)

Session/run outcomes (attribute infer.run.outcome) - infer-action's enum.

Variables

View Source
var (
	Version       = "dev"
	ExecutionMode = ExecHeadless
)

Process-wide facts stamped onto every metric via the resource. Version is the build version; ExecutionMode distinguishes interactive chat from headless `infer agent`. cmd sets these before building the service container.

Functions

func Archive

func Archive(dir string, cutoff time.Time)

Archive moves session files older than cutoff into an archive/ subdir instead of deleting them; Aggregate's non-recursive glob then skips them. Best-effort.

func NewToolService

func NewToolService(inner domain.ToolService, rec *Recorder) domain.ToolService

NewToolService wraps inner so tool executions are recorded. The container only applies this when rec is non-nil, so the disabled tool path carries no decorator at all.

func ParseSince added in v0.146.0

func ParseSince(s string) (time.Time, error)

ParseSince converts a window like "7d"/"24h"/"30m" to an absolute cutoff. Empty means all time (zero cutoff). time.ParseDuration has no day unit, so "Nd" is handled here.

func PropagationTransport added in v0.147.0

func PropagationTransport(base http.RoundTripper) http.RoundTripper

PropagationTransport returns an http.RoundTripper that injects W3C trace-context and baggage headers into every outgoing request. When base is nil, http.DefaultTransport is used.

func RenderTraceTree added in v0.147.0

func RenderTraceTree(roots []*TraceSpan, style TreeStyle) string

RenderTraceTree renders the span tree via lipgloss/v2/tree with durations right-aligned in a column:

session (standard, success)          42.1s
├── chat deepseek/deepseek-v4-flash   3.2s
╰── execute_tool Bash                27.5s

Failed spans carry a trailing [error: <type>] marker.

func SetSpanError added in v0.147.0

func SetSpanError(ctx context.Context, err error)

SetSpanError marks the span in ctx failed: error.type attribute, recorded error event, and Error status, per the semconv recording-errors rules.

func SetSpanUsage added in v0.147.0

func SetSpanUsage(ctx context.Context, inputTokens, outputTokens int)

SetSpanUsage stamps token usage (gen_ai.usage.*) onto the span in ctx.

Types

type CostFunc

type CostFunc func(model string, prompt, completion, cached, cacheWrite int) (input, output, total float64)

CostFunc returns the input, output, and total cost for a model's token counts (wraps domain.PricingService.CalculateCost). cached and cacheWrite are the cache-read and cache-creation subsets of prompt tokens. Pass nil to skip cost.

type ModelStat

type ModelStat struct {
	Model      string  `json:"model"`
	Prompt     int     `json:"prompt"`
	Cached     int     `json:"cached"`
	Completion int     `json:"completion"`
	Total      int     `json:"total"`
	Cost       float64 `json:"cost"`
}

ModelStat aggregates token usage and cost for one model. Cached is the cached-prompt subset of Prompt (not added to Total); it stays 0 for telemetry files written before cache_read datapoints existed.

type Options

type Options struct {
	Enabled         bool
	Dir             string
	SessionID       string
	OTLPEndpoint    string
	OTLPHeaders     map[string]string
	OTLPInterval    time.Duration
	ReceiverAddress string
	Cost            CostFunc
	// AttrSessionIDKey / AttrToolCallIDKey override the baggage member names;
	// empty falls back to the OTel semconv defaults.
	AttrSessionIDKey  string
	AttrToolCallIDKey string
}

Options configures a Recorder. Dir + SessionID locate the per-process local file; OTLP* enable the optional remote export.

type Recorder

type Recorder struct {
	// contains filtered or unexported fields
}

Recorder maps recorded events onto OTel instruments and spans. A nil *Recorder is a valid no-op, so callers guard hot paths with `if rec != nil` and the container skips wrapping when disabled.

func New

func New(opts Options) *Recorder

New builds a Recorder, or nil when disabled or no sink could be created. The local file sink is always attempted; the OTLP sink is added when an endpoint is configured (or OTEL_EXPORTER_OTLP_ENDPOINT is set). Sink failures are logged and dropped so telemetry never breaks a run.

func (*Recorder) ChildEnv added in v0.147.0

func (r *Recorder) ChildEnv(ctx context.Context) []string

ChildEnv returns the W3C trace-context environment for a subprocess launched under the current tool span, plus its OTLP span sink: a configured endpoint passes through, an env-configured one is inherited, otherwise the ephemeral loopback receiver keeps child spans in the local session store. Nil when the recorder is nil or no span is active.

func (*Recorder) Flush

func (r *Recorder) Flush(ctx context.Context)

Flush forces an immediate export of everything recorded so far, without tearing down (used by tests and callers that want data on disk now). Safe on nil.

func (*Recorder) Meter added in v0.148.0

func (r *Recorder) Meter() metric.Meter

Meter returns the meter from the provider, or nil when the recorder is nil or the provider is nil. Used by subsystems (e.g. channels-manager) to register their own instruments on the shared meter provider.

func (*Recorder) RecordSession

func (r *Recorder) RecordSession(mode, outcome string, dur time.Duration)

RecordSession records one completed agent session (infer.agent.runs + infer.agent.run.duration). outcome is one of RunSuccess/RunFailed/RunStoppedEarly.

func (*Recorder) RecordTool

func (r *Recorder) RecordTool(tool, outcome, errType string, dur time.Duration)

RecordTool records one tool execution (infer.agent.tool.calls + gen_ai.execute_tool.duration).

func (*Recorder) RecordUsage

func (r *Recorder) RecordUsage(model string, prompt, completion, cached, cacheWrite int)

RecordUsage records one request's token usage (gen_ai.client.token.usage, one datapoint per token type) and the derived infer.client.cost split. cached and cacheWrite are the cache-read and cache-creation subsets of prompt tokens; their datapoints are only emitted when non-zero.

func (*Recorder) SetConversationID added in v0.149.0

func (r *Recorder) SetConversationID(id string)

SetConversationID tags subsequent metric datapoints with the given conversation id (gen_ai.conversation.id) so aggregation can scope to it. No-op on a nil recorder or empty id.

func (*Recorder) Shutdown

func (r *Recorder) Shutdown(ctx context.Context)

Shutdown flushes the final export and releases resources for both OTel signals. Safe on nil.

func (*Recorder) SpanContext added in v0.147.0

func (r *Recorder) SpanContext(ctx context.Context) context.Context

SpanContext grafts the session root span onto ctx so spans created from the returned context parent to it. Safe on nil.

func (*Recorder) StartLLMTurnSpan added in v0.147.0

func (r *Recorder) StartLLMTurnSpan(ctx context.Context, model string) (context.Context, trace.Span)

StartLLMTurnSpan creates a span for one LLM request with GenAI semconv attributes and CLIENT kind (a remote call to the gateway). Safe on nil (returns ctx unchanged and a no-op span).

func (*Recorder) StartSession added in v0.147.0

func (r *Recorder) StartSession(mode string) func(outcome string)

StartSession begins the session root span. The returned end function stamps infer.run.outcome and ends the span. Safe on nil.

func (*Recorder) Tracer added in v0.147.0

func (r *Recorder) Tracer() trace.Tracer

Tracer returns the tracer for the CLI's instrumentation scope. Safe on nil (returns a no-op tracer).

type SessionStat

type SessionStat struct {
	Execution string `json:"execution"`
	Mode      string `json:"mode"`
	Count     int    `json:"count"`
}

SessionStat counts sessions by execution mode (interactive/headless) and agent mode.

type Stats

type Stats struct {
	Tools    []ToolStat    `json:"tools"`
	Models   []ModelStat   `json:"models"`
	Sessions []SessionStat `json:"sessions"`
	Empty    bool          `json:"-"`
}

Stats is the `infer stats` aggregate over the local telemetry files. Empty is true when no datapoints matched (clean empty-store render).

func Aggregate

func Aggregate(dir string, since time.Time, conversationID string) (Stats, error)

Aggregate reads the per-session OTLP/stdout files under dir and folds their delta datapoints (timestamped on/after since) into Stats. The JSON shape is the SDK stdout exporter's ResourceMetrics; delta temporality means summing every datapoint across every file yields the totals.

type ToolStat

type ToolStat struct {
	Name     string `json:"name"`
	Calls    int    `json:"calls"`
	Failures int    `json:"failures"`
	AvgMs    int64  `json:"avg_ms"`
}

ToolStat aggregates one tool. Failures counts the error outcome (a rejection is not a failure). AvgMs is the mean execution duration.

type TraceSession added in v0.147.0

type TraceSession struct {
	ID       string    `json:"id"`
	Modified time.Time `json:"modified"`
}

TraceSession identifies one session with a non-empty local trace file (<dir>/<id>-traces.jsonl).

func TraceSessions added in v0.147.0

func TraceSessions(dir string) ([]TraceSession, error)

TraceSessions lists sessions with non-empty trace files under dir, newest first. Empty files (sessions that recorded no spans) are skipped.

type TraceSpan added in v0.147.0

type TraceSpan struct {
	Name       string            `json:"name"`
	Start      time.Time         `json:"start"`
	DurationMs float64           `json:"duration_ms"`
	Error      string            `json:"error,omitempty"`
	Attributes map[string]string `json:"attributes,omitempty"`
	Children   []*TraceSpan      `json:"children,omitempty"`
}

TraceSpan is one node of a session's span tree.

func LoadTraceTree added in v0.147.0

func LoadTraceTree(dir, session string) ([]*TraceSpan, error)

LoadTraceTree reads a session's trace file and assembles the span tree. Spans whose parent is not in the file (the root, or orphans from a partial file) become roots; siblings are ordered by start time. Malformed lines are skipped best-effort.

type TreeStyle added in v0.147.0

type TreeStyle struct {
	Enumerator lipgloss.Style // tree connector glyphs
	Duration   lipgloss.Style
	Error      lipgloss.Style // the [error: ...] marker
}

TreeStyle colorizes the rendered tree segments. The zero value renders plain text, which suits markdown/code-fence output.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL