Antigravity Model Canonicalization and Cost Fallback#
Context: "Antigravity" refers to an AI provider whose sessions can be ingested by Decant. The notes below document the correct rules for identifying the model, mapping it to a tariff key, and handling costs — and why certain shortcuts are wrong.
Model ID vs. Display Name#
Antigravity session records expose two model fields on the status line: model.id (a machine-readable slug) and model.display_name (a human-readable label such as Gemini 3.7 Flash (High)). Always prefer model.id when present; it is a slug that Decant's canonicalModel() function can resolve directly to a tariff key.
When model.id is absent, the capture falls back to persisting model.display_name verbatim. Display names like Gemini 3.7 Flash (High) are not valid slugs: canonicalModel() lowercases its input and uses substring matching on version tokens — the (High) suffix is an unrecognized decorator that is simply ignored during matching .
Tariff Key Mapping for Display Names#
Even though display names are not canonical slugs, canonicalModel() still resolves them correctly by version-number substring detection:
Gemini 3.7 Flash (High)→ lowercased togemini 3.7 flash (high)→ matches"3.7"→ returns"gemini-3.7-flash"
The (High) effort suffix does not change token rates; it controls inference quality but is billed at the same per-token price. The correct tariff key for any 3.7 Flash variant (regardless of effort level) is gemini-3.7-flash, currently priced at $0.75/M input, $0.075/M cache read, $3.75/M output (promotional rate through 2026-12-31).
If canonicalModel() cannot resolve a display name — e.g., for non-text Gemini variants (image, TTS, live, transcribe, native-audio, computer-use) — it returns null and cost is recorded as zero . No guessed prices are substituted.
Cost: Provider-Reported vs. Token-Derived Estimates#
Decant's ingest pipeline computes estimated_cost_usd using estimateCost(), which derives cost from per-request token counts and the local pricing table . For most providers this works correctly. Every sync also recalculates stored session costs from token usage for all sessions: costs update when rates or components differ from current pricing, even for archived sessions whose source files are unavailable. This refresh applies to Antigravity sessions as well as other providers.
For Antigravity sessions, do not replace the provider-reported cost.estimated with a token-derived estimate during initial ingest. There are three compounding reasons:
-
Cumulative totals, not per-request. Antigravity status-line token fields are running session totals, not per-request deltas. Applying per-request rates to cumulative figures produces inflated costs.
-
No cache-read splits. Cumulative totals do not preserve cache-read breakdowns across requests within the session. Decant's
estimateCostParts()prices cache-read tokens at a separate (lower) rate ; without accurate cache splits, the computed estimate overstates cost. (A related issue — token totals lacking cache splits — is documented in issue #142.) -
Mixed-model and tiered-rate boundaries. Gemini Pro uses a tiered rate that switches above the 200 k-token prompt threshold . If a session crosses that boundary mid-session, cumulative token counts do not encode which tier applied to which tokens, making any single-rate estimate incorrect.
Because the provider supplies an authoritative cost.estimated that already accounts for these factors, that value is captured at ingest. However, stored costs may be refreshed during subsequent syncs if pricing tables or components change. The same fallback logic applies: token-derived recalculation still produces approximations for Antigravity due to the cumulative-total and cache-split issues above.
Reference Pointers#
| Resource | What to find there |
|---|---|
src/cost.ts — canonicalModel() | Full Gemini slug normalization logic |
src/cost.ts — defaultPricing() | Gemini tariff map (all canonical keys and rates) |
src/cost.ts — estimateCostParts() | How cache-read and cache-creation costs are split |
src/ingest.ts line 815 | Where estimateCost() is called at ingest |
| Issue #142 | Cumulative token totals inflating costs (cache-split problem) |