use super::{ApiClient, BareLoop, LoopError};
use crate::capabilities::FallbackCapable;
use crate::observer::ModelSwitchedContext;
pub struct ModelSwitch<'a, C: ApiClient> {
pub(super) loop_: &'a mut BareLoop<C>,
pub(super) target_model: String,
pub(super) context_window: Option<u64>,
}
impl<C: ApiClient> ModelSwitch<'_, C> {
#[must_use]
pub fn with_context_window(mut self, tokens: u64) -> Self {
self.context_window = Some(tokens);
self
}
pub fn apply(self) -> Result<(), LoopError> {
let Self {
loop_,
target_model,
context_window,
} = self;
let trimmed = target_model.trim();
if trimmed.is_empty() {
return Err(LoopError::Config(
"model name must not be empty or whitespace".into(),
));
}
loop_.managers.fallback().state()?;
let from = loop_.client.model();
if !loop_.client.set_model(trimmed) {
return Err(LoopError::Config(format!(
"client rejected the model switch to {trimmed:?} \
(set_model returned false); nothing was changed"
)));
}
if let Some(cw) = context_window {
loop_.session.config.context_window = cw;
}
loop_.managers.fallback().reset()?;
loop_
.managers
.fallback()
.set_original_model(trimmed.to_string())?;
loop_
.managers
.observers()
.on_model_switched(&ModelSwitchedContext {
from,
to: trimmed.to_string(),
});
Ok(())
}
}