DocumentsDosu Decant
Ingest-Time Cost Semantics
Ingest-Time Cost Semantics
Type
Topic
Status
Published
Created
Aug 3, 2026
Updated
Aug 3, 2026

Ingest-Time Cost Semantics#

Overview#

Decant computes session cost once, at ingest time, and writes it permanently to the estimated_cost_usd column on the session row. No background job or pricing-table update ever rewrites historical rows.

This is an intentional design choice: cost figures are point-in-time snapshots that reflect the pricing table in effect when a session was first ingested.

How Cost Is Calculated#

The entry point is estimateCost(model, usage, pricing) in src/cost.ts. It delegates to estimateCostParts, which applies per-million-token rates across four billing dimensions:

DimensionRate field
Input tokensinputPerMtok
Output tokensoutputPerMtok
Cache read tokenscacheReadPerMtok
Cache creation tokens (5-min TTL)cacheWritePerMtok
Cache creation tokens (1-hour TTL)cacheWrite1hPerMtok

For Claude, cache-read is 10% of input rate and cache-write is 1.25× (5-min) or 2× (1-hour) of input rate. For OpenAI models prior to GPT-5.6, cache writes carry no additional fee; GPT-5.6 bills cache writes at 1.25× the uncached input rate.

If the model slug is unrecognized or explicitly unpriceable (e.g., codex-auto-review, gpt-5.3-codex-spark), estimateCostParts returns all-zero parts rather than guessing a neighboring model's rate.

Where Pricing Comes From#

defaultPricing() returns an in-code Map<string, Price> covering Claude and OpenAI model families. The pricing table was last verified on July 25, 2026, against official Anthropic and OpenAI sources. The rates use standard global API token prices and do not apply Batch, Flex, Priority, regional-processing, or partner-cloud modifiers.

Model slug normalization is handled by canonicalModel(), which strips provider prefixes, lowercases, and maps version-specific suffixes to a canonical pricing key (e.g., claude-opus-4.5claude-opus).

Where Cost Is Stored#

writeSession() in src/ingest.ts calls estimateCost(s.model, s.totals, defaultPricing()) and immediately passes the result as parameter ?17 in the INSERT INTO session(...) statement. The value lands in estimated_cost_usd REAL NOT NULL DEFAULT 0 on the session table.

Cost is inserted as part of the same database transaction as the rest of the session row — it is never updated afterward.

Implications for Pricing Changes#

  • Existing rows are unaffected when defaultPricing() is updated. Historical estimated_cost_usd values remain frozen at the rates that were in code when each session was ingested.
  • To re-estimate existing sessions, rebuild the archive. This re-runs the full ingest pipeline with the current pricing table.
  • New sources that report no token usage must surface cost as unavailable, not zero, per the add-source guide. The presentation tier is wired deliberately based on the source's capability report.

Key Files#

FileRole
src/cost.tsestimateCost, estimateCostParts, defaultPricing, canonicalModel
src/ingest.tsCalls estimateCost and inserts the result into the session row
src/schema.sqlestimated_cost_usd column definition
docs/pricing.mdHuman-readable rate tables and source verification notes