Documentation
¶
Overview ¶
Package usage normalizes provider token accounting into one additive, provider-neutral form so that aggregation never has to know which CLI produced an event.
Two provider differences make a raw api.AgentUsage unsafe to sum:
- Whether InputTokens already contains the cached portion. Claude and pi report fresh input with cache counted separately; Codex reports a total that includes both cache reads and cache writes.
- Whether the provider reports token counts at all. Antigravity and Qoder transcripts carry none, so a zero there means "unknown", not "free".
Both are resolved here and nowhere else. Write, re-aggregation, and display paths must all route through this package: cc-switch, which solves the same problem, regressed exactly once by duplicating the cache-inclusive provider list in a backfill path and missing one provider there.
Index ¶
Constants ¶
const PricingEndpoint = "https://warrenai.xyz/api/model-pricing"
PricingEndpoint is Warren's own projection of the models.dev catalog. Going through it rather than models.dev directly keeps the payload small, gives one stable document shape to depend on, and resolves each model to its first-party vendor price instead of whichever reseller the upstream catalog happened to list last.
Variables ¶
This section is empty.
Functions ¶
func NormalizeModelID ¶
NormalizeModelID reduces a provider's model string to the identity used for price lookup. models.dev keys models by bare id, while the CLIs decorate that id with a routing prefix, a variant suffix, or a context-window marker.
The rules mirror cc-switch's normalizeModelIdForPricing, which is proven against the same catalog:
anthropic/claude-opus-5 -> claude-opus-5 (routing prefix) claude-opus-5[1m] -> claude-opus-5 (context-window marker) gpt-5.5:thinking -> gpt-5.5 (variant suffix) gemini-2.5-pro@20260101 -> gemini-2.5-pro-20260101
The original string is stored alongside the normalized one so a model that still fails to match a price can be identified rather than vanishing into an unpriced total.
Types ¶
type Buckets ¶
type Buckets struct {
// FreshInput is prompt input that was neither read from nor written to
// cache. Priced at the input rate.
FreshInput int64
// CacheWrite is input persisted into the prompt cache. Priced above input.
CacheWrite int64
// CacheRead is input served from the prompt cache. Priced far below input.
CacheRead int64
// Output is every generated token, reasoning included, since reasoning is
// billed at the output rate. Verified against 46186 real Codex events
// where reasoning_output_tokens never exceeded output_tokens.
Output int64
// Reasoning is the reasoning subset of Output. Display only: adding it to
// a cost would double-count, so it is deliberately not a fifth bucket.
Reasoning int64
}
Buckets is one billable model call split into four disjoint token classes. Disjoint and exhaustive is the whole point: the four sum to the real total and each maps to exactly one unit price, which makes every stored column additive across any grouping.
func Normalize ¶
func Normalize(provider string, value *api.AgentUsage) (Buckets, bool)
Normalize folds one provider usage observation into disjoint buckets. It reports false when the provider does not measure usage or the observation carried nothing countable, in which case no rollup row must be written.
TotalTokens and the provider's own InputTokens are deliberately dropped: the former is derivable and would disagree with the buckets the moment a provider rounds differently, and the latter has no single meaning across providers.
type CostStatus ¶
type CostStatus int
CostStatus describes how completely a set of buckets could be priced. It has to survive aggregation: a group holding anything other than Priced must be presented as a lower bound, or the panel shows a total that looks complete while silently omitting spend.
const ( // CostPriced means every bucket with tokens had a unit price. CostPriced CostStatus = iota // CostPartial means the model was found but a bucket carrying tokens had no // stated price, so the returned cost is short by that bucket. CostPartial // CostUnpriced means the model is not in the table at all. CostUnpriced )
func Cost ¶
func Cost(buckets Buckets, price ModelPrice, found bool) (int64, CostStatus)
Cost prices one call's buckets, returning integer nanodollars.
Reasoning is not priced separately: it is already contained in Output and billed at the output rate, so adding it would double-charge.
type InputBasis ¶
type InputBasis int
InputBasis describes what a provider's InputTokens already contains.
const ( // InputFresh means InputTokens excludes cache reads and cache writes, // which are reported as their own counters. Anthropic's shape. InputFresh InputBasis = iota // InputTotal means InputTokens is the whole prompt including any cached // portion, so billable fresh input is the remainder after subtracting the // cache counters. OpenAI's shape. InputTotal )
type ModelPrice ¶
type ModelPrice struct {
Provider string `json:"provider"`
Name string `json:"name"`
Input *float64 `json:"input"`
Output *float64 `json:"output"`
CacheRead *float64 `json:"cacheRead"`
CacheWrite *float64 `json:"cacheWrite"`
}
ModelPrice is the unit price set for one model. A nil field means the catalog did not state that price, which is different from stating zero: unknown must not be billed as free.
type PriceFetcher ¶
type PriceFetcher struct {
// Endpoint overrides PricingEndpoint in tests.
Endpoint string
// Client overrides the default HTTP client.
Client *http.Client
// TTL is how long a fetched table is reused. Unit prices change on release
// announcements, so a short TTL only adds requests.
TTL time.Duration
// contains filtered or unexported fields
}
PriceFetcher retrieves and caches the price table.
func (*PriceFetcher) Table ¶
func (f *PriceFetcher) Table(ctx context.Context) (*PriceTable, error)
Table returns the cached table, fetching it when absent or stale.
On a failed refresh an existing table is returned unchanged: stale prices produce a slightly dated cost, while treating the failure as an empty table would silently reprice every model to zero.
type PriceTable ¶
type PriceTable struct {
Unit string `json:"unit"`
FetchedAt time.Time `json:"fetchedAt"`
Models map[string]ModelPrice `json:"models"`
}
PriceTable maps a normalized model id to its unit prices.
func (*PriceTable) Price ¶
func (t *PriceTable) Price(model string) (ModelPrice, bool)
Price returns the entry for a model id that has already been normalized by NormalizeModelID.
type Semantics ¶
type Semantics struct {
// Reports is false when the provider's transcript gives Warren no token
// counts at all. Such providers produce no rollup rows and must surface as
// unmeasured rather than as zero cost.
Reports bool
// Basis is what InputTokens contains.
Basis InputBasis
// ReportsCacheWrite is false when the provider never distinguishes cache
// creation from ordinary input. A zero cache-write bucket from such a
// provider is unknown, not observed-zero, and must render as N/A.
ReportsCacheWrite bool
}
Semantics is the per-provider accounting contract. It describes what Warren's adapter currently emits, not what the underlying CLI is capable of emitting: a provider whose transcript carries usage that Warren does not yet parse must stay Reports=false, or the panel would report a confident zero for spend that actually happened.
func SemanticsFor ¶
SemanticsFor returns the accounting contract for a provider.