Skip to content

symfonic.platform.prompt_budget

prompt_budget

Bind prompt accounting in a native AgentHost composer.

This is prompt compilation, distinct from :mod:symfonic.platform.budget's invocation admission ledger. A declared window and prompt share grant a ceiling; omitting either leaves the prompt unbounded (a share alone is refused).

For example, inside AgentHost(composer=compose, resources=resources)::

def compose(scope, resources):
    return Agent(resources.provider, capabilities=[
        PromptingCapability(
            sources=resources.prompt_sources(scope),
            options=prompt_compile_options(context_window=8192, prompt_share=0.5),
        ),
    ])

The shared facade compiler binds service ports to capability values. Keeping that binding outside cutover lets native hosts compose it after cutover removal, while the compatibility root can reuse it without importing the platform.

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/prompt_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(
                "prompt_compile_options(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