geminishape

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package geminishape parses the Gemini CLI telemetry file shape shared by the gemini and agy adapters, so a parser bug is fixed exactly once. Records for a single turn are CUMULATIVE: each new record for a given id re-emits the turn's growing running totals, so the last record for an id (equivalently, the one with the largest total) holds the final figures. ReadFile therefore groups records by id within a file, keeps the max snapshot per id, and emits one AggregateSnapshot per (file, id).

Token mapping (per plan section 1):

Input         = (tokens.input + tokens.tool) - cached overlap   (clamped >= 0)
Output        = tokens.output
CacheRead     = tokens.cached
CacheCreation = 0
Reasoning     = tokens.thoughts
Total         = max(tokens.total, input+tool+output+thoughts)

The total accounts for every component this shape folds into the event's own token fields, so a row can never report a total below the counts stored beside it (issue #49). tokens.tool is part of it because it is folded into InputTokens; tokens.thoughts is part of it because reasoning is ADDITIVE for this shape (model.ReasoningModeFor). Cached is NOT added on top: it is the slice of tokens.input that was served from cache, already inside that count, and it is reported separately in CacheReadTokens.

tokens.total is the provider's own figure and stays authoritative wherever it covers those components; it is only raised when the provider itemised more than it totalled (real records do: a turn reporting input+output+thoughts as its total leaves its tool tokens out of it).

The adapters keep their own roots/Discover policy and their size+mtime checkpoint gates; only the file parsing lives here.

CRITICAL: strictly read-only. Files are opened O_RDONLY and never written, locked, or modified.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HasUsageExt

func HasUsageExt(path string) bool

HasUsageExt reports whether a path ends in .json or .jsonl — the extensions the Discover walks of both adapters accept.

Types

type FileResult

type FileResult struct {
	Snapshots []model.AggregateSnapshot
	// Skipped counts genuinely unparseable lines/entries (permanently bad, a
	// re-read cannot fix them).
	Skipped int
	// ScanErr is non-nil when the JSONL scanner aborted mid-file (e.g. an
	// over-long line): the snapshots above are best-effort and the caller MUST
	// NOT advance its checkpoint — the unread remainder would be skipped until
	// the next size/mtime change.
	ScanErr error
}

FileResult is the outcome of parsing one telemetry file.

type Shape

type Shape struct {
	Tool     string // tool id stamped on every snapshot (model.ToolGemini, ...)
	Provider string // billing identity stamped on every snapshot (model.ProviderGoogle)
	Project  string // project label (the telemetry records no cwd)
}

Shape parameterises the shared parser for one adapter.

func (Shape) ReadFile

func (s Shape) ReadFile(path string, now time.Time) (FileResult, error)

ReadFile parses path and returns one AggregateSnapshot per (file, id), keeping the max (final) cumulative snapshot per id. now supplies ObservedTime for records without a parseable timestamp. The error return is fatal only (the file could not be opened or read at all).

Jump to

Keyboard shortcuts

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