pricing

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jun 15, 2026 License: AGPL-3.0 Imports: 10 Imported by: 0

Documentation

Overview

Package pricing is the domain layer for the Pricing entity — a named rate sheet owned by a Host and applied to one or more Models. One Pricing row holds every rate that bills against a (model, host) pair: input, output, cache_read, cache_creation, reasoning, audio_*, and any context-tier variants.

Pricing is owner=host so that "Anthropic's price list" and "Bedrock's price list" are two distinct entities even when they cover overlapping Models. TargetModelIDs lets one rate sheet cover a whole tier (claude-4 family etc.) without duplicating rows.

store.go is the data-access layer for Pricing. TargetModelIDs live in the pricing_models junction (not JSONB), so Upsert fans out across pricings + pricing_models inside a single transaction. host_id is a real column, hydrated alongside the rest of the row on List.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Meter

type Meter string

Meter is the dimension a Rate prices. Closed set; new meters require a schema bump (intentional — billing surface area should grow deliberately).

const (
	MeterTokensInput               Meter = "tokens.input"
	MeterTokensOutput              Meter = "tokens.output"
	MeterTokensCacheRead           Meter = "tokens.cache_read"
	MeterTokensCacheCreation       Meter = "tokens.cache_creation"
	MeterTokensReasoning           Meter = "tokens.reasoning"
	MeterTokensAudioInput          Meter = "tokens.audio_input"
	MeterTokensAudioOutput         Meter = "tokens.audio_output"
	MeterTokensAcceptedPrediction  Meter = "tokens.accepted_prediction"
	MeterTokensRejectedPrediction  Meter = "tokens.rejected_prediction"
	MeterTokensServerToolUseInput  Meter = "tokens.server_tool_use_input"
	MeterTokensServerToolUseOutput Meter = "tokens.server_tool_use_output"
)

func MeterForUsageKey

func MeterForUsageKey(k string) (Meter, bool)

MeterForUsageKey maps a usage.Tokens key (as emitted by Adapter.ExtractTokens) to its corresponding pricing Meter. Returns the meter and true if known, or "" and false for unpriced keys.

type Pricing

type Pricing struct {
	Meta meta.Metadata `json:"metadata" yaml:"metadata"`
	Spec Spec          `json:"spec"     yaml:"spec"`
}

Pricing is a rate sheet. Owner.Kind must be OwnerHost; Owner.ID is the Host id.

func (*Pricing) Cost

func (p *Pricing) Cost(tokens usage.Tokens) float64

Cost computes the total cost (in Spec.Currency units, typically USD) for the given Tokens map. Tier selection uses the input token count — the conventional axis for context-length tiers across major providers. Keys not mapped by MeterForUsageKey are silently skipped (unpriced dimension). Returns 0 when Pricing is disabled or carries no rates.

func (*Pricing) CostNanos added in v0.3.0

func (p *Pricing) CostNanos(tokens usage.Tokens) (total int64, breakdown map[string]int64, ok bool)

CostNanos computes the total cost in integer nano-USD (1 USD = 1e9 nanos) plus a per-meter breakdown keyed by Meter string. Integer money because the result is stamped onto immutable usage events — float drift across sums/aggregates is not acceptable for a billing-adjacent record, and per-request magnitudes fit int64 with room to spare (~9.2e9 USD).

Tier semantics match Cost (and sdk/catalog.Binding.Cost): the tier axis is the input token count; the rate with the largest AboveTokens ≤ input applies, and the WHOLE meter count bills at that tier (no marginal splitting) — the conventional context-length-tier model across providers.

ok is false when nothing was priced: nil/disabled pricing, no tokens, or no token key matched a rate. Callers must treat !ok as "unpriced", never as a zero cost — a fabricated $0 is the silent-drop bug class. A genuine zero (priced meters with zero counts) returns ok=true with total 0.

func (*Pricing) IsEnabled

func (p *Pricing) IsEnabled() bool

IsEnabled returns true when Enabled is unset or explicitly true.

func (*Pricing) RateFor

func (p *Pricing) RateFor(meter Meter, tokens int) (*Rate, bool)

RateFor returns the rate that should bill against the given meter and request-side token count. Walks Rates for the meter and picks the row with the largest AboveTokens that is still ≤ tokens. Returns (nil, false) if no rate exists for the meter.

func (*Pricing) Validate

func (p *Pricing) Validate() error

Validate runs intra-row rules via the shared meta.Validator and enforces the Pricing-specific invariants:

  • Owner.Kind == OwnerHost and Owner.ID is set (the Host id).
  • No two Rates share the same (Meter, AboveTokens) — would be ambiguous at billing time.

Cross-entity checks (TargetModelIDs resolve, no two enabled Pricings claim the same (model, host) pair) live in the catalog composition layer.

type Rate

type Rate struct {
	Meter       Meter   `` /* 319-byte string literal not displayed */
	Unit        Unit    `json:"unit"                  yaml:"unit"                  validate:"required,oneof=per_million per_unit"`
	Amount      float64 `json:"amount"                yaml:"amount"                validate:"required,gt=0"`
	AboveTokens int     `json:"aboveTokens,omitempty" yaml:"aboveTokens,omitempty" validate:"gte=0"`
}

Rate is one priced meter. AboveTokens=0 is the base tier; AboveTokens>0 is the rate charged once the request's billable token count exceeds that threshold. The billing-time picker walks rates for the meter and applies the largest qualifying threshold.

type Spec

type Spec struct {
	Currency       string   `json:"currency"             yaml:"currency"             validate:"required"`
	TargetModelIDs []string `json:"targetModels"         yaml:"targetModels"         validate:"required,min=1,dive,required"`
	Rates          []Rate   `json:"rates"                yaml:"rates"                validate:"required,min=1,dive"`
	Enabled        *bool    `json:"enabled,omitempty"    yaml:"enabled,omitempty"`
}

Spec carries the rate list, the target model set, currency, and an enable flag.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is the Pricing data-access type. Holds a pool so Upsert can run a multi-table transaction.

func NewStore

func NewStore(pool *pgxpool.Pool) *Store

NewStore constructs a Store bound to a pool.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, id string) error

Delete removes a Pricing by id. Junction rows cascade via FK.

func (*Store) Get

func (s *Store) Get(ctx context.Context, id string) (*Pricing, error)

Get returns the Pricing with the given id, hydrating TargetModelIDs. Returns (nil, nil) if not found.

func (*Store) List

func (s *Store) List(ctx context.Context) ([]*Pricing, error)

List returns every Pricing row with Spec.TargetModelIDs hydrated from the pricing_models junction in declaration order.

func (*Store) Upsert

func (s *Store) Upsert(ctx context.Context, p *Pricing) error

Upsert writes p across pricings + pricing_models in a single tx. Caller stamps Meta.ID and Owner.ID (host id).

type Unit

type Unit string

Unit is how Amount is interpreted. per_million is the common case for token rates (Amount=2.50 means $2.50 per 1M tokens). per_unit is for future meters like per-image or per-call.

const (
	UnitPerMillion Unit = "per_million"
	UnitPerUnit    Unit = "per_unit"
)

Jump to

Keyboard shortcuts

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