Skip to content

yggdrasil.loki.usage

usage

Token accounting — per-model usage, USD pricing, and a spend budget.

Every :class:~yggdrasil.loki.engine.TokenEngine records what it spends into the process-global :data:METER after each completion: input/output tokens keyed by (engine, model). The meter rolls those up per model and globally, prices them against :data:PRICING (USD per million tokens — defaults set here, override per workspace), and enforces an optional budget so an interactive session stops and asks the user to raise the cap rather than spending without bound.

from yggdrasil.loki.usage import METER

METER.set_limit(50_000)          # cap total tokens
...                              # engines record automatically
METER.total().total_tokens       # global tokens so far
METER.total_cost                 # global USD so far
METER.check_budget()             # raises TokenBudgetExceeded when over

ModelPricing dataclass

ModelPricing(input_usd_per_mtok: float, output_usd_per_mtok: float)

USD price per million tokens, split input/output.

ModelUsage dataclass

ModelUsage(
    engine: str,
    model: str,
    calls: int = 0,
    input_tokens: int = 0,
    output_tokens: int = 0,
)

Running consumption for one (engine, model) pair (or the global roll-up).

TokenMeter

TokenMeter()

Accumulates per-model token usage and enforces a cost budget (USD).

Recording never raises — it only counts. Enforcement is explicit: :meth:check_budget raises :class:TokenBudgetExceeded when the USD spend crosses the cap, so the caller (the REPL) can stop between actions and offer to raise it. The cap is money, not tokens — a fixed budget across models of wildly different per-token prices.

total_cost property

total_cost: float

Global USD — summed per row so each is priced at its own rate.

record

record(
    engine: str, model: str, input_tokens: int, output_tokens: int
) -> ModelUsage

Add one completion's tokens to the (engine, model) row.

rows

rows() -> list[ModelUsage]

Per-model usage rows, busiest first.

total

total() -> ModelUsage

The global roll-up across every engine and model.

set_limit

set_limit(usd: Optional[float]) -> None

Set (or clear, with None) the total-spend cap in USD.

raise_limit

raise_limit(by: Optional[float] = None) -> float

Bump the cap by by USD (or one :attr:cost_step); returns the new cap.

remaining

remaining() -> Optional[float]

USD left under the cap (None when uncapped; may go negative).

check_budget

check_budget() -> None

Raise :class:TokenBudgetExceeded if the spend cap is set and crossed.

price_for

price_for(engine: str, model: str) -> ModelPricing

Resolve pricing: exact (engine, model)(engine, "*") → default.

estimate_tokens

estimate_tokens(text: str) -> int

Cheap offline token estimate (~4 chars/token) for when a provider response carries no usage block.