NAME
Langertha::Reasoning::BudgetPolicy - Invented level<->token-budget interpolation, clamped to a Profile's enforced bounds
VERSION
version 0.503
SYNOPSIS
my $policy = Langertha::Reasoning::BudgetPolicy->new(
profile => Langertha::Reasoning::Profile->for_model('gemini-2.5-pro'),
form => 'range', # interpolate across the profile's [min,max]
levels => [qw( low medium high )],
);
my $budget = $policy->budget_for('medium'); # ~16448, always within 128..32768
my $level = $policy->level_for(4096); # nearest anchor level
# explicit anchors instead of a curve
my $curated = Langertha::Reasoning::BudgetPolicy->new(
profile => Langertha::Reasoning::Profile->for_model('gemini-2.5-flash'),
points => { none => 0, low => 2048, medium => 8192, high => 24576 },
);
DESCRIPTION
Category (c) of karr k173's three-category reasoning taxonomy (ADR 0023): the invented level-to-token-budget interpolation and its inbound inverse. No provider publishes an official reasoning-level to token-budget mapping — OpenAI and Anthropic effort is adaptive and undocumented, and the only numeric level-to-token formulas in the wild are OpenRouter's, labeled OpenRouter's own convention. So every number this class emits is library convention, not wire-truth, and "source" should say so honestly.
Because the numbers are invented, they must never be able to emit a value the API rejects. Every numeric output of this class is clamped to the owning Langertha::Reasoning::Profile's category-(b) enforced bounds ("budget_min" in Langertha::Reasoning::Profile / "budget_max"). This is the firewall rule: convention (c) may read the profile's enforced bounds (b) but can never cross them. Putting those bounds inside this convention layer — as the original karr k173 ticket proposed — would let an override silently violate a hard API bound; keeping them in the Profile and clamping against them here is the single most important correction of the design.
This is not a capability and is not default-shipped for effort providers. It exists only where a downstream consumer needs budget<->level conversion and constructs it explicitly. Nothing in Langertha wires it in by default.
profile
The owning Langertha::Reasoning::Profile. Its category-(b) bounds ("budget_min" in Langertha::Reasoning::Profile / "budget_max") are the firewall every numeric output is clamped to. A range-form policy needs both bounds present (there is nothing to interpolate across otherwise).
form
range (interpolate a curve across the profile's [budget_min, budget_max]) or explicit (look the budget up in curated "points"). Defaults to explicit when "points" is given, otherwise range.
curve
For form => 'range': linear (even spacing) or log (geometric spacing, finer at the low end). Ignored for explicit. Default linear; per karr k173 q3 the curve is convention, so log is offered but not asserted as truth.
points
For form => 'explicit': a curated map of reasoning level to token budget (the invented anchors). Keys must be members of the normalized vocabulary (none|minimal|low|medium|high|xhigh|max). Each looked-up budget is still clamped to the profile's (b) bounds, so a curated anchor outside them can never reach the wire.
levels
The quantization anchor ladder (ascending) this convention spans — the consumer-side anchors that deliberately do not live in the Profile (whose levels is empty for a budget control, keeping it pure wire-truth). Defaults to the profile's levels when it has any, to the sorted keys of "points" for an explicit policy, else to low|medium|high.
default_bool_level
The normalized level a boolean-control provider's "thinking on" (think:true) corresponds to for this convention, the boolean-to-level half of the inbound bijection (its "off" counterpart is none). Default medium.
source
The honest curation receipt. Because no provider publishes a level-to-budget table, this defaults to a string that says the numbers are convention rather than wire-truth; a consumer curating empirical anchors should replace it with what it measured and when.
budget_for
my $tokens = $policy->budget_for('medium');
Convert a normalized reasoning level to an integer token budget. In explicit form the curated "points" anchor is used (the nearest anchor by ordinal when the exact level is absent); in range form the "curve" is interpolated across the profile's [budget_min, budget_max]. The result is always clamped to the profile's category-(b) bounds — the firewall — so an invented number can never reach the wire above the ceiling or below the floor.
level_for
my $level = $policy->level_for(4096);
The inbound inverse: convert an integer token budget to the nearest normalized level on the anchor ladder. The budget is clamped to the profile's (b) bounds first, so an out-of-range budget maps to the boundary level rather than off the ladder; a budget equal to the profile's off_value maps to none.
for_model
my $policy = Langertha::Reasoning::BudgetPolicy->for_model(
'gemini-2.5-pro', form => 'range' );
Convenience constructor mirroring "for_model" in Langertha::Reasoning::Profile: resolves the id to its profile and passes the remaining convention options ("form", "curve", "points", "levels", "source") through to "new".
SEE ALSO
Langertha::Reasoning::Profile - The wire-truth categories (a)+(b) whose bounds this convention (c) is clamped to
Langertha::Reasoning - The value object that resolves and consumes profiles for outbound serialization
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.