symfonic.services.budget.overflow¶
overflow ¶
Overflow policy: what happens to content that does not fit.
Three answers, and which one is right depends on what the content is — which is why it is declared per line rather than chosen here. Dropping a memory section costs recall the model can live without; truncating a tool manifest produces a half-parsed schema; refusing to build is the only honest response when the operator's own instructions are what overflowed.
Whichever action applies, two properties hold. Order is total: items are
processed by (order, item_id), and dropping walks that order reversed —
whatever renders last is whatever goes first — so the same input always yields
the same output and a byte-comparable corpus is possible. Pinned content is
never dropped and never truncated, and pinned content that alone exceeds the
ceiling fails under every action, including TRUNCATE. That is the same rule
the prompt compiler holds for pinned contributions, deliberately: a context
missing its boundaries is worse than a context that refused to build, and two
layers that disagreed about it would produce a prompt whose pinned half
survived one budget and not the other.
BudgetItem
dataclass
¶
One admission candidate: identity, kind, content, position, pinned-ness.
An item declares what it is, never what it costs or where it belongs in
the budget. Cost is measured here with the resolved counter so two items
cannot be measured by two different rules, and position comes from
order broken by item_id so the sequence is totally ordered even when
two callers pick the same number.
apply_overflow ¶
apply_overflow(items: Sequence[BudgetItem], limit: int, action: OverflowAction, counter: TokenCounter, truncation: TruncationPolicy | None = None) -> OverflowResult
Fit items into limit tokens under action.
Returns the survivors in the order they arrived — only membership and content are decided here. Reordering content to make it fit would change what the model reads for a reason the operator never declared.