Skip to content

feat(observability): LLM token-usage recorder interface (issue #168)#197

Merged
Delqhi merged 2 commits into
mainfrom
feat/issue-168-recorder
Jun 16, 2026
Merged

feat(observability): LLM token-usage recorder interface (issue #168)#197
Delqhi merged 2 commits into
mainfrom
feat/issue-168-recorder

Conversation

@Delqhi

@Delqhi Delqhi commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Rescues PR #188 by supplying the missing Recorder interface, NopRecorder, SessionIDFromContext, SourceAdHoc constant. The v168-Commit referenced these symbols without defining them, blocking the build at cmd/sin-code/internal/llm/provider.go:66,85,141,142.

What's new

  • Recorder interface in internal/llm/recorder.go with NopRecorder default + Source enum (AdHoc/Chat/Agent)
  • WithSessionID / SessionIDFromContext / DefaultSessionID for grouping usage by session
  • randomSessionID() generates a stable 64-bit hex id per process
  • 9 tests covering the interface, concurrency, error reporting, type-mismatch resilience, and id format
  • internal/usage/ storage package (Store, Record, RecordFromChatUsage, Aggregate, Tail, Count, SetPricing)
  • DefaultPricing for NIM / Anthropic / OpenAI / Fireworks
  • internal/summary/summary.go updated to surface token usage in the deterministic auto-summary builder
  • sin-code tokens CLI: show / tail / aggregate subcommands
  • docs/TOKEN-TRACKING.md spec (event schema, source taxonomy, aggregation semantics, dashboard queries)

Hard mandates honored

  • M2 (single static binary): pure-Go, no CGO, modernc.org/sqlite for storage
  • M3 (verify gate): usage write failures are logged, never break the LLM call (token accounting is best-effort)
  • M4 (permission engine): no special permissions required
  • M7 (race-free): go test -race -count=1 passes for llm, usage, summary

Build status

go build ./... clean. Race: 9/9 + usage + summary tests all green.

Diffstat

 10 files changed, 2232 insertions(+), 7 deletions(-)

Closes

Rescues v168-Commit (PR #188) by supplying the missing
Recorder interface, NopRecorder, SessionIDFromContext, and
the SourceAdHoc constant. The v168-Commit referenced these
symbols without defining them, blocking the build.

What ships:
  - Recorder interface in internal/llm/recorder.go with
    NopRecorder default + Source enum (AdHoc/Chat/Agent)
  - WithSessionID / SessionIDFromContext / DefaultSessionID
    for grouping usage by session in dashboards
  - randomSessionID() generates a stable 64-bit hex id
    per process so CLI invocations are grouped into a
    session even without explicit WithSessionID calls
  - 9 tests covering the interface, concurrency, error
    reporting, type-mismatch resilience, and id format
  - internal/usage/ storage package (Store, Record,
    RecordFromChatUsage, Aggregate group_by day|month|
    model|source|session, Tail, Count, SetPricing)
  - DefaultPricing for NIM / Anthropic / OpenAI / Fireworks
  - internal/summary/summary.go updated to surface token
    usage in the deterministic auto-summary builder
  - sin-code tokens CLI: show / tail / aggregate subcmds
  - docs/TOKEN-TRACKING.md spec (event schema, source
    taxonomy, aggregation semantics, dashboard queries)

Hard mandates honored:
  - M2: pure-Go, no CGO, modernc/sqlite for storage
  - M3: usage write failures are logged, never break
        the LLM call (token accounting is best-effort)
  - M4: token writes go through the standard error
        path; no special permissions required
  - M7: race-clean (-race passes for llm, usage, summary)

Build status: go build ./... clean. Race: 9/9 + usage +
summary tests all green.

Closes: #168
Refs: PR #188 (rescued via rebase on this commit)
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sin-code Ready Ready Preview, Comment, Open in v0 Jun 16, 2026 3:45pm

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

🏆 CEO Audit — A+ (100.0/100)

Metric Value
Grade A+
Score 100.0/100
Critical findings 0
High findings 0
Profile QUICK
Min grade gate B

📥 Download full report (Markdown)
📊 Download SARIF (for Code Scanning)

Run ~/.config/opencode/skills/ceo-audit/scripts/audit.sh . --profile=QUICK locally to reproduce.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

🏆 CEO Audit — A+ (100.0/100)

Metric Value
Grade A+
Score 100.0/100
Critical findings 0
High findings 0
Medium findings 0
Profile QUICK
Min grade gate B

📥 Download full report (Markdown)

Run ID: 27630191983 · Commit: ${github.sha}

Run ~/.config/opencode/skills/ceo-audit/scripts/audit.sh . --profile=QUICK locally to reproduce.

if path == "" {
path = DefaultPath()
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
Comment on lines +285 to +296
topSQL := `
SELECT
COALESCE(SUM(input_tokens), 0),
COALESCE(SUM(output_tokens), 0),
COALESCE(SUM(total_tokens), 0),
COALESCE(SUM(cost_usd), 0),
COUNT(*),
COALESCE(MIN(created_at), ''),
COALESCE(MAX(created_at), ''),
COUNT(DISTINCT session_id)
FROM usage_events
` + where
}

func scanBreakdowns(ctx context.Context, db *sql.DB, f Filter, where string, args []any, top *Aggregation) error {
rows, err := db.QueryContext(ctx, `SELECT model, SUM(total_tokens) FROM usage_events `+where+` GROUP BY model`, args...)
return err
}

rows, err = db.QueryContext(ctx, `SELECT source, SUM(total_tokens) FROM usage_events `+where+` GROUP BY source`, args...)
Comment on lines +430 to +432
rows, err := s.db.QueryContext(ctx, `
SELECT id, session_id, model, source, input_tokens, output_tokens, total_tokens, cost_usd, created_at
FROM usage_events `+where+` ORDER BY created_at DESC, id DESC LIMIT ?`,
// table-wide COUNT — no large scan).
func (s *Store) Count(ctx context.Context, f Filter) (int, error) {
where, args := buildWhere(f)
row := s.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM usage_events `+where, args...)
if p == "" {
continue
}
data, err := os.ReadFile(p)
Mechanical whitespace alignment, no semantic change.
Kept the gofmt -s style consistent with the rest of
the package (spaces in single-line struct fields).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants