cardcost

package
v1.2.11 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package cardcost is what a card cost: a route's price sheet as nova-config holds it (the route kind's price fields, docs/SPEC-CONFIG.md, "route"), the tokens one run spent by class, the predicted cost of the one priced by the other, and the usage record a consumer card keeps (docs/SPEC-SPRINT.md, "What a card cost"), in exact decimal arithmetic. Every amount is a decimal string end to end, never a float: a price is typed as a decimal, kept as one in Postgres and Redis, and multiplied as a rational.

Libraries considered: math/big's Rat (the standard library) does exact decimal arithmetic on strings of any length; no adopted module does decimals, and internal/tokens keeps micro-dollars, which would round a price per token.

Index

Constants

View Source
const (
	FieldInput             = "price_input"         // uncached input
	FieldCacheRead         = "price_cache_read"    // cached input read
	FieldCacheWrite        = "price_cache_write"   // cache write
	FieldOutput            = "price_output"        // output
	FieldReasoningAsOutput = "reasoning_as_output" // bool: reasoning tokens billed at the output price
	FieldLongContext       = "long_context"        // tokens: a request whose prompt is above it is priced long
	FieldInputLong         = "price_input_long"    // input above the threshold
	FieldOutputLong        = "price_output_long"   // output above the threshold
	FieldRequest           = "price_request"       // USD per request
	FieldBilling           = "billing"             // metered or plan
	FieldGateway           = "gateway_percent"     // percent added on top by a gateway
	FieldSource            = "price_source"        // free text: where the prices were read
	FieldAsOf              = "price_as_of"         // the date they were read, YYYY-MM-DD
)

The route kind's price fields (pkg/config/kind.go), the names add and set take as flags, the columns of config.routes and the keys apply writes into the hash route:<name>, which the sprint reads with the routes. Prices are USD per million tokens unless the name says otherwise.

View Source
const (
	BillingMetered = "metered"
	BillingPlan    = "plan"
)

The billing kinds: metered is paid per token; plan is paid by subscription, so the predicted cost is the metered price of the same tokens, not a charge.

View Source
const (
	WhyNoSheet    = "no-price-sheet"
	WhyNoTokens   = "no-tokens"
	WhyNoPrice    = "no-price:"
	WhyBadPrice   = "bad-price:"
	WhyNoRequests = "no-request-count"
	WhyNoRoute    = "no-route"
)

Why a run has no prediction, one word each, as the usage record keeps it (unpriced=<why>): no route with a price sheet, no token reported, a class with tokens and no price (no-price:<class>), a price that is not a decimal (bad-price:<class>), a per-request fee with no request count, and no route found for the run at all.

View Source
const (
	CostBoth      = "both"
	CostPredicted = "predicted"
	CostActual    = "actual"
	CostNone      = "none"
)

The record's presence words (cost=).

View Source
const ActualByHarness = "harness"

ActualByHarness says the harness reported the cost: opencode prices each message from its own model table and keeps the cost, a float, beside its tokens; the figure is the decimal of their float sum, the harness's computation and never an invoice.

View Source
const MaxFraction = 30

MaxFraction is the most digits after the point a typed decimal may have: a price or a percent past it is refused at input, so every amount this package makes from one terminates inside maxDigits and is written exactly.

View Source
const Unreported int64 = -1

Unreported is a token count, request count or prompt size the harness did not report: an absence, never a zero.

Variables

Billings are the billing kinds, the route's enum.

Functions

func Canonical

func Canonical(s string) (string, error)

Canonical is a decimal's one spelling: no leading zero before the point but one, no trailing zero after it ("0.30" is "0.3", "007" is "7", "1.0" is "1"); "" stays "" (not set).

func Cents

func Cents(usd *big.Rat) string

Cents is a dollar amount as a table or a line shows it: "$" and the amount in dollars and cents, rounded up to the next cent ("$1.24" for 1.2345, "$20.22" for 20.2111; the owner's rule, money to the cent, rounded up). What is kept of an amount elsewhere is exact; this is only how it is shown.

func Decimal

func Decimal(s string) (*big.Rat, error)

Decimal reads a non-negative decimal as add and set take it ("0.30", "15", "0.0000125") exactly, refusing more than MaxFraction digits after the point.

func SpendWord

func SpendWord(t Tokens, cost, model string) string

SpendWord is what a job spent as native's NATIVE line carries it (spend=), one word: input:<n>,cache_read:<n>,cache_write:<n>,output:<n>,reasoning:<n>, requests:<n>,max_prompt:<n>,cost:<usd>,model:<provider/model>, each part left out when the harness did not report it; "" when it reported nothing.

func Sum

func Sum(vals ...string) (sum string, ok bool)

Sum is the exact sum of decimal strings, "" left out; ok is false when one is not a decimal.

func Text

func Text(r *big.Rat) string

Text is a rational whose decimal terminates, written exactly, with no trailing zero after the point. The package's exact decimal contract (package comment: every amount is a decimal string end to end, never a float; docs/SPEC-SPRINT.md, "What a card cost") has amount accept a stored decimal of any length and Sum add exactly, so Text cuts no terminating digit: terminatingScale reads the exact number of places off the denominator. A rational that does not terminate has no exact spelling and is cut at maxDigits.

func Words

func Words(by string) []string

Words splits an actual_by list ("" is none).

Types

type Prediction

type Prediction struct {
	USD  string `json:"usd"`
	Long bool   `json:"long,omitempty"`
	Why  string `json:"why,omitempty"`
}

Prediction is a run's predicted cost under a price sheet: USD the exact decimal, "" when the run cannot be priced, and Why says why not. Long is true when a request's prompt was above the sheet's threshold and the run was priced at the long input and output prices.

func Predict

func Predict(t Tokens, p Prices) Prediction

Predict prices the run's tokens by the sheet (docs/SPEC-SPRINT.md, "What a card cost"): each class's tokens times its price per million; reasoning at the output price when the sheet bills it as output, else not billed; plus the per-request fee times the requests; all times one plus the gateway percent. When the sheet has a long-context threshold and the run's largest prompt is above it, input and output are priced at the long prices: the harness reports the run's sums, not each request's, so a run that crossed the threshold once is priced long as a whole (an upper bound, marked Long). A sheet with no price, a run with no token reported, a class with tokens and no price, or a fee with no request count gives no prediction, never a zero.

type Prices

type Prices struct {
	Input             string `json:"input,omitempty"`
	CacheRead         string `json:"cache_read,omitempty"`
	CacheWrite        string `json:"cache_write,omitempty"`
	Output            string `json:"output,omitempty"`
	ReasoningAsOutput bool   `json:"reasoning_as_output"`
	LongContext       int64  `json:"long_context,omitempty"`
	InputLong         string `json:"input_long,omitempty"`
	OutputLong        string `json:"output_long,omitempty"`
	Request           string `json:"request,omitempty"`
	Billing           string `json:"billing,omitempty"`
	GatewayPercent    string `json:"gateway_percent,omitempty"`
	Source            string `json:"source,omitempty"`
	AsOf              string `json:"as_of,omitempty"`
}

Prices is one route's price sheet. Every amount is a canonical decimal string, "" when not set.

func PricesOf

func PricesOf(f map[string]string) Prices

PricesOf is the price sheet of a route's fields, as a config row or the route's hash holds them: a field missing is not set, and reasoning is billed as output unless the field says false.

func (Prices) Copy

func (p Prices) Copy() string

Copy is the sheet as a consumer card keeps it beside its predicted cost, so a later change of the route's prices does not rewrite what the card cost: one word, in:<p>,cr:<p>,cw:<p>,out:<p>,ro:<bool>,long:<n>,inl:<p>,outl:<p>,req:<usd>, bill:<kind>,gw:<pct>,asof:<date>, each part left out when not set. The source is free text and stays on the route's history (nova-config route history).

func (Prices) Priced

func (p Prices) Priced() bool

Priced says the sheet holds a price: a route with none has no predicted cost.

type Tokens

type Tokens struct {
	Input      int64 `json:"input"`
	CacheRead  int64 `json:"cache_read"`
	CacheWrite int64 `json:"cache_write"`
	Output     int64 `json:"output"`
	Reasoning  int64 `json:"reasoning"`
	Requests   int64 `json:"requests"`
	MaxPrompt  int64 `json:"max_prompt"`
}

Tokens is what one run spent, by class, as the harness reported it: uncached input, cached input read, cache write, output and reasoning (opencode reports reasoning apart from output), the requests made, and the largest prompt of one request (its input, cache read and cache write). Each is Unreported when the harness did not say.

func None

func None() Tokens

None is a run of which nothing was reported.

func (Tokens) Reported

func (t Tokens) Reported() bool

Reported says the harness reported a token class of the run.

func (Tokens) Total

func (t Tokens) Total() int64

Total is the run's tokens over the classes reported.

type Total

type Total struct {
	Records   int    `json:"records"`
	Tokens    Tokens `json:"tokens"`
	Wait      int64  `json:"wait_s"`
	Run       int64  `json:"run_s"`
	Predicted string `json:"predicted_usd"` // "" when no record holds one
	PredOf    int    `json:"predicted_records"`
	Actual    string `json:"actual_usd"`
	ActualOf  int    `json:"actual_records"`
	// ActualBy is who reported the actual costs summed: one word when every record's
	// came from the same reporter, the words joined with + when they differ, "" when
	// none did. The harness's figure is its own (opencode prices each message from
	// its model table), never an invoice.
	ActualBy string `json:"actual_by"`
	// Charged is the sum, over the records, of each one's actual cost where reported,
	// else its predicted one; ChargedOf is how many records had either.
	Charged   string `json:"charged_usd"`
	ChargedOf int    `json:"charged_records"`
}

Total is the sum over a producer card's consumer records: the tokens of each class over the records that reported it (Unreported when none did), the waiting and running seconds over the records that have them, and each cost over the records that hold it, with how many of the records did. Charged is the producer's one figure: each record's actual cost where reported, else its predicted one.

func NoTotal

func NoTotal() Total

NoTotal is the total of no record.

func ParseTotal

func ParseTotal(line string) Total

ParseTotal reads a total's line (Total.String); "" is the total of no record.

func SumUsage

func SumUsage(us []Usage) Total

SumUsage is the total of the records.

func (Total) Add

func (t Total) Add(u Usage) Total

Add is the total with one more record in it, every sum exact: only a valid non-negative decimal amount is summed, counted and named as a reporter (Usage, "a cost is never guessed"; the exact-cost contract in Total). A rejected amount changes nothing, so it cannot stand in for or contaminate a valid one.

func (Total) Repriced

func (t Total) Repriced(was, now Usage) Total

Repriced is the total with one of its records priced again: was is the record as the total counted it, now the same record with its prediction made again (Priced). Its tokens, times and actual cost are the same, so only the predicted and charged sums and their counts move, each exactly; a total that holds records past its card's list (the cut) keeps them this way, where a sum over the list would lose them. A sum that would go below zero, which a total that counted was cannot, is left as it is.

func (Total) String

func (t Total) String() string

String is the total as a producer card keeps it, one line of key=value words, a count or time not reported left out.

type Usage

type Usage struct {
	Wall     string `json:"wall,omitempty"`   // the harness's wall, as native's line says it ("49.49s")
	Budget   string `json:"budget,omitempty"` // the budget word (swarm.BudgetWord)
	Tokens   Tokens `json:"tokens"`
	Model    string `json:"model,omitempty"`      // provider/model, as the harness reported the run
	Actual   string `json:"actual_usd,omitempty"` // USD the harness reported
	ActualBy string `json:"actual_by,omitempty"`  // who reported it: ActualByHarness
	// Wait is the seconds from dealt (asked) to taken (begun), Run from taken (begun)
	// to the end; Unreported when a stamp is missing.
	Wait int64 `json:"wait_s"`
	Run  int64 `json:"run_s"`
	// Route is the route whose price sheet priced the run, Prices the sheet's copy
	// (Prices.Copy), Predicted the USD, Long the long-context pricing, Unpriced why
	// there is no prediction.
	Route     string `json:"price_route,omitempty"`
	Prices    string `json:"prices,omitempty"`
	Predicted string `json:"predicted_usd,omitempty"`
	Long      bool   `json:"long,omitempty"`
	Unpriced  string `json:"unpriced,omitempty"`
	// Extra are words of the line this record does not know, kept in order.
	Extra []string `json:"-"`
}

Usage is a consumer card's usage record: what one run of a work card (a take) or of a read card cost, kept in the card's usage field (and in a failed take's record) as one line of key=value words, so the sprint's tables gain no column (docs/SPEC-SPRINT.md, "What a card cost"). The member writes what the harness reported (wall, budget, the tokens by class, the model, the harness's own cost); the sprint's step adds the time and the prediction when the card ends:

wall=49.49s budget=20088/400000 input=19541 cache_read=36336 cache_write=0
output=692 reasoning=70 requests=6 max_prompt=9861 model=opencode/deepseek-v4-pro
actual_usd=0.04219614 actual_by=harness wait=3s run=52s price_route=pro-a
prices=in:0.27,cr:0.07,out:1.1,ro:true predicted_usd=0.00680779 cost=both

A count the harness did not report is left out, never written as 0; a cost is never guessed: actual_usd only when the harness reported one, predicted_usd only when the route that served the run has a price sheet (else unpriced=<why>). cost says which of the two the record holds: both, predicted, actual or none. A line of the older shape (wall= and budget= alone) reads as a record with no token, no time and no cost.

func NoUsage

func NoUsage() Usage

NoUsage is a record with nothing in it: every count and time unreported.

func ParseSpend

func ParseSpend(w string) Usage

ParseSpend is a spend= word's record: its tokens, the harness's cost and its model, the cost marked as the harness's.

func ParseUsage

func ParseUsage(line string) Usage

ParseUsage reads a usage line (Usage.String, or the older wall= budget= shape).

func (Usage) Present

func (u Usage) Present() string

Present is which of the two costs the record holds.

func (Usage) Priced

func (u Usage) Priced(route string, p Prices) Usage

Priced is the record with its prediction under the sheet of the route named, the sheet copied beside it: route "" (no route found) is unpriced=no-route.

func (Usage) String

func (u Usage) String() string

String is the record's one line, in the order the type documents, each word left out when it holds nothing; cost= always closes it.

func (Usage) Timed

func (u Usage) Timed(from, began string, end time.Time) Usage

Timed is the record with its waiting and running time: from the stamp it was dealt (asked) to the one it was taken (begun), and from that to end; a stamp missing or unreadable leaves its time unreported.

Jump to

Keyboard shortcuts

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