symfonic.services.budget.estimation¶
estimation ¶
The offline estimator — one arithmetic rule, in one place.
Before this module the repository had four token estimators that disagreed with
each other. documents/truncation.estimate_tokens floor-divided by four;
triage/tool_routing.estimate_dropped_tokens floor-divided by four over a
different string; prompt/messages_cache._estimate_message_tokens
floor-divided and then added a per-tool-use overhead; the prompt compiler's
default rounded up with a floor of one. Four call sites, three answers for
the same text, and the budgets built on top of them were therefore not
comparable.
The rule here is the compiler's, and the reason is the direction of the error. Rounding down answers zero tokens for a three-character string, so a budget filled with short fragments overflows while the arithmetic insists it fits — a provider 400 at the end of a request that already cost money. Rounding up can only refuse a prompt that would have fitted, which is a cheaper mistake and a visible one.
Nothing here is loaded, cached, or fetched: the estimate is a function of
len(text). That is the entire reason a cold container compiles the same
prompt as a warm one.
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.
Source code in src/symfonic/services/budget/estimation.py
estimate ¶
TokenEstimatorAdapter
dataclass
¶
Exposes any :class:TokenCounter under the prompting port's name.
The one line of glue that lets an adopter's provider-exact counter budget a
compiled prompt: the prompting capability binds estimate, this package
speaks count, and neither imports the other.
count_parts ¶
Total cost of parts counted separately, plus a per-part envelope.
Counting parts separately rather than concatenating them is deliberate and is what makes this a ceiling: a tokenizer merges across a join, so the concatenated cost is never higher. A budget wants the ceiling.
per_part_overhead is the structural cost a part carries beyond its text
— a tool-use block's type, id, and name field. Empty parts are skipped
entirely: an absent block has no envelope either.