Splitting one context window across the kinds of content that share it.
Prompt, tools, and memory compete for the same window, and before this module
each of them decided its own allowance from its own config knob. Three
independent ceilings that were never checked against each other add up to more
than the window whenever the workload is unusual โ which is precisely when the
window matters.
Two rules make the split safe. Shares round down: three lines at one third
of a hundred are 33 each, not 34, because rounding up three lines is how a
100% split ships at 101%. The remainder is reported, not redistributed:
unallocated is visible in the plan, so an operator can see the two tokens
nobody claimed instead of wondering which kind quietly absorbed them.
The plan is emitted in :class:~.contracts.BudgetKind ladder order rather than
in declaration order, so two configurations that mean the same split are the
same plan โ which is what lets a golden corpus compare them at all.
allocate
allocate(policy: BudgetPolicy) -> BudgetPlan
Derive the per-kind ceilings policy implies.
A pure function of the policy: no clock, no environment, no counter. The
plan for a given policy is the same on every machine and in every process,
which is what makes it comparable across a deploy.
Source code in src/symfonic/services/budget/allocation.py
| def allocate(policy: BudgetPolicy) -> BudgetPlan:
"""Derive the per-kind ceilings ``policy`` implies.
A pure function of the policy: no clock, no environment, no counter. The
plan for a given policy is the same on every machine and in every process,
which is what makes it comparable across a deploy.
"""
usable = policy.usable
minimums = sum(line.minimum for line in policy.lines)
if minimums > usable:
raise BudgetPolicyError(
f"declared minimums total {minimums} tokens but only {usable} are usable "
f"({policy.context_window} window less {policy.output_reserve} reserved "
"for output). Lower a minimum or widen the window."
)
limits: dict[BudgetKind, int] = {}
for line in policy.lines:
limits[line.kind] = max(line.minimum, int(usable * line.share))
allocated = sum(limits.values())
if allocated > usable:
forced = ", ".join(
f"{kind.value}={limit}" for kind, limit in sorted(
limits.items(), key=lambda item: item[0].value
)
)
raise BudgetPolicyError(
f"per-kind minimums raise the total allocation to {allocated} tokens "
f"against {usable} usable ({forced}). Minimums are floors, not "
"priorities: the budget cannot honour all of them at once."
)
allocations = tuple(
BudgetAllocation(kind=kind, limit=limits[kind])
for kind in BudgetKind
if kind in limits
)
return BudgetPlan(
context_window=policy.context_window,
output_reserve=policy.output_reserve,
usable=usable,
allocations=allocations,
unallocated=usable - allocated,
)
|