Documentation
¶
Overview ¶
Package pricing turns the raw model strings adapters record into canonical models with known token rates.
Cost is computed at read time from the price list rather than stored on model_invocations. A stored cost_usd would freeze whatever the list said the day a row was written: a wrong rate, or a fix to the normalisation rules, would then apply only to new rows and need a backfill. Computing at read time means fixing the list fixes history.
The normalisation rules are code and the rates are data (list.go). A model whose name the rules already resolve — any new Claude or GPT name — is priced by adding its rate alone, which is what lets a published list price it without a release. A name that needs a new alias still needs one.
Index ¶
- Constants
- Variables
- func CompareVersions(a, b string) int
- func Cost(model Model, usage Usage) (float64, bool)
- func EmbeddedDocument() []byte
- func Install(list *List)
- func LoadPersisted(dir string) error
- func PublishedKey() ed25519.PublicKey
- func Refresh(ctx context.Context, dir string) error
- func UnpricedReason(model Model) string
- func Version() string
- type Document
- type Entry
- type Family
- type List
- func (l *List) AccountingCost(a *agent.Accounting) (*float64, string)
- func (l *List) BackendAccountingCost(backend string, a *agent.Accounting) (*float64, string)
- func (l *List) BackendOutcomeCost(backend, model string, outcome agent.Outcome) *float64
- func (l *List) Cost(model Model, usage Usage) (float64, bool)
- func (l *List) Full() bool
- func (l *List) Models() []string
- func (l *List) Normalize(raw, vendor string) Model
- func (l *List) OutcomeCost(model string, outcome agent.Outcome) *float64
- func (l *List) PromptBands() []int64
- func (l *List) Rates(model Model) (Rate, bool)
- func (l *List) RatesFor(model Model, usage Usage) (RateSet, bool)
- func (l *List) Version() string
- type Model
- type Rate
- type RateSet
- type Source
- type Speed
- type Tier
- type Usage
Constants ¶
const ( VendorClaudeCode = "claude_code" VendorCodex = "codex" )
Canonical vendor slugs this package reasons about. They are `agent_session_ingests.vendor` values, which is what analytics groups by; the recorder that delivered the session (upload, Entire checkpoint) never reaches here, because it says nothing about how the tokens were counted.
const ( SourceLiteLLM = "litellm" SourceManual = "manual" )
Entry sources. The sync tool rewrites litellm entries from LiteLLM's price file and never touches manual ones, which record a deliberate choice the source would overwrite (a Cursor rate, a peak-hour rate, a retired model).
const ( OriginEmbedded = "embedded" OriginFetched = "fetched" OriginCached = "cached" )
Origins of the active price list.
const FormatVersion = 2
FormatVersion marks the full format: a list that may carry cache_write_1h, above, fast and fast_above. Published lists must carry this marker.
const PublishedURL = "https://static.usebaum.com/pricing/v3/prices.json"
PublishedURL is where main's prices.json is published, with its signature at PublishedURL + ".sig". It carries the full format (tiers, fast mode, 1-hour cache writes). The path version changes whenever older builds could not read the publication: v2 added the full format, and v3 is signed with the price-list key. pricing-publish keeps writing v2, signed with the release manifest key, for appliances built before v3.
const ReasonBilledByCursor = "billed_by_cursor_subscription"
ReasonBilledByCursor is why a cursor-agent run has no recorded cost: Cursor bills it through the user's own subscription, so no list price describes it, and pricing its tokens at an API rate would mislead.
Variables ¶
var ErrSignatureNotTrusted = errors.New("price list signature is not trusted")
ErrSignatureNotTrusted is the error for a list that publishedKey (or the key passed to ParsePublished) did not sign.
Functions ¶
func CompareVersions ¶
CompareVersions orders two valid versions: negative when a is older.
func Cost ¶
Cost prices one rollup. ok is false when the model has no rate or the usage cannot be matched to one (see List.Cost), in which case the caller must report the tokens without a dollar figure rather than treating the cost as zero.
func EmbeddedDocument ¶
func EmbeddedDocument() []byte
EmbeddedDocument returns the compiled prices.json bytes.
func Install ¶
func Install(list *List)
Install makes list the active list. Callers choose which list is newest; see Newest.
func LoadPersisted ¶
LoadPersisted installs the list a previous Refresh stored in dir if its signature still verifies and it is newer than the active list. A missing file is not an error; any other failure leaves the active list unchanged.
func PublishedKey ¶
PublishedKey is the key published lists are signed with.
func Refresh ¶
Refresh downloads only public signed data, without credentials or redirects. A failure leaves the compiled or previously verified list active. When dir is not empty, a downloaded list that becomes active is stored there for LoadPersisted.
func UnpricedReason ¶
UnpricedReason explains why a recognised model has no rate, or "" if the model is priced or unknown.
Types ¶
type Document ¶
type Document struct {
// Format identifies the full pricing format.
Format int `json:"format,omitzero"`
// Version is a date, with a ".N" suffix for a second list on one day. It
// travels with every figure computed from the list.
Version string `json:"version"`
Models map[string]Entry `json:"models"`
}
Document is prices.json.
func ParseDocument ¶
ParseDocument decodes and validates a price list. Unknown fields are rejected so a document written for a newer format is not half understood.
type Entry ¶
type Entry struct {
Input float64 `json:"input"`
Output float64 `json:"output"`
CacheRead float64 `json:"cache_read"`
CacheWrite float64 `json:"cache_write"`
CacheWrite1h float64 `json:"cache_write_1h,omitzero"`
// Above holds the rates for a request whose prompt is over
// Above.PromptTokens.
Above Tier `json:"above,omitzero"`
// Fast holds the rates for a request run in fast mode.
Fast RateSet `json:"fast,omitzero"`
// FastAbove holds the fast-mode rates for a request whose prompt is over
// FastAbove.PromptTokens, which is Above.PromptTokens. It needs both Above
// and Fast. An entry with Above and Fast but no FastAbove has no published
// fast rate over the threshold, and a fast request over it is not priced.
FastAbove Tier `json:"fast_above,omitzero"`
Source string `json:"source"`
// LiteLLMKey names the source entry when it differs from the canonical id.
LiteLLMKey string `json:"litellm_key,omitempty"`
Note string `json:"note,omitempty"`
}
Entry is one canonical model's rates, per million tokens. It stays comparable with == (pricingsync compares entries), so optional parts are value structs that are omitted when zero rather than pointers.
type List ¶
type List struct {
// contains filtered or unexported fields
}
List is a validated price list ready to price usage.
func Newest ¶
Newest returns whichever list has the later version, preferring a when they are equal.
func ParsePublished ¶
ParsePublished accepts a published list only when key signed it and it still prices every model the compiled list prices. The second check stops a truncated or mistaken publication from turning known costs into unknowns across every appliance at once.
func (*List) AccountingCost ¶
func (l *List) AccountingCost(a *agent.Accounting) (*float64, string)
AccountingCost prices attempts separately so a repair using a different model cannot reprice the whole run at its final model's rate.
func (*List) BackendAccountingCost ¶
BackendAccountingCost is AccountingCost for a run of backend: nil with the reason for a backend the list does not price.
func (*List) BackendOutcomeCost ¶
BackendOutcomeCost is OutcomeCost for a run of backend: nil for a backend the list does not price.
func (*List) Cost ¶
Cost prices one rollup with the list's rates. A tier applies to a request whose prompt is over its threshold, so the usage's prompt range decides: at or under the threshold throughout is the base (or fast) rates, over it throughout is the tier's, and a range that straddles it is not priced, because the usage does not say which requests were long. It also reports not priced for fast mode on an entry without fast rates, for fast mode over a threshold the entry has no fast_above rates for, for ultrafast, for 1-hour writes where the chosen rates have no 1-hour price, for a 1-hour count that is negative or larger than the cache writes it is part of, and for an inverted or negative prompt range.
func (*List) Full ¶
Full reports whether the list was read from the full format. An unmarked list prices correctly only for usage without tiers, fast mode or 1-hour writes, so a build that reads the full format must not price with one.
func (*List) Normalize ¶
Normalize resolves a raw model string to a canonical model.
The hard case is Claude, where two orderings are both in the wild: Anthropic writes `claude-sonnet-4-5` and Cursor writes `claude-4.5-sonnet` — family and size transposed. Prefix matching fails on that pair, so the decomposition below finds the size word and the version numbers wherever they sit rather than assuming an order.
vendor is the last resort, and only disambiguates a bare version string. The session model stores no provider column: which API served a model is not a fact any mapper reliably recorded, and the vendor implies it wherever it mattered.
func (*List) OutcomeCost ¶
OutcomeCost keeps unknown or ambiguous usage unknown. Agent usage is already disjoint, including Codex: it must not apply Codex's raw-session subtraction.
func (*List) PromptBands ¶
PromptBands lists, ascending and without repeats, the prompt sizes at which any entry's rates change: the thresholds of its above and fast_above tiers. A query that sums usage groups requests by band, which keeps every request of a group on one side of every threshold, so the group can be priced exactly.
func (*List) RatesFor ¶
RatesFor is the rate set Cost applies to the usage: the base, fast or long-prompt tier rates its speed and prompt range select. ok is false where Cost reports not priced for the model, speed or prompt range (an unknown model, ultrafast, fast with no fast rates, a range straddling a threshold, an inverted range), so a caller estimating before a request can tell "priced at these rates" from "would not be priced" and never falls back to base rates.
type Model ¶
type Model struct {
// Canonical is the id the price table is keyed on, e.g. "claude-sonnet-4-5".
// Empty when the raw string could not be resolved.
Canonical string
Family Family
// Size is the Claude tier (opus, sonnet, haiku, fable, mythos). Empty for
// other families.
Size string
// Raw is the string the adapter recorded, kept so an unknown model can be
// surfaced by name rather than as a silent zero.
Raw string
}
Model is a normalised model identity.
type RateSet ¶
type RateSet struct {
Input float64 `json:"input"`
Output float64 `json:"output"`
CacheRead float64 `json:"cache_read"`
CacheWrite float64 `json:"cache_write"`
// CacheWrite1h prices cache writes with the 1-hour TTL; zero means the
// set has no such rate.
CacheWrite1h float64 `json:"cache_write_1h,omitzero"`
}
RateSet is one set of per-million-token rates in USD.
type Source ¶
type Source struct {
// Origin is "embedded", "fetched" or "cached".
Origin string
Version string
// FetchedAt is when Refresh installed the active list; zero for the
// embedded and cached lists.
FetchedAt time.Time
// RefreshError is why the last Refresh failed; empty after a success or
// before any attempt.
RefreshError string
}
Source says where the active price list came from: compiled in, fetched by Refresh in this process, or loaded by LoadPersisted from the list an earlier daemon run stored.
func CurrentSource ¶
func CurrentSource() Source
CurrentSource describes the active price list and the last Refresh. It is safe to call while Refresh runs.
type Speed ¶
type Speed string
Speed is the service speed a request was billed at.
const ( SpeedUnknown Speed = "" SpeedStandard Speed = "standard" SpeedFast Speed = "fast" SpeedUltrafast Speed = "ultrafast" )
Speeds. Unreported speed is priced as standard, which is how a caller that does not record speed has always been priced; fast mode has its own rates; ultrafast is a separate, pricier tier that the price list has no rates for.
type Tier ¶
type Tier struct {
PromptTokens int64 `json:"prompt_tokens"`
Input float64 `json:"input"`
Output float64 `json:"output"`
CacheRead float64 `json:"cache_read"`
CacheWrite float64 `json:"cache_write"`
CacheWrite1h float64 `json:"cache_write_1h,omitzero"`
}
Tier is the rate set a vendor charges for a request whose prompt is over PromptTokens. It replaces every rate, output included.
type Usage ¶
type Usage struct {
Vendor string
InputTokens int64
OutputTokens int64
CacheReadTokens int64
CacheWriteTokens int64
// CacheWrite1hTokens is the part of CacheWriteTokens written with the
// 1-hour TTL; 0 when unknown or none. The rest are 5-minute writes.
CacheWrite1hTokens int64
// Speed is the service speed the request was billed at; SpeedUnknown (the
// zero value) when the vendor did not report it.
Speed Speed
// SingleRequest is true when the usage is exactly one API request, so its
// prompt is PromptTokens() and the prompt range below is not needed.
SingleRequest bool
// PromptRangeKnown says MinPromptTokens and MaxPromptTokens are set: the
// smallest and largest prompt (by the PromptTokens rule) among the single
// requests the usage covers. A caller that sums rows can know them without
// knowing each request, for instance from a SQL group whose rows all sit on
// one side of every tier threshold. Without it, and without SingleRequest,
// the range is [0, PromptTokens()]: each request's prompt is at most the
// combined prompt, and nothing bounds it from below.
PromptRangeKnown bool
MinPromptTokens int64
MaxPromptTokens int64
}
Usage is one rollup's raw token columns, exactly as agent_session_model_invocations and agent_session_token_usage store them.
func (Usage) PromptTokens ¶
PromptTokens is the size of the prompt a request was billed for, which decides whether a long-prompt tier applies. Codex's input_tokens already includes its cached input, so it is the prompt; for every other vendor the buckets are disjoint and the prompt is their sum. This is the single place to change if a vendor documents a different rule.
func (Usage) UncachedInput ¶
UncachedInput applies the vendor-dependent cache-subsumption rule.
Codex's input_tokens already includes its cached input, reads and writes alike; Anthropic's buckets are disjoint. The token columns are stored raw, so any rollup that skips this branch charges a codex session for its cache reads (and writes) twice, inflating its cost by the cache-hit rate, which on a long session is 3-5x and still a believable dollar figure.