From b9bcc9c71039a29d59d22e92ca97e582a61bfd94 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 00:17:53 +0000 Subject: [PATCH] fix(providers): update stale Atlascloud/Ollama capability tests to the generic model-based path; add bare Moonshot/MiniMax/Z.ai model rows (kimi-k2.6, kimi-for-coding, minimax-m3, glm-5.1, glm-5v-turbo) mirroring vendor-prefixed rows; add Moonshot capability + engine capacity tests (#3023, #1310) Co-Authored-By: Claude https://claude.ai/code/session_018zaP8vUfTAsrE38L6h6fw5 --- crates/tui/src/config.rs | 44 +++++++++++++++++++++++--- crates/tui/src/core/engine/tests.rs | 27 ++++++++++++++++ crates/tui/src/models.rs | 48 ++++++++++++++++++++++++----- 3 files changed, 107 insertions(+), 12 deletions(-) diff --git a/crates/tui/src/config.rs b/crates/tui/src/config.rs index 4c15aa6a..eac5e694 100644 --- a/crates/tui/src/config.rs +++ b/crates/tui/src/config.rs @@ -10756,14 +10756,30 @@ model = "deepseek-ai/deepseek-v4-pro" } #[test] - fn provider_capability_atlascloud_custom_model_is_chat_completions_without_thinking() { + fn provider_capability_atlascloud_v4_model_resolves_model_metadata() { + // #3023: Atlascloud uses the generic model-based path, so its default + // DeepSeek V4 model resolves the real V4 metadata instead of the old + // hardcoded legacy floor. let cap = provider_capability(ApiProvider::Atlascloud, "deepseek-ai/deepseek-v4-flash"); assert_eq!( cap.context_window, - crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS + crate::models::DEEPSEEK_V4_CONTEXT_WINDOW_TOKENS ); - assert_eq!(cap.max_output, 4096); - assert!(!cap.thinking_supported); + assert_eq!(cap.max_output, 384_000); + assert!(cap.thinking_supported); + assert!(!cap.cache_telemetry_supported); + assert_eq!( + cap.request_payload_mode, + RequestPayloadMode::ChatCompletions + ); + } + + #[test] + fn provider_capability_moonshot_default_model_resolves_kimi_metadata() { + let cap = provider_capability(ApiProvider::Moonshot, DEFAULT_MOONSHOT_MODEL); + assert_eq!(cap.context_window, 262_144); + assert_eq!(cap.max_output, 262_144); + assert!(cap.thinking_supported); assert!(!cap.cache_telemetry_supported); assert_eq!( cap.request_payload_mode, @@ -10788,8 +10804,26 @@ model = "deepseek-ai/deepseek-v4-pro" } #[test] - fn provider_capability_ollama_is_openai_compatible_without_thinking() { + fn provider_capability_ollama_deepseek_tag_uses_deepseek_heuristic() { + // #3023: known model families resolve through models.rs lookups even + // on Ollama — a legacy DeepSeek tag gets the 128K heuristic window. let cap = provider_capability(ApiProvider::Ollama, "deepseek-v3.1:671b"); + assert_eq!( + cap.context_window, + crate::models::LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS + ); + assert_eq!(cap.max_output, 4096); + assert!(!cap.thinking_supported); + assert!(!cap.cache_telemetry_supported); + assert_eq!( + cap.request_payload_mode, + RequestPayloadMode::ChatCompletions + ); + } + + #[test] + fn provider_capability_ollama_unknown_model_falls_back_to_8192() { + let cap = provider_capability(ApiProvider::Ollama, "llama3.2:3b"); assert_eq!(cap.context_window, 8192); assert_eq!(cap.max_output, 4096); assert!(!cap.thinking_supported); diff --git a/crates/tui/src/core/engine/tests.rs b/crates/tui/src/core/engine/tests.rs index 212042c4..d8379549 100644 --- a/crates/tui/src/core/engine/tests.rs +++ b/crates/tui/src/core/engine/tests.rs @@ -2863,6 +2863,33 @@ async fn pre_request_refresh_skips_compaction_below_normal_threshold() { assert_eq!(engine.session.messages.len(), before_len); } +#[test] +fn capacity_observation_uses_bare_kimi_context_window() { + // #3023: capacity math reads models::context_window_for_model directly, + // so bare Moonshot ids must resolve their real window, not the 128K + // legacy fallback. + let mut engine = build_engine_with_capacity(CapacityControllerConfig::default()); + engine.session.model = "kimi-k2.6".to_string(); + engine.session.messages.push(Message { + role: "user".to_string(), + content: vec![ContentBlock::Text { + text: "x".repeat(40_000), + cache_control: None, + }], + }); + + let estimated = engine.estimated_input_tokens() as f64; + let turn = TurnContext::new(1); + let observation = engine.capacity_observation(&turn); + + let expected = estimated / 262_144.0; + assert!( + (observation.context_used_ratio - expected).abs() < 1e-9, + "context_used_ratio must use kimi-k2.6's 262,144-token window (got {})", + observation.context_used_ratio + ); +} + #[tokio::test] async fn pre_request_refresh_invoked_when_medium_risk() { let capacity = CapacityControllerConfig { diff --git a/crates/tui/src/models.rs b/crates/tui/src/models.rs index 80849c94..e280d639 100644 --- a/crates/tui/src/models.rs +++ b/crates/tui/src/models.rs @@ -263,9 +263,13 @@ fn known_context_window_for_model(model_lower: &str) -> Option { | "qwen/qwen3.6-27b" | "tencent/hy3-preview" | "moonshotai/kimi-k2.6" - | "moonshotai/kimi-k2.6:free" => Some(262_144), - "z-ai/glm-5.1" | "z-ai/glm-5v-turbo" => Some(202_752), - "minimax/minimax-m3" | "qwen/qwen3.6-flash" | "qwen/qwen3.6-plus" => Some(1_000_000), + | "moonshotai/kimi-k2.6:free" + | "kimi-k2.6" + | "kimi-for-coding" => Some(262_144), + "z-ai/glm-5.1" | "z-ai/glm-5v-turbo" | "glm-5.1" | "glm-5v-turbo" => Some(202_752), + "minimax/minimax-m3" | "minimax-m3" | "qwen/qwen3.6-flash" | "qwen/qwen3.6-plus" => { + Some(1_000_000) + } "xiaomi/mimo-v2.5-pro" | "xiaomi/mimo-v2.5" | "mimo-v2.5-pro" | "mimo-v2.5" => { Some(1_000_000) } @@ -285,10 +289,12 @@ pub fn max_output_tokens_for_model(model: &str) -> Option { return Some(384_000); } match lower.as_str() { - "arcee-ai/trinity-large-thinking" | "trinity-large-thinking" | "moonshotai/kimi-k2.6" => { - Some(262_144) - } - "minimax/minimax-m3" => Some(524_288), + "arcee-ai/trinity-large-thinking" + | "trinity-large-thinking" + | "moonshotai/kimi-k2.6" + | "kimi-k2.6" + | "kimi-for-coding" => Some(262_144), + "minimax/minimax-m3" | "minimax-m3" => Some(524_288), "qwen/qwen3.6-35b-a3b" | "qwen/qwen3.6-27b" => Some(262_140), "qwen/qwen3.6-flash" | "qwen/qwen3.6-max-preview" | "qwen/qwen3.6-plus" => Some(65_536), "xiaomi/mimo-v2.5-pro" | "xiaomi/mimo-v2.5" | "mimo-v2.5-pro" | "mimo-v2.5" => { @@ -322,7 +328,9 @@ pub fn model_supports_reasoning(model: &str) -> bool { | "google/gemma-4-26b-a4b-it:free" | "moonshotai/kimi-k2.6" | "moonshotai/kimi-k2.6:free" + | "kimi-k2.6" | "minimax/minimax-m3" + | "minimax-m3" | "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free" | "qwen/qwen3.6-flash" | "qwen/qwen3.6-35b-a3b" @@ -335,6 +343,7 @@ pub fn model_supports_reasoning(model: &str) -> bool { | "mimo-v2.5-pro" | "mimo-v2.5" | "z-ai/glm-5.1" + | "glm-5.1" ) } @@ -587,6 +596,31 @@ mod tests { ); } + #[test] + fn bare_provider_model_ids_mirror_vendor_prefixed_rows() { + // Direct-provider routes (Moonshot, MiniMax, Z.ai) serve bare model + // ids without the OpenRouter vendor prefix; both spellings must + // resolve identical metadata (#1310 ride-along on #3023). + for (model, expected_window) in [ + ("kimi-k2.6", 262_144), + ("minimax-m3", 1_000_000), + ("glm-5.1", 202_752), + ] { + assert_eq!(context_window_for_model(model), Some(expected_window)); + assert!(model_supports_reasoning(model)); + } + assert_eq!(context_window_for_model("kimi-for-coding"), Some(262_144)); + assert!(!model_supports_reasoning("kimi-for-coding")); + assert_eq!(context_window_for_model("glm-5v-turbo"), Some(202_752)); + assert!(!model_supports_reasoning("glm-5v-turbo")); + assert_eq!(max_output_tokens_for_model("kimi-k2.6"), Some(262_144)); + assert_eq!( + max_output_tokens_for_model("kimi-for-coding"), + Some(262_144) + ); + assert_eq!(max_output_tokens_for_model("minimax-m3"), Some(524_288)); + } + #[test] fn deepseek_models_with_k_suffix_use_hint() { assert_eq!(context_window_for_model("deepseek-v3.2-32k"), Some(32_000));