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
- Variables
- func Canonical(s string) (string, error)
- func Cents(usd *big.Rat) string
- func Decimal(s string) (*big.Rat, error)
- func SpendWord(t Tokens, cost, model string) string
- func Sum(vals ...string) (sum string, ok bool)
- func Text(r *big.Rat) string
- func Words(by string) []string
- type Prediction
- type Prices
- type Tokens
- type Total
- type Usage
Constants ¶
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.
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.
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.
const ( CostBoth = "both" CostPredicted = "predicted" CostActual = "actual" CostNone = "none" )
The record's presence words (cost=).
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.
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.
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 ¶
var Billings = []string{BillingMetered, BillingPlan}
Billings are the billing kinds, the route's enum.
Functions ¶
func Canonical ¶
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 ¶
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 ¶
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 ¶
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 ¶
Sum is the exact sum of decimal strings, "" left out; ok is false when one is not a decimal.
func Text ¶
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.
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 ¶
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 ¶
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).
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.
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 ParseTotal ¶
ParseTotal reads a total's line (Total.String); "" is the total of no record.
func (Total) Add ¶
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 ¶
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.
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 ¶
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 ¶
ParseUsage reads a usage line (Usage.String, or the older wall= budget= shape).
func (Usage) Priced ¶
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.