Documentation
¶
Overview ¶
Package cost prices what the agent spends. Every provider reports the tokens a call used; this package turns those counts into dollars, keeps a running ledger per session and overall, and refuses a turn once a budget cap is reached.
Prices come from the model catalog OpenRouter publishes — the one public list that carries per-token rates for every major vendor — cached on disk so a cold start is not a network round trip. Models served from this machine are free by construction, and anything the catalog does not carry is counted in tokens and left unpriced rather than guessed at.
Index ¶
- Constants
- func BudgetStop(err error) string
- func FormatTokens(n int) string
- func FormatUSD(v float64) string
- func Local(c config.Candidate) bool
- func NewTool(m *Meter) []tools.Tool
- type BudgetError
- type Catalog
- type ChatProvider
- type Ledger
- type Meter
- func (m *Meter) Active() bool
- func (m *Meter) Budget() config.BudgetConfig
- func (m *Meter) Chat(ctx context.Context, req *provider.Request) (*provider.Response, error)
- func (m *Meter) OverviewLine() string
- func (m *Meter) Report(sessionKey string) string
- func (m *Meter) SessionLine(sessionKey string) string
- func (m *Meter) Snapshot(sessionKey string) Snapshot
- func (m *Meter) Unpriced(s Snapshot) []string
- type Price
- type Snapshot
- type Tool
- type Totals
Constants ¶
const DefaultPricesURL = "https://openrouter.ai/api/v1/models"
DefaultPricesURL is the public model catalog: ids with per-token rates for every vendor OpenRouter fronts, which is most of them.
Variables ¶
This section is empty.
Functions ¶
func BudgetStop ¶
BudgetStop returns the sentence to answer with when err is a budget refusal, and "" for every other error. A cap is a decision the user made, not a failure to report as one.
func FormatTokens ¶
FormatTokens abbreviates a token count for a line that has no room to spell it out.
func FormatUSD ¶
FormatUSD writes an amount at the precision it deserves: cents once there are cents to see, four places while a turn still costs less than one.
Types ¶
type BudgetError ¶
type BudgetError struct {
Scope string // session | day | month | total
Spent float64
Limit float64
}
BudgetError is a call refused because a cap was met.
func (*BudgetError) Error ¶
func (e *BudgetError) Error() string
func (*BudgetError) Message ¶
func (e *BudgetError) Message() string
Message is the sentence the user reads instead of an answer: what was spent, against which cap, and the two ways out of it.
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog answers what a model costs. Overrides from config win, models served locally are free, and everything else comes from the fetched catalog — by exact id first, then by the vendor-and-date-insensitive form that lets a native Anthropic model id find its OpenRouter twin.
func NewCatalog ¶
NewCatalog builds the price book for one configuration. It reads whatever the last fetch cached, so pricing works before — and without — a network call.
func (*Catalog) Paid ¶
Paid reports whether any configured candidate bills per token — the answer to "is this machine spending money when it thinks?".
func (*Catalog) Price ¶
Price returns what a model charges. ok is false when nothing prices it, which the caller must report as unpriced rather than as free.
type ChatProvider ¶
type ChatProvider interface {
Chat(ctx context.Context, req *provider.Request) (*provider.Response, error)
}
ChatProvider is the seam the meter wraps: the failover chain, or anything else that answers a request.
type Ledger ¶
type Ledger struct {
// contains filtered or unexported fields
}
Ledger accumulates spend and keeps it on disk, so "what has this cost me" survives a restart. Each call is merged into the file rather than overwriting it: the gateway and a terminal session can both be spending at once, and neither should erase the other's total.
func NewLedger ¶
NewLedger opens the ledger at path, reading whatever is already there. A file that cannot be read starts empty: losing a total is not worth failing startup over.
func (*Ledger) Record ¶
Record bills one provider call to a session and flushes it to disk. A write that fails keeps the call pending, so the next one carries both — worth saying once, because the totals are then only as durable as the process, but not worth saying on every call while the disk stays broken.
type Meter ¶
type Meter struct {
// contains filtered or unexported fields
}
Meter sits between the agent loop and the provider chain. Every call that comes back is priced and billed to the session that made it — including the ones the user never asked for, like compaction — and every call that goes out is checked against the budget first, because the only useful moment to stop is before the money is spent.
func NewMeter ¶
func NewMeter(inner ChatProvider, catalog *Catalog, ledger *Ledger, cfg config.CostConfig) *Meter
NewMeter wires a meter around inner. It stays a pass-through unless tracking is on or a cap is set — a cap with tracking switched off would otherwise be a cap that never counts anything.
func (*Meter) Budget ¶
func (m *Meter) Budget() config.BudgetConfig
Budget returns the caps in force.
func (*Meter) OverviewLine ¶
OverviewLine is the tray row: today against all time, or against the global cap when one is set — the same numbers, said the way the cap makes relevant.
func (*Meter) SessionLine ¶
SessionLine is the status-bar segment: what this conversation has cost, and the cap it is spending against when there is one.
type Snapshot ¶
type Snapshot struct {
Session Totals
Day Totals
Month Totals
Total Totals
Models map[string]Totals
Sessions map[string]Totals
}
Snapshot is everything a report or a status line needs, read at once so the numbers in it agree with each other.
type Tool ¶
type Tool struct{ Meter *Meter }
Tool lets the agent answer "what has this cost?" — for the conversation it is in, for today, for all time — and say how much room is left under a cap before it runs into one.
func (*Tool) Description ¶
func (*Tool) Parameters ¶
type Totals ¶
type Totals struct {
Input int `json:"input_tokens"`
Output int `json:"output_tokens"`
USD float64 `json:"usd"`
Calls int `json:"calls"`
}
Totals is one bucket of spend: what went in, what came out, and what it cost. USD stays at zero for tokens nothing priced, so a report can say "unpriced" instead of implying "free".