symfonic.services.cost.values¶
values ¶
The one budget decision shape (BUD-1).
Two enforcement points are deliberate โ the platform admits a request, the invocation re-checks before its own effects. Two decision shapes are not: the moment admission and re-check disagree about what "allowed" means, one of them is wrong and nobody can tell which.
BudgetDecision
dataclass
¶
BudgetDecision(allowed: bool, reason: str | None = None, code: str = 'budget_ok', daily_used_usd: float = 0.0, daily_limit_usd: float | None = None, monthly_used_usd: float = 0.0, monthly_limit_usd: float | None = None, retry_after_seconds: int = 3600)
Allowed or not, plus a stable code a transport can map on.
code is the field BUD-2 adds and the reason this type exists at all.
The shipped path raises a plain HTTPException whose message begins
"Budget exceeded:", and three routers branch on that prefix โ so a
reworded or translated message turns a 429 into a 500. A caller that reads
code cannot be broken by editing prose.
CostSummary
dataclass
¶
CostSummary(scope_key: str, window: str, cost_usd: float = 0.0, input_tokens: int = 0, output_tokens: int = 0, calls: int = 0)
A projection of runtime-service accounting (BUD-6).
A read model: it carries numbers somebody else computed. The platform performs no accounting arithmetic of its own, because two places that both know how to add up a bill will eventually disagree about a rounding rule.