usage

package
v0.14.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

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:

  1. 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.
  2. 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

View Source
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

func NormalizeModelID(value string) string

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.

func (*Buckets) Add

func (b *Buckets) Add(other Buckets)

Add accumulates another call. Every field is additive by construction.

func (Buckets) Empty

func (b Buckets) Empty() bool

Empty reports whether the call carried no countable tokens.

func (Buckets) Total

func (b Buckets) Total() int64

Total is the real token count for this call.

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

func SemanticsFor(provider string) Semantics

SemanticsFor returns the accounting contract for a provider.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL