symfonic.services.budget.ports¶
ports ¶
The declared port of the budget service (LAY-ADR §2, port).
Same cell, same rule as :mod:symfonic.services.models.ports and
:mod:symfonic.services.shadow.ports: facade-compiler → runtime-service is
port, so the composition root in :mod:symfonic.agent.cutover may compose
this service only through a declared narrow interface, never through
:mod:~symfonic.services.budget.service or the pure functions behind it.
The three names below are exactly what a composition root needs to bind the seam
this package's docstring declared and left open — "the seam to the prompting
capability is :meth:~symfonic.services.budget.service.BudgetService.as_token_estimator
": one counter to price every prompt region, and the derived per-kind ceiling
when the deployment declared a window and a share to split it by.
:class:~symfonic.services.budget.estimation.HeuristicTokenCounter is here for
the deployment that declared no window. It answers estimate as well as
count, so the composition root can bind the package's one arithmetic rule
without inventing a context_window the adopter never asked for — the
alternative being the fourth private estimator this package exists to retire.
Nothing here is defined locally; every name is re-exported from its owning module.
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.
BudgetService
dataclass
¶
Counting, budgets, truncation, and overflow for one invocation.
as_token_estimator ¶
This service's counter, under the prompting capability's port name.
The seam the prompt compiler declared and left open: it budgets through
a bound TokenEstimator and refuses to resolve one itself, so exact
counting reaches prompt assembly without the capability layer importing
a runtime service — an edge its row of the dependency matrix forbids.
Source code in src/symfonic/services/budget/service.py
count ¶
fit ¶
fit(items: Sequence[BudgetItem], kind: BudgetKind, *, action: OverflowAction | None = None, truncation: TruncationPolicy | None = None) -> OverflowResult
Fit items into kind's allowance under kind's overflow action.
Items of another kind are refused rather than budgeted: charging tool manifests against the memory line produces a plan whose arithmetic is right and whose meaning is wrong, and nothing downstream would notice.
Source code in src/symfonic/services/budget/service.py
limit_for ¶
offline
classmethod
¶
offline(*, context_window: int, output_reserve: int = 0, prompt: float | None = None, tools: float | None = None, memory: float | None = None, chars_per_token: int | None = None) -> BudgetService
The zero-configuration constructor: a window, optional shares, no network.
Deliberately does not accept a registry or a counting mode. An adopter
who wants exact counting is making a decision with operational
consequences, and that decision belongs in an explicit
:class:~.contracts.BudgetPolicy rather than in a convenience helper.
Source code in src/symfonic/services/budget/service.py
truncate ¶
truncate(text: str, *, max_tokens: int | None = None, kind: BudgetKind | None = None, truncation: TruncationPolicy | None = None) -> TruncationResult
Cut text to an explicit ceiling, or to kind's derived one.
Source code in src/symfonic/services/budget/service.py
HeuristicTokenCounter
dataclass
¶
Characters-per-token arithmetic — the offline default.
Named an estimate because that is what it is. A provider-exact counter is
a legitimate substitution, but it arrives as a registered port
(:mod:.registry) rather than as a download this module performs.
Implements both port spellings: count for this package and estimate
for the prompting capability's :class:TokenEstimator, so one object can be
handed to either without an adapter or an import edge.
count ¶
Tokens in text, rounded up; empty text costs nothing.
The floor of one for non-empty text matters more than it looks: without
it a very large chars_per_token makes short strings free, and a
budget filled with free strings has no ceiling at all.