2026-07-16 04:52:59 +07:00
|
|
|
#![allow(dead_code)]
|
2026-07-16 04:09:27 +07:00
|
|
|
//! Single source of truth for resolving the active model's context
|
|
|
|
|
//! window size, replacing three copies of the same lookup that had
|
|
|
|
|
//! drifted (`Action::Compact`, `spawn_turn`, and `view/status.rs` each
|
|
|
|
|
//! had their own inline version — the status bar's copy additionally
|
|
|
|
|
//! displayed "?" on no match instead of falling back like the other two,
|
|
|
|
|
//! an inconsistency this unifies away).
|
2026-07-16 16:30:12 +07:00
|
|
|
use zesdex_cms::domain::app_config::AppConfig;
|
2026-07-16 16:27:38 +07:00
|
|
|
use zesdex_cms::domain::settings::Settings;
|
2026-07-16 04:09:27 +07:00
|
|
|
|
|
|
|
|
/// Resolve the context-window size (in tokens) for the currently
|
|
|
|
|
/// configured provider/model.
|
|
|
|
|
///
|
|
|
|
|
/// Flow: find the `ModelRole` whose `provider`+`model` match
|
|
|
|
|
/// `settings` -> use its `context_window` if set -> otherwise fall back
|
|
|
|
|
/// to `app_config.default_context_window`.
|
|
|
|
|
///
|
|
|
|
|
/// Return: always a concrete token count, never "unknown".
|
|
|
|
|
pub fn resolve(app_config: &AppConfig, settings: &Settings) -> usize {
|
2026-07-16 07:42:03 +07:00
|
|
|
app_config
|
|
|
|
|
.model_roles
|
|
|
|
|
.values()
|
2026-07-16 04:09:27 +07:00
|
|
|
.find(|role| role.provider == settings.provider && role.model == settings.model)
|
|
|
|
|
.and_then(|role| role.context_window)
|
|
|
|
|
.unwrap_or(app_config.default_context_window) as usize
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
2026-07-16 16:30:12 +07:00
|
|
|
use zesdex_cms::domain::app_config::ModelRole;
|
2026-07-16 04:09:27 +07:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn resolves_context_window_from_matching_model_role() {
|
|
|
|
|
let mut app_config = AppConfig::default();
|
2026-07-16 07:42:03 +07:00
|
|
|
app_config.model_roles.insert(
|
|
|
|
|
"default".to_string(),
|
|
|
|
|
ModelRole {
|
|
|
|
|
provider: "zen".to_string(),
|
|
|
|
|
model: "deepseek-v4-flash-free".to_string(),
|
|
|
|
|
max_tokens: None,
|
|
|
|
|
context_window: Some(128_000),
|
|
|
|
|
temperature: None,
|
|
|
|
|
},
|
|
|
|
|
);
|
2026-07-16 04:09:27 +07:00
|
|
|
let mut settings = Settings::default();
|
|
|
|
|
settings.provider = "zen".to_string();
|
|
|
|
|
settings.model = "deepseek-v4-flash-free".to_string();
|
|
|
|
|
|
|
|
|
|
assert_eq!(resolve(&app_config, &settings), 128_000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn falls_back_to_default_context_window_when_no_role_matches() {
|
|
|
|
|
let app_config = AppConfig::default();
|
|
|
|
|
let mut settings = Settings::default();
|
|
|
|
|
settings.provider = "nonexistent".to_string();
|
|
|
|
|
settings.model = "nonexistent-model".to_string();
|
|
|
|
|
|
2026-07-16 07:42:03 +07:00
|
|
|
assert_eq!(
|
|
|
|
|
resolve(&app_config, &settings),
|
|
|
|
|
app_config.default_context_window as usize
|
|
|
|
|
);
|
2026-07-16 04:09:27 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn falls_back_to_default_when_matching_role_has_no_context_window_set() {
|
|
|
|
|
let mut app_config = AppConfig::default();
|
2026-07-16 07:42:03 +07:00
|
|
|
app_config.model_roles.insert(
|
|
|
|
|
"default".to_string(),
|
|
|
|
|
ModelRole {
|
|
|
|
|
provider: "zen".to_string(),
|
|
|
|
|
model: "deepseek-v4-flash-free".to_string(),
|
|
|
|
|
max_tokens: None,
|
|
|
|
|
context_window: None,
|
|
|
|
|
temperature: None,
|
|
|
|
|
},
|
|
|
|
|
);
|
2026-07-16 04:09:27 +07:00
|
|
|
let mut settings = Settings::default();
|
|
|
|
|
settings.provider = "zen".to_string();
|
|
|
|
|
settings.model = "deepseek-v4-flash-free".to_string();
|
|
|
|
|
|
2026-07-16 07:42:03 +07:00
|
|
|
assert_eq!(
|
|
|
|
|
resolve(&app_config, &settings),
|
|
|
|
|
app_config.default_context_window as usize
|
|
|
|
|
);
|
2026-07-16 04:09:27 +07:00
|
|
|
}
|
|
|
|
|
}
|