PricingEngine
The PricingEngine is the pure calculation core. It evaluates pricing expressions against usage metrics. No database dependency — can be used standalone.
Creating an Engine
from ducto import PricingEngine
# From a dict (testing, stateless)
engine = PricingEngine.from_dict({
"version": 1,
"models": {"_default": "input_tokens * 0.001 + output_tokens * 0.003"},
})
# From a config file
engine = PricingEngine.from_yaml("pricing.yaml")
Methods
calculate(metrics: UsageMetrics) -> CostBreakdown
Evaluate pricing expressions against usage metrics.
result = engine.calculate(
UsageMetrics(model="gpt-4", input_tokens=500, output_tokens=200)
)
print(f"Total: {result.total}")
print(f"Model credits: {result.model_credits}")
calculate_batch(metrics: list[UsageMetrics]) -> list[CostBreakdown]
Evaluate multiple metrics at once.
resolve_model(model_version: str) -> str | None
Resolve a model version string against configured model names.
has_model(model_name: str) -> bool
Check if a specific model is configured.
get_fixed_cost(job_name: str) -> int | None
Get the fixed cost for a named job.
pricing_schema() -> PricingConfigData
Get the raw pricing config dict that the engine was initialized with.
CostBreakdown
Returned by calculate():
| Field | Type | Description |
|---|---|---|
total | float | Total credits |
model_credits | float | Credits from model expression |
tool_credits | float | Credits from tool expressions |
search_credits | float | Credits from search expressions |
cache_credits | float | Credits from cache expression (negative = savings) |
fixed_credits | int ` | None` |
metadata | dict | Debug info: expressions evaluated, values |
UsageMetrics
Input to calculate():
| Field | Type | Default |
|---|---|---|
model | str | "_default" |
input_tokens | int | 0 |
output_tokens | int | 0 |
cache_read_tokens | int | 0 |
cache_write_tokens | int | 0 |
tool_calls | list[ToolCall] | [] |
search_queries | int | 0 |
search_results | int | 0 |
web_search_calls | int | 0 |
code_exec_calls | int | 0 |
fixed_job | str ` | None` |
Expression Safety
The engine uses Python's ast module with a strict allowlist:
- Parses
ast.parse(expr, mode="eval") - Walks the AST — only ~25 node types allowed (binary ops, comparisons, conditionals, etc.)
- Function calls whitelisted:
ceil,floor,min,max,round,if,tier,clamp,percentile - Rejects: attributes (
x.__class__), subscripts (x[0]), lambdas, comprehensions, imports - Evaluation namespace has
__builtins__emptied - All expressions validated at config load time