//! Effort mode: cycles the agent's reasoning effort level, which scales the //! LLM's temperature and max_tokens for subsequent turns. use crate::app::state::rest::AppStateRest; pub const EFFORT_LEVELS: &[&str] = &["low", "medium", "high", "xhigh", "max"]; /// Multiplier applied to the user's configured `max_tokens`, and the temperature to use, /// for each entry in `EFFORT_LEVELS` (same index). Higher effort trades a larger token /// budget for lower temperature (more deterministic, more room to reason/act). const MAX_TOKENS_MULTIPLIER: &[f32] = &[0.5, 1.0, 1.5, 2.0, 3.0]; const TEMPERATURE_OVERRIDE: &[f32] = &[0.9, 0.7, 0.5, 0.3, 0.1]; /// Maps an effort level index to the `(temperature, max_tokens)` pair that should be sent /// to the LLM, scaling the user's configured `max_tokens` by the level's multiplier. pub fn generation_params(level: usize, base_max_tokens: Option) -> (f32, Option) { let idx = level.min(EFFORT_LEVELS.len() - 1); let temperature = TEMPERATURE_OVERRIDE[idx]; let max_tokens = base_max_tokens.map(|t| ((t as f32) * MAX_TOKENS_MULTIPLIER[idx]) as u32); (temperature, max_tokens.map(|t| t.max(256))) } /// Return the current effort level index, clamped to a valid `EFFORT_LEVELS` slot. /// /// Why: clamping guards against a stale/out-of-range value in loaded state /// (e.g. after `EFFORT_LEVELS` shrinks between versions). pub fn current_effort(state: &AppStateRest) -> usize { state.misc.effort_level.min(EFFORT_LEVELS.len() - 1) } /// Return the current effort level's display name (e.g. "medium"). pub fn current_effort_str(state: &AppStateRest) -> &'static str { let idx = current_effort(state); EFFORT_LEVELS[idx] } /// Advance to the next effort level, wrapping around, and toast the new value. /// /// Flow: compute `(current + 1) % len` → store it → push an info toast with /// the new level's label → mark state dirty. pub fn cycle_effort(state: &mut AppStateRest) { let current = current_effort(state); state.misc.effort_level = (current + 1) % EFFORT_LEVELS.len(); let label = current_effort_str(state); state.push_toast(crate::app::state::types::Toast::new( crate::app::state::types::ToastKind::Info, format!("Effort: {}", label), )); state.dirty = true; }