:::info Executable tutorial
This page is generated from a tested Jupyter notebook. Open it in Google Colab or view the source notebook.
:::
Calculate usage costs with the pricing engine
PricingEngine evaluates a validated configuration without a database or network connection. This tutorial prices several UsageMetrics values and inspects the exact-decimal breakdown returned by the same engine used during deduct.
Learning objectives
After completing this tutorial, you can:
- Construct a pricing engine from a validated configuration
- Price metered operations and batches with exact decimals
- Read a cost breakdown and compare rate cards
- Handle usage that has no matching price rule
Prerequisites
- Read the pricing configuration tutorial
- Start the notebook server from
samples/python/notebooks/
Build the engine from the canonical config
PricingEngine.from_dict validates the config and builds the engine in one call — the same validation path publish_config uses. The engine is cheap and stateless; you can build as many as you like, or reuse the facade's one.
from decimal import Decimal
from bursar.engine import PricingEngine
from bursar.metrics import UsageMetrics
from shared import base_config
engine = PricingEngine.from_dict(base_config())
print("engine built from canonical config")
A gpt-4o completion: the math
A completion reports 1,000 input tokens, 500 output tokens, and 200 cached tokens on gpt-4o. The rule matches on model, and its sum charge adds three per_unit components, each computed as measure / unit_size * rate:
0.0025 * 1000 / 1e6 + 0.0100 * 500 / 1e6 + 0.00125 * 200 / 1e6
= 0.0000025 + 0.000005 + 0.00000025 = 0.00000775 credits
The engine quantizes every result to 6 decimal places with ROUND_HALF_UP, so the total you see is 0.000008 — never truncated, never silently rounded down.
completion = UsageMetrics(
operation="completion",
measures={
"input_tokens": Decimal(1000),
"output_tokens": Decimal(500),
"cache_read_tokens": Decimal(200),
},
dimensions={"model": "gpt-4o"},
)
cost = engine.calculate(completion, rate_card="standard")
print("gpt-4o completion total:", cost.total)
A second model, and the expression fallback
gpt-4o-mini matches the same rule, so it is priced at the same per-1M rates: 2,000 input + 800 output tokens costs 0.0025 * 2000 / 1e6 + 0.0100 * 800 / 1e6 = 0.000005 + 0.000008 = 0.000013 credits.
A model with no rule — here mistral-large — falls through to the operation's unmatched policy, which charges the expression input_tokens * 0.005 + output_tokens * 0.015. For 1,000 + 500 tokens that is 5 + 7.5 = 12.5 credits. This is your "any new model is priced, even before we publish a rule for it" backstop.
mini = UsageMetrics(
operation="completion",
measures={"input_tokens": Decimal(2000), "output_tokens": Decimal(800)},
dimensions={"model": "gpt-4o-mini"},
)
print("gpt-4o-mini total:", engine.calculate(mini, rate_card="standard").total)
unknown = UsageMetrics(
operation="completion",
measures={"input_tokens": Decimal(1000), "output_tokens": Decimal(500)},
dimensions={"model": "mistral-large"},
)
print("unknown model total:", engine.calculate(unknown, rate_card="standard").total)
Execution jobs, and a batch
The execution operation bills jobs at a flat 0.04 credits per job when model is gpt-4o — compute_seconds is metered but not priced, a measure the operation accepts and the rule ignores. Three jobs cost 3 * 0.04 = 0.12 credits.
calculate_batch prices many events in one call and returns one CostBreakdown each — the pattern for a nightly re-pricing job or a usage report.
execution = UsageMetrics(
operation="execution",
measures={"jobs": Decimal(3), "compute_seconds": Decimal(120)},
dimensions={"model": "gpt-4o"},
)
print("3 jobs total:", engine.calculate(execution, rate_card="standard").total)
batch = engine.calculate_batch([completion, mini, execution], rate_card="standard")
print("batch totals:", [str(item.total) for item in batch])
The breakdown, and rate cards per plan
Every result carries a breakdown dict describing how the total was produced: the operation, the rate card that priced it, the charge type that won, and the exact measures and dimensions that were used. This is the audit trail for "why did this cost what it did?".
Rate cards are bound to plans, not to users: get_rate_card_for_plan("pro") resolves the plan's configured card. Both free and pro use standard; an unknown plan resolves to None, meaning the caller must decide how to price it.
cost = engine.calculate(completion, rate_card="standard")
print("total:", cost.total)
print("breakdown keys:", sorted(cost.breakdown.keys()))
print("charge type:", cost.breakdown["charge_type"])
print("rate card:", cost.breakdown["rate_card"])
print("measures:", cost.breakdown["measures"])
print("dimensions:", cost.breakdown["dimensions"])
print("rate card for 'pro':", engine.get_rate_card_for_plan("pro"))
print("rate card for 'free':", engine.get_rate_card_for_plan("free"))
print("rate card for unknown plan:", engine.get_rate_card_for_plan("enterprise"))
Errors: unpriced work is refused
The engine is strict where it counts. execution has unmatched: reject, so a job on a model without a rule is a ConfigError, not a silent free ride. And an operation that does not exist in the config at all — say transcription — fails before any pricing logic runs. Both are caught below.
from bursar.config import ConfigError
try:
engine.calculate(
UsageMetrics(
operation="execution",
measures={"jobs": Decimal(1)},
dimensions={"model": "llama-3"},
),
rate_card="standard",
)
except ConfigError as error:
print("unpriced execution ->", type(error).__name__, "|", error)
try:
engine.calculate(
UsageMetrics(operation="transcription", measures={"seconds": Decimal(60)}),
rate_card="standard",
)
except ConfigError as error:
print("unknown operation ->", type(error).__name__, "|", error)