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.