use std::sync::Arc;
use crate::AppState;
use crate::ir::ThinkingDirective;
pub(super) fn resolve_thinking(
state: &Arc<AppState>,
directive: ThinkingDirective,
max_tokens: u32,
tools_active: bool,
) -> (bool, Option<u32>) {
resolve(
directive,
Policy {
disable_thinking: state.disable_thinking,
model_default: state.behavior.thinking_default,
thinking_in_tools: state.behavior.thinking_in_tools,
max_thinking_budget: state.behavior.max_thinking_budget,
effort_capped_at_ceiling: state.behavior.effort_capped_at_ceiling,
cap_at_max_tokens: state.behavior.cap_thinking_at_max_tokens,
uncapped: state.uncapped_thinking,
},
max_tokens,
tools_active,
)
}
pub(super) fn generation_max_tokens(
max_tokens: usize,
tools_active: bool,
tool_max_tokens: usize,
) -> usize {
if tools_active {
max_tokens.min(tool_max_tokens)
} else {
max_tokens
}
}
#[derive(Clone, Copy)]
struct Policy {
disable_thinking: bool,
model_default: bool,
thinking_in_tools: bool,
max_thinking_budget: u32,
effort_capped_at_ceiling: bool,
cap_at_max_tokens: bool,
uncapped: bool,
}
fn resolve(
directive: ThinkingDirective,
policy: Policy,
max_tokens: u32,
tools_active: bool,
) -> (bool, Option<u32>) {
if policy.disable_thinking {
return (false, None);
}
let (et, tb) = match directive {
ThinkingDirective::Unspecified => (policy.model_default, None),
ThinkingDirective::Off => (false, None),
ThinkingDirective::On { budget } => (true, budget),
ThinkingDirective::OnEffort(level) => (
true,
Some(effort_budget(
level,
policy.max_thinking_budget,
policy.effort_capped_at_ceiling,
)),
),
};
let et = if tools_active && !policy.thinking_in_tools && !directive.is_explicit() {
false
} else {
et
};
let budget = if et && policy.uncapped {
match directive {
ThinkingDirective::On { budget } => budget,
_ => None,
}
} else if et {
let b = tb.unwrap_or(policy.max_thinking_budget);
if !policy.cap_at_max_tokens {
Some(b)
} else {
let safety_cap_pct = 9;
let max = ((max_tokens * safety_cap_pct) / 10).max(1);
Some(b.min(max))
}
} else {
None
};
(et, budget)
}
fn effort_budget(
level: crate::ir::EffortLevel,
max_thinking_budget: u32,
capped_at_ceiling: bool,
) -> u32 {
use crate::ir::EffortLevel;
let e = max_thinking_budget.max(1);
let b = match level {
EffortLevel::Minimal => (e / 4).max(1),
EffortLevel::Low => (e / 2).max(1),
EffortLevel::Medium => e,
EffortLevel::High => e.saturating_mul(2),
EffortLevel::XHigh => e.saturating_mul(4),
};
if capped_at_ceiling { b.min(e) } else { b }
}
#[cfg(test)]
mod tests {
use super::*;
fn policy() -> Policy {
Policy {
disable_thinking: false,
model_default: false,
thinking_in_tools: true,
max_thinking_budget: 2048,
effort_capped_at_ceiling: false,
cap_at_max_tokens: true,
uncapped: false,
}
}
#[test]
fn kill_switch_overrides_everything() {
let (et, tb) = resolve(
ThinkingDirective::On { budget: Some(512) },
Policy {
disable_thinking: true,
..policy()
},
4096,
false,
);
assert!(!et);
assert!(tb.is_none());
}
#[test]
fn unspecified_falls_to_model_default() {
let (et, tb) = resolve(
ThinkingDirective::Unspecified,
Policy {
model_default: true,
..policy()
},
4096,
false,
);
assert!(et);
assert_eq!(tb, Some(2048));
let (et, tb) = resolve(ThinkingDirective::Unspecified, policy(), 4096, false);
assert!(!et);
assert!(tb.is_none());
}
#[test]
fn explicit_budget_capped_at_90_pct_of_max_tokens() {
let (et, tb) = resolve(
ThinkingDirective::On { budget: Some(4096) },
policy(),
1000,
false,
);
assert!(et);
assert_eq!(tb, Some(900));
}
#[test]
fn budgetless_on_defers_to_model_cap() {
let (et, tb) = resolve(
ThinkingDirective::On { budget: None },
policy(),
4096,
false,
);
assert!(et);
assert_eq!(tb, Some(2048));
}
#[test]
fn effort_ladder_at_default_ceiling_matches_the_historical_absolutes() {
use crate::ir::EffortLevel::*;
let default_ceiling = Policy {
max_thinking_budget: 256,
cap_at_max_tokens: false,
..policy()
};
for (level, historical) in [
(Minimal, 64),
(Low, 128),
(Medium, 256),
(High, 512),
(XHigh, 1024),
] {
let (et, tb) = resolve(
ThinkingDirective::OnEffort(level),
default_ceiling,
4096,
false,
);
assert!(et);
assert_eq!(tb, Some(historical), "level={level:?}");
}
}
#[test]
fn effort_ladder_scales_with_the_operator_ceiling() {
use crate::ir::EffortLevel::*;
let operator = Policy {
max_thinking_budget: 16256,
cap_at_max_tokens: false,
..policy()
};
let (et, tb) = resolve(ThinkingDirective::OnEffort(Medium), operator, 32768, false);
assert!(et);
assert_eq!(tb, Some(16256));
let (_, tb) = resolve(ThinkingDirective::OnEffort(Minimal), operator, 32768, false);
assert_eq!(tb, Some(4064));
let capped = Policy {
max_thinking_budget: 16256,
..policy()
};
let (_, tb) = resolve(ThinkingDirective::OnEffort(Medium), capped, 1000, false);
assert_eq!(tb, Some(900));
}
#[test]
fn effort_cap_defaults_off_so_shipping_it_changes_nothing() {
assert!(!metrale_kernels::ModelBehavior::default().effort_capped_at_ceiling);
}
#[test]
fn effort_cap_clamps_the_ladder_at_the_ceiling() {
use crate::ir::EffortLevel::*;
let p = Policy {
max_thinking_budget: 128,
effort_capped_at_ceiling: true,
cap_at_max_tokens: false,
..policy()
};
for (level, expected) in [
(Minimal, 32),
(Low, 64),
(Medium, 128),
(High, 128),
(XHigh, 128),
] {
let (et, tb) = resolve(ThinkingDirective::OnEffort(level), p, 4096, false);
assert!(et);
assert_eq!(tb, Some(expected), "level={level:?}");
}
}
#[test]
fn effort_cap_never_touches_an_explicit_client_budget() {
let p = Policy {
max_thinking_budget: 128,
effort_capped_at_ceiling: true,
cap_at_max_tokens: false,
..policy()
};
let (et, tb) = resolve(ThinkingDirective::On { budget: Some(4096) }, p, 8192, false);
assert!(et);
assert_eq!(tb, Some(4096));
}
#[test]
fn effort_is_explicit_for_thinking_in_tools_purposes() {
let p = Policy {
thinking_in_tools: false,
..policy()
};
let (et, _) = resolve(
ThinkingDirective::OnEffort(crate::ir::EffortLevel::Medium),
p,
4096,
true,
);
assert!(et);
}
#[test]
fn cap_at_max_tokens_false_skips_the_90pct_clamp() {
let no_cap = Policy {
cap_at_max_tokens: false,
..policy()
};
let (et, tb) = resolve(
ThinkingDirective::On { budget: Some(4096) },
no_cap,
1000,
false,
);
assert!(et);
assert_eq!(tb, Some(4096));
let (_, tb) = resolve(ThinkingDirective::On { budget: None }, no_cap, 1000, false);
assert_eq!(tb, Some(2048));
}
#[test]
fn tools_suppression_only_when_client_silent() {
let no_tools_thinking = Policy {
model_default: true,
thinking_in_tools: false,
..policy()
};
let (et, _) = resolve(
ThinkingDirective::Unspecified,
Policy {
..no_tools_thinking
},
4096,
true,
);
assert!(!et);
let (et, _) = resolve(
ThinkingDirective::On { budget: None },
Policy {
model_default: true,
thinking_in_tools: false,
..policy()
},
4096,
true,
);
assert!(et);
let (et, _) = resolve(
ThinkingDirective::Off,
Policy {
model_default: true,
thinking_in_tools: false,
..policy()
},
4096,
true,
);
assert!(!et);
}
#[test]
fn explicit_off_wins_over_model_default() {
let (et, tb) = resolve(
ThinkingDirective::Off,
Policy {
model_default: true,
..policy()
},
4096,
false,
);
assert!(!et);
assert!(tb.is_none());
}
#[test]
fn issue_517_tool_capped_ceiling_leaves_room_for_tool_call() {
let (et, tb) = resolve(ThinkingDirective::On { budget: None }, policy(), 256, true);
assert!(et);
assert_eq!(tb, Some(230));
assert!(tb.unwrap() < 256);
}
#[test]
fn uncapped_arms_no_server_budget_but_keeps_a_stated_one_unclamped() {
use crate::ir::EffortLevel::Low;
let uncapped = Policy {
uncapped: true,
model_default: true,
..policy()
};
let (_, capped_low) = resolve(ThinkingDirective::OnEffort(Low), policy(), 4096, false);
assert_eq!(
capped_low,
Some(1024),
"low effort is half the 2048 ceiling"
);
for directive in [
ThinkingDirective::OnEffort(Low),
ThinkingDirective::On { budget: None },
ThinkingDirective::Unspecified,
] {
assert_eq!(
resolve(directive, uncapped, 4096, false),
(true, None),
"{directive:?}"
);
}
assert_eq!(
resolve(
ThinkingDirective::On { budget: Some(4096) },
uncapped,
1000,
false
),
(true, Some(4096)),
"a stated budget applies, without the 90%-of-max_tokens clamp"
);
assert_eq!(
resolve(ThinkingDirective::Off, uncapped, 4096, false),
(false, None)
);
}
}