Skip to content

symfonic.agent.cutover.budget

budget

Binding the budget service to the prompt compiler, in the composition root.

Both halves of this seam were written, tested, and shipped unreachable. The prompting capability budgets through a bound TokenEstimator and refuses to resolve one itself (capabilities/prompting/compiler.py:_estimator); the budget service produces exactly that object and says so in its own package docstring — "the seam to the prompting capability is BudgetService.as_token_estimator". Neither may import the other: capability and runtime-service are no in both directions on the dependency matrix, so the two protocols meet as identical shapes and something else has to hand one to the other. That something is a composition root, and until this module there was none — which is why RCH-1 counted symfonic.services.budget among the packages nothing imports.

Two deployments, two bindings, one arithmetic rule:

  • No declared window. The counter alone. It is the same rounding the compiler's private default already used, so nothing about the compiled prompt changes — what changes is whose rule it is: the package that exists because four estimators disagreed now owns the answer for the migrated path too. No ceiling is derived, because inventing a context_window the adopter never declared would silently truncate prompts that fit.
  • A declared window and a prompt share. The service resolves the counter and derives the plan, and the compiler runs under the ceiling that plan gives the prompt line. This is the only path that can drop a contribution, and it runs only when the deployment asked for it in both halves.

A share without a window is refused rather than ignored: a fraction of an unknown number is not a ceiling, and a composition root that accepted one would report a budget it never applied.

prompt_compile_options

prompt_compile_options(*, context_window: int | None = None, prompt_share: float | None = None, chars_per_token: int | None = None) -> dict[str, Any]

The compile options that bind this deployment's budgeting.

Returned as options for PromptingCapability rather than applied here: the capability owns the compile, and a composition root that pre-compiled for it would be a second compiler.

Source code in src/symfonic/agent/cutover/budget.py
def prompt_compile_options(
    *,
    context_window: int | None = None,
    prompt_share: float | None = None,
    chars_per_token: int | None = None,
) -> dict[str, Any]:
    """The compile options that bind this deployment's budgeting.

    Returned as options for ``PromptingCapability`` rather than applied here:
    the capability owns the compile, and a composition root that pre-compiled
    for it would be a second compiler.
    """
    if context_window is None:
        if prompt_share is not None:
            raise ValueError(
                "fold_retrieval_bundle(prompt_share=...) needs a context_window: "
                "a share is a fraction of a window, and a fraction of an unknown "
                "number is not a ceiling. Declare the model's window, or drop the "
                "share and compile unbounded."
            )
        counter = (
            HeuristicTokenCounter(chars_per_token=chars_per_token)
            if chars_per_token is not None
            else HeuristicTokenCounter()
        )
        return {"estimator": counter}

    service = BudgetService.offline(
        context_window=context_window,
        prompt=prompt_share,
        chars_per_token=chars_per_token,
    )
    options: dict[str, Any] = {"estimator": service.as_token_estimator()}
    limit = service.limit_for(BudgetKind.PROMPT)
    if limit is not None:
        # ``None`` when no share was declared, and that is not a ceiling of
        # zero: an undeclared line is unbudgeted, so the compile stays
        # unbounded and the counter's numbers are a report rather than a gate.
        options["budget"] = PromptBudget(max_total_tokens=limit)
    return options