symfonic.services.budget.contracts¶
contracts ¶
The budget policy contracts: what an adopter declares, and what it may not.
A policy declares intent — how large the window is, how much of it each kind
of content may claim, how exact the counting must be, what to do when content
does not fit. It never declares a number of tokens for a specific block: that
is derived, in one place (:mod:.allocation), from the window and the shares.
The distinction is what makes two deployments with the same policy produce the
same split, and what stops a capability from quietly voting itself a larger
allowance than the operator granted.
The two ports are deliberately tiny. :class:TokenCounter is one method over a
string, so a provider's exact tokenizer can be bound without this package
knowing anything about that provider. :class:TokenEstimator is the same
method under the name the prompting capability already uses — declared here so
the two packages can be wired together without an import edge in either
direction, which is what their rows of the dependency matrix require.
BudgetKind ¶
Bases: Enum
The kinds of content that share a context window.
Declared in ladder order: iteration order is the canonical order of an allocation plan, so two policies that mean the same split are the same plan regardless of the order their lines were written in.
BudgetLine
dataclass
¶
BudgetLine(kind: BudgetKind, share: float, minimum: int = 0, overflow: OverflowAction = OverflowAction.TRUNCATE)
One kind's claim on the usable window.
share is a fraction of the usable window (the context window less the
output reserve), and minimum is a floor in tokens for the case where a
proportional share of a small window would round down to something useless.
BudgetPolicy
dataclass
¶
BudgetPolicy(context_window: int, output_reserve: int = 0, lines: tuple[BudgetLine, ...] = (), mode: CountingMode = CountingMode.HEURISTIC, provider: str = '', chars_per_token: int = DEFAULT_CHARS_PER_TOKEN, truncation: TruncationPolicy = TruncationPolicy())
The whole budgeting intent for one invocation.
Frozen and self-validating: every impossible combination is refused at
construction, so a policy object in hand is a policy that can be satisfied
by arithmetic — the only remaining way to fail is content that does not
fit, which is :class:~.errors.BudgetOverflowError and a different type.
line_for ¶
The declared line for kind, or None when it has no claim.
CountingMode ¶
Bases: Enum
How exact the counting has to be.
EXACT fails when no counter is registered for the provider; that is the
difference between it and EXACT_PREFERRED, and the whole reason both
exist. An operator reconciling invoices needs the failure; an operator who
merely prefers precision needs the fallback, plus a diagnostic saying it
happened.
ExactCounterSpec
dataclass
¶
An adopter's provider-exact counter, offered for registration.
offline is an assertion the registrant makes and the registry enforces:
a counter that would fetch an encoding on first use is refused at
registration, on the machine that wrote the config, rather than at 3am on
the container that has no egress.
Keep ¶
Bases: Enum
Which end of an over-long string survives truncation.
OverflowAction ¶
Bases: Enum
What to do when a kind's content exceeds its allowance.
TokenCounter ¶
Bases: Protocol
Counts the tokens in a string.
Implementations must be pure (same string, same number) and monotone non-decreasing in prefix length — truncation's binary search assumes the second, and its shrink loop survives an implementation that breaks it.
TokenEstimator ¶
Bases: Protocol
The prompting capability's spelling of the same port.
symfonic.capabilities.prompting binds an estimator by structure, not by
import: capability and runtime-service may not import each other, so the
two protocols meet as identical shapes rather than as a shared base class.
TruncationPolicy
dataclass
¶
How an over-long string is cut down.
The marker is mandatory. A silent elision is a defect rather than a style choice: neither the model nor the operator reading a transcript can tell truncated content from content that was always that short, and the two lead to opposite debugging conclusions.