use super::trim::{
estimate_request_tokens, estimate_tokens, estimate_value_tokens, protected_prompt_head_len,
};
use super::{budget, prompt_read, RoundObservation};
pub(super) fn exposure_budget_tokens(
send_budget: Option<usize>,
safe_context: Option<u32>,
) -> Option<usize> {
send_budget.or_else(|| safe_context.map(|s| s as usize))
}
pub(super) fn authoritative_request_budget(
send_budget: Option<usize>,
send_budget_authoritative: bool,
token_threshold: Option<usize>,
) -> Option<usize> {
let send = send_budget_authoritative.then_some(send_budget).flatten();
match (send, token_threshold.filter(|budget| *budget > 0)) {
(Some(a), Some(b)) => Some(a.min(b)),
(a, b) => a.or(b),
}
}
pub(super) fn count_guard_has_headroom(
current_tokens: usize,
authoritative_budget: Option<usize>,
max_ok_input: Option<u32>,
) -> bool {
authoritative_budget.is_some()
|| max_ok_input.is_some_and(|accepted| current_tokens <= accepted as usize)
}
pub(super) fn capped_accepted_prompt_tokens(
accepted_prompt_tokens: u32,
declared_ceiling: Option<usize>,
) -> usize {
(accepted_prompt_tokens as usize).min(declared_ceiling.unwrap_or(usize::MAX))
}
pub(super) fn preflight_irreducible_request(
messages: &[serde_json::Value],
tools: Option<&serde_json::Value>,
authoritative_budget: Option<usize>,
calibration: f32,
estimation: crate::tokens::TokenEstimation,
model: &str,
) -> anyhow::Result<()> {
let Some(budget) = authoritative_budget else {
return Ok(());
};
let head = protected_prompt_head_len(messages, prompt_read::ACTIVE_PROMPT_PREFIX);
let newest_live_user = messages[head..]
.iter()
.rev()
.find(|message| message["role"].as_str() == Some("user"));
let estimated = estimate_request_tokens(&messages[..head], tools, estimation)
+ newest_live_user
.map(|message| estimate_value_tokens(message, estimation))
.unwrap_or(0);
let required = calibrate_up(estimated, calibration);
if required > budget {
anyhow::bail!(
"the exact active prompt, live user presentation, and required request scaffolding \
need ~{required} input \
tokens (including advertised tool schemas), which cannot fit model `{model}`'s \
authoritative {budget}-token input budget; refusing before inference dispatch — \
the operator prompt was not truncated"
);
}
Ok(())
}
fn full_message_request_real_tokens(
messages: &[serde_json::Value],
tools: Option<&serde_json::Value>,
calibration: f32,
estimation: crate::tokens::TokenEstimation,
) -> usize {
calibrate_up(
estimate_request_tokens(messages, tools, estimation),
calibration,
)
}
pub(super) fn full_message_request_pressure_tokens(
tracked_tokens: usize,
wire_messages: &[serde_json::Value],
tools: Option<&serde_json::Value>,
calibration: f32,
estimation: crate::tokens::TokenEstimation,
) -> usize {
tracked_tokens.max(full_message_request_real_tokens(
wire_messages,
tools,
calibration,
estimation,
))
}
pub(super) fn preflight_full_message_request(
messages: &[serde_json::Value],
tools: Option<&serde_json::Value>,
authoritative_budget: Option<usize>,
calibration: f32,
estimation: crate::tokens::TokenEstimation,
model: &str,
) -> anyhow::Result<()> {
let Some(budget) = authoritative_budget else {
return Ok(());
};
let required = full_message_request_real_tokens(messages, tools, calibration, estimation);
if required > budget {
anyhow::bail!(
"the complete inference request needs ~{required} input tokens, which cannot fit \
model `{model}`'s authoritative {budget}-token input budget; refusing before \
inference dispatch — the exact operator prompt and tool results were not truncated"
);
}
Ok(())
}
pub(super) fn estimate_responses_request_tokens(
instructions: Option<&str>,
input: &[serde_json::Value],
tools: Option<&[serde_json::Value]>,
estimation: crate::tokens::TokenEstimation,
) -> usize {
let instructions_tokens = instructions
.map(|text| {
estimate_value_tokens(
&serde_json::json!({"role": "system", "content": text}),
estimation,
)
})
.unwrap_or(0);
let input_tokens = estimate_tokens(input, estimation);
let tool_tokens = tools
.map(|tools| estimate_value_tokens(&serde_json::Value::Array(tools.to_vec()), estimation))
.unwrap_or(0);
instructions_tokens + input_tokens + tool_tokens
}
pub(super) fn estimate_responses_request_real_tokens(
instructions: Option<&str>,
input: &[serde_json::Value],
tools: Option<&[serde_json::Value]>,
estimation: crate::tokens::TokenEstimation,
calibration: f32,
) -> usize {
calibrate_up(
estimate_responses_request_tokens(instructions, input, tools, estimation),
calibration,
)
}
pub(super) fn responses_context_remaining_report(
instructions: Option<&str>,
input: &[serde_json::Value],
tools: Option<&[serde_json::Value]>,
budget_state: &ResponsesBudgetState,
calibration: f32,
estimation: crate::tokens::TokenEstimation,
low_budget_pct: usize,
) -> String {
let used_real =
estimate_responses_request_real_tokens(instructions, input, tools, estimation, calibration);
budget::render_context_budget(
used_real,
budget_state.actionable_input_budget(),
budget_state.num_ctx(),
budget_state.input_ceiling_pct(),
low_budget_pct,
)
}
pub(super) fn preflight_responses_request(
instructions: Option<&str>,
input: &[serde_json::Value],
tools: Option<&[serde_json::Value]>,
authoritative_budget: Option<usize>,
calibration: f32,
estimation: crate::tokens::TokenEstimation,
model: &str,
) -> anyhow::Result<()> {
let Some(budget) = authoritative_budget else {
return Ok(());
};
let required =
estimate_responses_request_real_tokens(instructions, input, tools, estimation, calibration);
if required > budget {
anyhow::bail!(
"the Responses request needs ~{required} input tokens, which cannot fit model \
`{model}`'s authoritative {budget}-token input budget; refusing before inference \
dispatch — the exact operator prompt and function outputs were not truncated"
);
}
Ok(())
}
pub(super) fn context_window_split(
window: u32,
input_limit: usize,
output_reserve: u32,
) -> (usize, u32) {
let input = input_limit.min(window.saturating_sub(output_reserve) as usize);
(input, window - input as u32)
}
pub(super) fn num_ctx_input_ceiling(
num_ctx: Option<u32>,
input_ceiling_pct: u32,
max_output_tokens: Option<u32>,
) -> Option<usize> {
num_ctx.map(|context_window| {
let percentage_ceiling =
crate::config::input_percentage_ceiling(context_window, input_ceiling_pct) as usize;
context_window_split(
context_window,
percentage_ceiling,
max_output_tokens.unwrap_or(0),
)
.0
})
}
pub(super) fn resolve_responses_budget(
num_ctx: Option<u32>,
safe_context: Option<u32>,
max_ok_input: Option<u32>,
mid_loop_trim_tokens: Option<usize>,
input_ceiling_pct: u32,
cognition: Option<crate::role_profile::Cognition>,
output_allowance: Option<u32>,
) -> ResponsesBudgetState {
ResponsesBudgetState::new(
num_ctx,
input_ceiling_pct,
super::generation_policy::resolve_output_allowance(output_allowance, cognition),
max_ok_input,
safe_context,
mid_loop_trim_tokens,
)
}
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn initial_context_input_budget(
kind: crate::BackendKind,
api: crate::OpenAiApi,
context_window: Option<u32>,
input_ceiling_pct: u32,
cognition: Option<crate::role_profile::Cognition>,
output_allowance: Option<u32>,
chat_capability: crate::model_card::ChatCompletionsCapability,
reasoning_replay_scope: crate::model_card::ReasoningReplayScope,
max_ok_input: Option<u32>,
safe_context: Option<u32>,
) -> Option<u32> {
if kind == crate::BackendKind::Openai && api == crate::OpenAiApi::Responses {
return resolve_responses_budget(
context_window,
safe_context,
max_ok_input,
None,
input_ceiling_pct,
cognition,
output_allowance,
)
.soft_send_budget()
.map(|budget| u32::try_from(budget).expect("input budgets originate as u32 values"));
}
let policy_allowance = || {
super::generation_policy::GenerationPolicy::resolve(
cognition,
output_allowance,
chat_capability,
reasoning_replay_scope,
)
.output_allowance
};
let output_allowance = match kind {
crate::BackendKind::Openai if api == crate::OpenAiApi::ChatCompletions => {
policy_allowance()
}
crate::BackendKind::Anthropic => {
Some(policy_allowance().unwrap_or_else(super::anthropic_wire::default_max_tokens))
}
_ => output_allowance,
};
let ceiling = num_ctx_input_ceiling(context_window, input_ceiling_pct, output_allowance);
initial_send_budget(max_ok_input, safe_context, ceiling)
.map(|budget| u32::try_from(budget).expect("input budgets originate as u32 values"))
}
pub(super) fn recovered_input_budget(
context_window: u32,
input_ceiling_pct: u32,
max_output_tokens: Option<u32>,
declared_ceiling: Option<usize>,
) -> usize {
let recovered =
num_ctx_input_ceiling(Some(context_window), input_ceiling_pct, max_output_tokens)
.expect("a known context window always resolves an input ceiling");
declared_ceiling.map_or(recovered, |declared| recovered.min(declared))
}
pub(super) fn initial_send_budget(
max_ok_input: Option<u32>,
safe_context: Option<u32>,
input_ceiling: Option<usize>,
) -> Option<usize> {
let cached = match (max_ok_input, safe_context) {
(Some(m), Some(s)) => Some(m.max(s) as usize),
(m, s) => m.or(s).map(|c| c as usize),
};
match (cached, input_ceiling) {
(Some(budget), Some(ceiling)) => Some(budget.min(ceiling)),
(budget, ceiling) => budget.or(ceiling),
}
}
pub(super) fn calibrate_up(est: usize, ratio: f32) -> usize {
(est as f32 * ratio).ceil() as usize
}
pub(super) fn calibrate_down(real: usize, ratio: f32) -> usize {
(real as f32 / ratio).floor() as usize
}
pub(super) fn sanitize_estimate_ratio(estimate_ratio: Option<f32>) -> f32 {
estimate_ratio
.filter(|r| r.is_finite() && (0.5..=3.0).contains(r))
.unwrap_or(1.0)
.max(1.0)
}
pub fn is_truncation_suspect(input_tokens: u32, num_ctx: Option<u32>) -> bool {
num_ctx.is_some_and(|c| input_tokens >= c.saturating_mul(95) / 100)
}
pub(super) fn emit_accepted(
hook: &mut Option<&mut dyn FnMut(RoundObservation)>,
round_usage: Option<crate::TokenUsage>,
truncation_suspect: bool,
estimated_tokens: usize,
) {
if truncation_suspect {
return;
}
if let (Some(hook), Some(u)) = (hook.as_deref_mut(), round_usage) {
hook(RoundObservation::Accepted {
prompt_tokens: u.input_tokens,
estimated_tokens,
});
}
}
pub(super) fn emit_context_window_400(
hook: &mut Option<&mut dyn FnMut(RoundObservation)>,
context_window: u32,
) {
if let Some(hook) = hook.as_deref_mut() {
hook(RoundObservation::ContextWindow400 { context_window });
}
}
pub(super) struct ResponsesBudgetState {
num_ctx: Option<u32>,
input_ceiling_pct: u32,
output_reserve: Option<u32>,
safe_context: Option<u32>,
tool_schema_tokens: usize,
learned_hard_ceiling: Option<usize>,
soft_send_budget: Option<usize>,
preflight_budget: Option<usize>,
mid_loop_trim_tokens: Option<usize>,
}
impl ResponsesBudgetState {
pub(super) fn new(
num_ctx: Option<u32>,
input_ceiling_pct: u32,
output_reserve: Option<u32>,
max_ok_input: Option<u32>,
safe_context: Option<u32>,
mid_loop_trim_tokens: Option<usize>,
) -> Self {
let seed_ceiling = num_ctx_input_ceiling(num_ctx, input_ceiling_pct, output_reserve);
let soft_send_budget = initial_send_budget(max_ok_input, safe_context, seed_ceiling);
let authoritative = safe_context.is_some() || seed_ceiling.is_some();
let preflight_budget =
authoritative_request_budget(soft_send_budget, authoritative, mid_loop_trim_tokens);
Self {
num_ctx,
input_ceiling_pct,
output_reserve,
safe_context,
tool_schema_tokens: 0,
learned_hard_ceiling: seed_ceiling,
soft_send_budget,
preflight_budget,
mid_loop_trim_tokens,
}
}
pub(super) fn set_tool_schema_tokens(&mut self, tokens: usize) {
self.tool_schema_tokens = tokens;
}
pub(super) fn output_reserve(&self) -> Option<u32> {
self.output_reserve
}
pub(super) fn num_ctx(&self) -> Option<u32> {
self.num_ctx
}
pub(super) fn input_ceiling_pct(&self) -> u32 {
self.input_ceiling_pct
}
pub(super) fn exposure_budget(&self) -> Option<usize> {
exposure_budget_tokens(self.soft_send_budget, self.safe_context)
}
pub(super) fn actionable_input_budget(&self) -> Option<usize> {
self.preflight_budget
}
pub(super) fn soft_send_budget(&self) -> Option<usize> {
self.soft_send_budget
}
#[cfg(test)]
pub(super) fn learned_hard_ceiling(&self) -> Option<usize> {
self.learned_hard_ceiling
}
pub(super) fn recovered_budget_for_window(&self, context_window: u32) -> usize {
recovered_input_budget(
context_window,
self.input_ceiling_pct,
self.output_reserve,
self.learned_hard_ceiling,
)
}
pub(super) fn recover_from_cw400(&mut self, recovered_budget: usize) {
let new_budget = self
.learned_hard_ceiling
.map_or(recovered_budget, |ceiling| recovered_budget.min(ceiling));
self.soft_send_budget = Some(new_budget);
self.learned_hard_ceiling = Some(new_budget);
self.preflight_budget =
authoritative_request_budget(self.soft_send_budget, true, self.mid_loop_trim_tokens);
}
pub(super) fn compaction_budget(&self, calibration: f32, with_tool_schemas: bool) -> usize {
let ceiling = self.preflight_budget.unwrap_or(0);
let overhead = if with_tool_schemas {
self.tool_schema_tokens
} else {
0
};
calibrate_down(ceiling.saturating_sub(overhead), calibration)
}
}
#[cfg(test)]
#[path = "send_budget_tests/mod.rs"]
mod send_budget_tests;