#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ModelInfo {
pub context_window: u32,
pub effective_context_window_percent: u16,
pub force_temperature: Option<f32>,
}
impl ModelInfo {
pub const DEFAULT_EFFECTIVE_PERCENT: u16 = 95;
#[must_use]
pub const fn new(context_window: u32) -> Self {
Self {
context_window,
effective_context_window_percent: Self::DEFAULT_EFFECTIVE_PERCENT,
force_temperature: None,
}
}
#[must_use]
pub const fn with_forced_temperature(mut self, temperature: f32) -> Self {
self.force_temperature = Some(temperature);
self
}
#[must_use]
pub const fn effective_window(self) -> u32 {
let usable = (self.context_window as u64 * self.effective_context_window_percent as u64)
/ Self::PERCENT_DENOM;
if usable > u32::MAX as u64 {
u32::MAX
} else {
#[allow(clippy::cast_possible_truncation)]
{
usable as u32
}
}
}
const PERCENT_DENOM: u64 = 100;
}
pub const FALLBACK_CONTEXT_WINDOW: u32 = 128_000;
static CATALOG: &[(&str, ModelInfo)] = &[
("gemini-3-flash", ModelInfo::new(1_048_576)),
("gemini-3.1-pro", ModelInfo::new(1_048_576)),
("gemini-3-pro", ModelInfo::new(1_048_576)),
("gemini-3", ModelInfo::new(1_048_576)),
("gpt-4o", ModelInfo::new(128_000)),
("gpt-4-turbo", ModelInfo::new(128_000)),
("gpt-4", ModelInfo::new(8_192)),
("llama3.2", ModelInfo::new(131_072)),
("llama3", ModelInfo::new(131_072)),
("glm-5.2", ModelInfo::new(1_048_576)),
("glm-5.1", ModelInfo::new(200_000)),
("glm-5", ModelInfo::new(204_800)),
(
"glm-4.7",
ModelInfo::new(204_800).with_forced_temperature(1.0),
),
(
"glm-4.6",
ModelInfo::new(204_800).with_forced_temperature(1.0),
),
("glm-4.5", ModelInfo::new(131_072)),
];
#[must_use]
pub fn lookup_model(slug: &str) -> Option<ModelInfo> {
CATALOG
.iter()
.filter(|(k, _)| slug.starts_with(k))
.max_by_key(|(k, _)| k.len())
.map(|(_, info)| *info)
}
#[must_use]
pub fn lookup_model_or_fallback(slug: &str) -> ModelInfo {
lookup_model(slug).unwrap_or_else(|| {
tracing::warn!(
model = %slug,
fallback_context_window = FALLBACK_CONTEXT_WINDOW,
"unknown model slug; using conservative fallback context window"
);
ModelInfo::new(FALLBACK_CONTEXT_WINDOW)
})
}
#[cfg(test)]
mod tests {
#![allow(clippy::pedantic, clippy::nursery, missing_docs)]
use super::*;
#[test]
fn exact_match_resolves() {
assert_eq!(lookup_model("gpt-4").unwrap().context_window, 8_192);
assert_eq!(lookup_model("gpt-4o").unwrap().context_window, 128_000);
}
#[test]
fn longest_prefix_beats_shorter_keys() {
assert_eq!(
lookup_model("gpt-4o-2024-08-06").unwrap().context_window,
128_000
);
assert_eq!(
lookup_model("gpt-4-turbo-2024").unwrap().context_window,
128_000
);
}
#[test]
fn preview_and_tag_suffixes_resolve_to_base() {
assert_eq!(
lookup_model("gemini-3.1-pro-preview")
.unwrap()
.context_window,
1_048_576
);
assert_eq!(
lookup_model("gemini-3-flash-preview-09-2025")
.unwrap()
.context_window,
1_048_576
);
assert_eq!(
lookup_model("llama3.2:latest").unwrap().context_window,
131_072
);
}
#[test]
fn glm_family_windows_resolve() {
assert_eq!(lookup_model("glm-5.2").unwrap().context_window, 1_048_576);
assert_eq!(lookup_model("glm-5.1").unwrap().context_window, 200_000);
assert_eq!(lookup_model("glm-5").unwrap().context_window, 204_800);
assert_eq!(lookup_model("glm-4.6").unwrap().context_window, 204_800);
assert_eq!(lookup_model("glm-4.5").unwrap().context_window, 131_072);
}
#[test]
fn glm_force_temperature_only_for_4_6_and_4_7() {
assert_eq!(
lookup_model("glm-4.6").unwrap().force_temperature,
Some(1.0)
);
assert_eq!(
lookup_model("glm-4.7-flash").unwrap().force_temperature,
Some(1.0)
);
assert_eq!(lookup_model("glm-4.5").unwrap().force_temperature, None);
assert_eq!(lookup_model("glm-5.2").unwrap().force_temperature, None);
assert_eq!(lookup_model("gpt-4o").unwrap().force_temperature, None);
}
#[test]
fn glm_longest_prefix_and_suffixes() {
assert_eq!(
lookup_model("glm-5.2-0712").unwrap().context_window,
1_048_576
);
assert_eq!(lookup_model("glm-5.1-air").unwrap().context_window, 200_000);
}
#[test]
fn unknown_slug_has_no_catalog_entry_but_falls_back() {
assert!(lookup_model("foo-model").is_none());
assert_eq!(
lookup_model_or_fallback("foo-model").context_window,
FALLBACK_CONTEXT_WINDOW
);
}
#[test]
fn effective_window_math() {
assert_eq!(ModelInfo::new(1_048_576).effective_window(), 996_147);
assert_eq!(ModelInfo::new(128_000).effective_window(), 121_600);
}
}