feat(antigravity): read token usage incl. cached from conversation DB - #211
feat(antigravity): read token usage incl. cached from conversation DB#211eugeneng04 wants to merge 1 commit into
Conversation
The agy harness returned all-zero token counts because it parsed transcript.jsonl, which carries no usage. agy persists a per-turn usage record in the conversation DB (conversations/<uuid>.db, gen_metadata protobuf blob) at wire path .1.4: f2=input, f5=cached, f9=reasoning, f10=output, f3=f9+f10. Decode that as the primary token source (input=f2, cached=f5, reasoning=f9, output=f10, cache_write=None, total=sum), polling for the async flush and guarding with the f3==f9+f10 invariant; fall back to transcript-aggregated counts for old formats, else all-None (never a fabricated 0). metadata.token_source records which source fed the run. Trajectory still comes from the transcript. Field mapping verified empirically on agy 1.1.3 via controlled-output runs and a warm-cache session.
e90a929 to
678b78f
Compare
Implements the unified token accounting design (docs/designs/token-accounting.md): one canonical six-bucket token schema — input (non-cached), cached, cache_write, reasoning, output (excludes reasoning), total — with None for unreported buckets, never a fabricated 0. - agents/result.py: shared TOKEN_BUCKETS + empty_tokens(). - api harness: extract_tokens canonicalizes each provider shape — Anthropic input_tokens passes through (already non-cached) with cache read/write buckets; Gemini subtracts cached_content_token_count (a subset of prompt_token_count) and adds tool_use_prompt tokens; OpenAI subtracts prompt_tokens_details.cached_tokens and splits completion_tokens_details.reasoning_tokens out of output. Total prefers the provider total, else the bucket sum. - gemini CLI harness: the terminal result.stats block maps to the canonical dict (input = full input − cached; reasoning derived from the total gap). - results/normalize.py + ResultRow: rows carry cachedTokens/reasoningTokens (additive nullable fields — no SCHEMA_VERSION bump; legacy provider aliases keep historical results.json readable); dashboard schema.d.ts and the ingest validator accept the new fields absent-tolerantly. - openclaw still passes provider-native usage through (aliases flatten it; cached/reasoning None until canonicalized); antigravity emits the shape via its own decoder (gke-labs#211).
jessie1111101
left a comment
There was a problem hiding this comment.
LGTM. might want to port over to sigs repo
Migration note: ported to kubernetes-sigsThe antigravity Still to port (not carried in #39):
Review findings fixed in the ported version (worth applying here at source too): A 4-agent review panel (correctness / security / perf / Gemini) ran on the ported code; 5 findings were confirmed and fixed in #39. They exist in this PR too:
Gemini's HIGH "protobuf field 0 → infinite loop/OOM" was adversarially refuted (the varint reader always advances; work is O(n) and the blob is agy's own local DB, not attacker input). |
Problem
The
agy(Antigravity CLI) harness reported{"input":0,"output":0,"total":0,"cached":0}for every run: it parsedtranscript.jsonlfor a per-recordtokensfield that real transcripts never carry (the shape existed only in test fixtures), so every bucket silently defaulted to 0.Fix
Token usage is persisted in the conversation DB (
conversations/<uuid>.db,gen_metadataprotobuf blobs) as a per-turn usage record at wire path.1.4:f2=input (non-cached),f5=cached,f9=reasoning,f10=output,f3=f9+f10 (integrity guard).{input, cached, cache_write: None, reasoning, output, total}, summed per turn; poll for agy's async DB flush.f3 == f9 + f10invariant rejects non-matching records; undecodable rows (schema drift) stop the poll early; anything unrecoverable yields all-None, never a fabricated 0.metadata.token_sourcerecords which source fed the run (db/transcript/unavailable).Verification
Notes / known limitations
docs/appendix/known_issues.mdwith a removal condition (drop the decoder once agy exposes usage headlessly) and re-verify-on-upgrade guidance.gen_metadata; totals cover the main trajectory (~0.2% low observed).normalize.py/ResultRow) does not yet carrycached/reasoning— deferred to the unified token-accounting work, along with moving the six-bucket schema to a shared module.antigravityrow to the supported-harnesses table and a Token accounting section covering the gemini/antigravity cached-input asymmetry.