symfonic.core.observability.budget_models¶
budget_models ¶
Shared data types + protocols for the budget tracker.
Split from :mod:symfonic.core.observability.budget to keep each module
under the 300-LOC cap. The concrete :class:TokenBudgetTracker lives
next door and imports these types.
BudgetCheckResult
dataclass
¶
BudgetCheckResult(allowed: bool, reason: str | None = None, daily_used_usd: float = 0.0, daily_limit_usd: float | None = None, monthly_used_usd: float = 0.0, monthly_limit_usd: float | None = None)
Result of a pre-call budget check.
BudgetStore ¶
Bases: Protocol
Optional persistence layer for daily aggregates.
Implementations should be idempotent on conflict — the tracker calls
upsert_daily after every LLM call, so the underlying storage must
handle UPSERT semantics (ON CONFLICT ... DO UPDATE).
The optional read_today / read_month methods hydrate the
tracker's in-memory buckets on demand so that multiple app replicas
converge on the same view of a tenant's cumulative usage. A store
that only implements upsert_daily still works (writes only) —
multi-replica correctness requires all three.
read_month
async
¶
Return the aggregated month-to-date usage for tenant_id.
year_month is the YYYY-MM key produced by
:func:month_key. Optional — see :meth:read_today.
Source code in src/symfonic/core/observability/budget_models.py
read_today
async
¶
Return today's usage row for tenant_id (None if absent).
Optional — implementations without this method get write-through semantics only (single-process correctness).
Source code in src/symfonic/core/observability/budget_models.py
BudgetUsage
dataclass
¶
BudgetUsage(tenant_id: str, window: str, window_key: str, input_tokens: int = 0, output_tokens: int = 0, cached_tokens: int = 0, cache_write_tokens: int = 0, cost_usd: float = 0.0, request_count: int = 0, updated_at: datetime = (lambda: datetime.now(UTC))())
Cumulative usage for a single (tenant, window) pair.
copy_usage ¶
Snapshot a bucket so callers can't mutate tracker state.