From 5a80556eb2bd24bf6b21f87447007d4ad61712d9 Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Sun, 31 May 2026 11:26:25 -0700 Subject: [PATCH] feat(models): add recent large OpenRouter models (#2461) --- README.ja-JP.md | 2 + README.md | 2 + README.zh-CN.md | 2 + config.example.toml | 11 ++- crates/agent/src/lib.rs | 125 +++++++++++++++++++++++++++++ crates/config/src/lib.rs | 99 +++++++++++++++++++++++ crates/tui/src/config.rs | 164 ++++++++++++++++++++++++++++++++++++++- crates/tui/src/models.rs | 99 +++++++++++++++++++++++ docs/CONFIGURATION.md | 2 +- docs/PROVIDERS.md | 16 +++- 10 files changed, 515 insertions(+), 7 deletions(-) diff --git a/README.ja-JP.md b/README.ja-JP.md index 92bfd0cb..37c380cc 100644 --- a/README.ja-JP.md +++ b/README.ja-JP.md @@ -218,6 +218,8 @@ codewhale --provider wanjie-ark --model deepseek-reasoner # OpenRouter codewhale auth set --provider openrouter --api-key "YOUR_OPENROUTER_API_KEY" codewhale --provider openrouter --model deepseek/deepseek-v4-pro +codewhale --provider openrouter --model arcee-ai/trinity-large-thinking +codewhale --provider openrouter --model qwen/qwen3.7-max # Xiaomi MiMo codewhale auth set --provider xiaomi-mimo --api-key "YOUR_XIAOMI_MIMO_API_KEY" diff --git a/README.md b/README.md index 7efd8496..a01742eb 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,8 @@ codewhale --provider wanjie-ark --model deepseek-reasoner # OpenRouter codewhale auth set --provider openrouter --api-key "YOUR_OPENROUTER_API_KEY" codewhale --provider openrouter --model deepseek/deepseek-v4-pro +codewhale --provider openrouter --model arcee-ai/trinity-large-thinking +codewhale --provider openrouter --model qwen/qwen3.7-max # Xiaomi MiMo codewhale auth set --provider xiaomi-mimo --api-key "YOUR_XIAOMI_MIMO_API_KEY" diff --git a/README.zh-CN.md b/README.zh-CN.md index 79e167a6..03a0975f 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -262,6 +262,8 @@ codewhale --provider wanjie-ark --model deepseek-reasoner # OpenRouter codewhale auth set --provider openrouter --api-key "YOUR_OPENROUTER_API_KEY" codewhale --provider openrouter --model deepseek/deepseek-v4-pro +codewhale --provider openrouter --model arcee-ai/trinity-large-thinking +codewhale --provider openrouter --model qwen/qwen3.7-max # Xiaomi MiMo codewhale auth set --provider xiaomi-mimo --api-key "YOUR_XIAOMI_MIMO_API_KEY" diff --git a/config.example.toml b/config.example.toml index f9dcd733..6a4d6bb4 100644 --- a/config.example.toml +++ b/config.example.toml @@ -36,6 +36,11 @@ base_url = "https://api.deepseek.com/beta" # deepseek-v4-flash — fast, cost-efficient (legacy aliases: deepseek-chat, deepseek-reasoner) # deepseek-ai/deepseek-v4-pro — NVIDIA NIM-hosted Pro model ID # deepseek-ai/deepseek-v4-flash — NVIDIA NIM-hosted Flash model ID +# deepseek/deepseek-v4-pro — default OpenRouter DeepSeek model ID +# arcee-ai/trinity-large-thinking — OpenRouter Arcee Trinity Large Thinking +# qwen/qwen3.7-max — OpenRouter Qwen 3.7 Max +# xiaomi/mimo-v2.5-pro — OpenRouter Xiaomi MiMo 2.5 Pro +# xiaomi/mimo-v2.5 — OpenRouter Xiaomi MiMo 2.5 # gpt-4.1 — default generic OpenAI-compatible model ID # deepseek-ai/deepseek-v4-flash — default AtlasCloud model ID # deepseek-reasoner — default Wanjie Ark model ID @@ -255,7 +260,11 @@ max_subagents = 10 # optional (1-20) [providers.openrouter] # api_key = "YOUR_OPENROUTER_API_KEY" # base_url = "https://openrouter.ai/api/v1" -# model = "deepseek/deepseek-v4-pro" # or deepseek/deepseek-v4-flash +# model = "deepseek/deepseek-v4-pro" +# Recent large model IDs also accepted here include arcee-ai/trinity-large-thinking, +# qwen/qwen3.7-max, xiaomi/mimo-v2.5-pro, qwen/qwen3.6-35b-a3b, +# google/gemma-4-31b-it, z-ai/glm-5.1, moonshotai/kimi-k2.6, and +# nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free. # Xiaomi MiMo OpenAI-compatible endpoint (https://platform.xiaomimimo.com) [providers.xiaomi_mimo] diff --git a/crates/agent/src/lib.rs b/crates/agent/src/lib.rs index c362d856..03cde0f9 100644 --- a/crates/agent/src/lib.rs +++ b/crates/agent/src/lib.rs @@ -186,6 +186,109 @@ impl Default for ModelRegistry { supports_tools: true, supports_reasoning: true, }, + ModelInfo { + id: "arcee-ai/trinity-large-thinking".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec![ + "trinity".to_string(), + "trinity-large-thinking".to_string(), + "arcee-trinity-large-thinking".to_string(), + ], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "qwen/qwen3.7-max".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec!["qwen3.7-max".to_string(), "qwen-3.7-max".to_string()], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "xiaomi/mimo-v2.5-pro".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec![ + "openrouter-mimo-v2.5-pro".to_string(), + "openrouter-xiaomi-mimo-v2.5-pro".to_string(), + ], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "xiaomi/mimo-v2.5".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec![ + "openrouter-mimo-v2.5".to_string(), + "openrouter-xiaomi-mimo-v2.5".to_string(), + ], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "qwen/qwen3.6-35b-a3b".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec![ + "qwen3.6-35b-a3b".to_string(), + "qwen-3.6-35b-a3b".to_string(), + ], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "qwen/qwen3.6-27b".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec!["qwen3.6-27b".to_string(), "qwen-3.6-27b".to_string()], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "moonshotai/kimi-k2.6".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec!["openrouter-kimi-k2.6".to_string()], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "z-ai/glm-5.1".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec!["glm-5.1".to_string(), "zai-glm-5.1".to_string()], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "tencent/hy3-preview".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec!["hy3-preview".to_string(), "tencent-hy3-preview".to_string()], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "google/gemma-4-31b-it".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec!["gemma-4-31b".to_string(), "gemma-4-31b-it".to_string()], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "google/gemma-4-26b-a4b-it".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec![ + "gemma-4-26b-a4b".to_string(), + "gemma-4-26b-a4b-it".to_string(), + ], + supports_tools: true, + supports_reasoning: true, + }, + ModelInfo { + id: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free".to_string(), + provider: ProviderKind::Openrouter, + aliases: vec![ + "nemotron-3-nano-omni".to_string(), + "nemotron-3-nano-omni-reasoning".to_string(), + ], + supports_tools: true, + supports_reasoning: true, + }, ModelInfo { id: "mimo-v2.5-pro".to_string(), provider: ProviderKind::XiaomiMimo, @@ -619,6 +722,28 @@ mod tests { assert_eq!(resolved.resolved.id, "deepseek/deepseek-v4-flash"); } + #[test] + fn recent_openrouter_large_model_aliases_resolve_when_provider_hinted() { + let registry = ModelRegistry::default(); + + for (alias, expected) in [ + ("trinity-large-thinking", "arcee-ai/trinity-large-thinking"), + ("qwen3.7-max", "qwen/qwen3.7-max"), + ("qwen3.6-35b-a3b", "qwen/qwen3.6-35b-a3b"), + ("gemma-4-31b-it", "google/gemma-4-31b-it"), + ("glm-5.1", "z-ai/glm-5.1"), + ("openrouter-mimo-v2.5-pro", "xiaomi/mimo-v2.5-pro"), + ("openrouter-kimi-k2.6", "moonshotai/kimi-k2.6"), + ] { + let resolved = registry.resolve(Some(alias), Some(ProviderKind::Openrouter)); + + assert_eq!(resolved.resolved.provider, ProviderKind::Openrouter); + assert_eq!(resolved.resolved.id, expected); + assert!(resolved.resolved.supports_tools); + assert!(resolved.resolved.supports_reasoning); + } + } + #[test] fn deepseek_v4_flash_alias_resolves_to_novita_when_provider_hinted() { let registry = ModelRegistry::default(); diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index fddd6a53..06edde79 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -29,6 +29,19 @@ const DEFAULT_VOLCENGINE_MODEL: &str = "DeepSeek-V4-Pro"; const DEFAULT_VOLCENGINE_BASE_URL: &str = "https://ark.cn-beijing.volces.com/api/coding/v3"; const DEFAULT_OPENROUTER_MODEL: &str = "deepseek/deepseek-v4-pro"; const DEFAULT_OPENROUTER_FLASH_MODEL: &str = "deepseek/deepseek-v4-flash"; +const OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL: &str = "arcee-ai/trinity-large-thinking"; +const OPENROUTER_GEMMA_4_31B_MODEL: &str = "google/gemma-4-31b-it"; +const OPENROUTER_GEMMA_4_26B_A4B_MODEL: &str = "google/gemma-4-26b-a4b-it"; +const OPENROUTER_GLM_5_1_MODEL: &str = "z-ai/glm-5.1"; +const OPENROUTER_KIMI_K2_6_MODEL: &str = "moonshotai/kimi-k2.6"; +const OPENROUTER_NEMOTRON_3_NANO_OMNI_MODEL: &str = + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free"; +const OPENROUTER_QWEN_3_7_MAX_MODEL: &str = "qwen/qwen3.7-max"; +const OPENROUTER_QWEN_3_6_35B_A3B_MODEL: &str = "qwen/qwen3.6-35b-a3b"; +const OPENROUTER_QWEN_3_6_27B_MODEL: &str = "qwen/qwen3.6-27b"; +const OPENROUTER_TENCENT_HY3_PREVIEW_MODEL: &str = "tencent/hy3-preview"; +const OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL: &str = "xiaomi/mimo-v2.5-pro"; +const OPENROUTER_XIAOMI_MIMO_V2_5_MODEL: &str = "xiaomi/mimo-v2.5"; const DEFAULT_XIAOMI_MIMO_MODEL: &str = "mimo-v2.5-pro"; const DEFAULT_NOVITA_MODEL: &str = "deepseek/deepseek-v4-pro"; const DEFAULT_NOVITA_FLASH_MODEL: &str = "deepseek/deepseek-v4-flash"; @@ -1426,6 +1439,11 @@ fn normalize_model_for_provider(provider: ProviderKind, model: &str) -> String { } let normalized = model.trim().to_ascii_lowercase(); + if provider == ProviderKind::Openrouter + && let Some(canonical) = canonical_openrouter_recent_model_id(&normalized) + { + return canonical.to_string(); + } match (provider, normalized.as_str()) { (ProviderKind::NvidiaNim, "deepseek-v4-pro" | "deepseek-v4pro") => { DEFAULT_NVIDIA_NIM_MODEL.to_string() @@ -1483,6 +1501,57 @@ fn normalize_model_for_provider(provider: ProviderKind, model: &str) -> String { } } +fn canonical_openrouter_recent_model_id(model: &str) -> Option<&'static str> { + let normalized = model.trim().to_ascii_lowercase(); + let normalized = normalized.replace(['_', ' '], "-"); + match normalized.as_str() { + OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL + | "trinity" + | "trinity-large-thinking" + | "arcee-trinity" + | "arcee-trinity-large-thinking" => Some(OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL), + OPENROUTER_GEMMA_4_31B_MODEL | "gemma-4-31b" | "gemma-4-31b-it" => { + Some(OPENROUTER_GEMMA_4_31B_MODEL) + } + OPENROUTER_GEMMA_4_26B_A4B_MODEL | "gemma-4-26b-a4b" | "gemma-4-26b-a4b-it" => { + Some(OPENROUTER_GEMMA_4_26B_A4B_MODEL) + } + OPENROUTER_GLM_5_1_MODEL | "glm-5.1" | "glm-5-1" | "zai-glm-5.1" | "zai-glm-5-1" => { + Some(OPENROUTER_GLM_5_1_MODEL) + } + OPENROUTER_KIMI_K2_6_MODEL | "kimi-k2.6" | "kimi-k2-6" | "moonshot-kimi-k2.6" => { + Some(OPENROUTER_KIMI_K2_6_MODEL) + } + OPENROUTER_NEMOTRON_3_NANO_OMNI_MODEL + | "nemotron-3-nano-omni" + | "nemotron-3-nano-omni-reasoning" => Some(OPENROUTER_NEMOTRON_3_NANO_OMNI_MODEL), + OPENROUTER_QWEN_3_7_MAX_MODEL | "qwen3.7-max" | "qwen-3.7-max" | "qwen3-7-max" => { + Some(OPENROUTER_QWEN_3_7_MAX_MODEL) + } + OPENROUTER_QWEN_3_6_35B_A3B_MODEL + | "qwen3.6-35b-a3b" + | "qwen-3.6-35b-a3b" + | "qwen3-6-35b-a3b" => Some(OPENROUTER_QWEN_3_6_35B_A3B_MODEL), + OPENROUTER_QWEN_3_6_27B_MODEL | "qwen3.6-27b" | "qwen-3.6-27b" | "qwen3-6-27b" => { + Some(OPENROUTER_QWEN_3_6_27B_MODEL) + } + OPENROUTER_TENCENT_HY3_PREVIEW_MODEL | "hy3-preview" | "tencent-hy3-preview" => { + Some(OPENROUTER_TENCENT_HY3_PREVIEW_MODEL) + } + OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL + | "mimo-v2.5-pro" + | "mimo-v2-5-pro" + | "xiaomi-mimo-v2.5-pro" + | "xiaomi-mimo-v2-5-pro" => Some(OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL), + OPENROUTER_XIAOMI_MIMO_V2_5_MODEL + | "mimo-v2.5" + | "mimo-v2-5" + | "xiaomi-mimo-v2.5" + | "xiaomi-mimo-v2-5" => Some(OPENROUTER_XIAOMI_MIMO_V2_5_MODEL), + _ => None, + } +} + fn default_model_for_provider(provider: ProviderKind) -> &'static str { match provider { ProviderKind::Deepseek => DEFAULT_DEEPSEEK_MODEL, @@ -3758,6 +3827,36 @@ unix_socket_path = "/tmp/cw-hooks.sock" assert_eq!(resolved.model, DEFAULT_OPENROUTER_FLASH_MODEL); } + #[test] + fn openrouter_provider_normalizes_recent_large_model_aliases() { + let _lock = env_lock(); + let _env = EnvGuard::without_deepseek_runtime_overrides(); + + for (alias, expected) in [ + ( + "trinity-large-thinking", + OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL, + ), + ("qwen3.7-max", OPENROUTER_QWEN_3_7_MAX_MODEL), + ("qwen3.6-35b-a3b", OPENROUTER_QWEN_3_6_35B_A3B_MODEL), + ("mimo-v2.5-pro", OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL), + ("kimi-k2.6", OPENROUTER_KIMI_K2_6_MODEL), + ("gemma-4-31b-it", OPENROUTER_GEMMA_4_31B_MODEL), + ("glm-5.1", OPENROUTER_GLM_5_1_MODEL), + ] { + let cli = CliRuntimeOverrides { + provider: Some(ProviderKind::Openrouter), + model: Some(alias.to_string()), + ..CliRuntimeOverrides::default() + }; + + let resolved = ConfigToml::default().resolve_runtime_options(&cli); + + assert_eq!(resolved.provider, ProviderKind::Openrouter); + assert_eq!(resolved.model, expected); + } + } + #[test] fn novita_provider_normalizes_flash_aliases() { let _lock = env_lock(); diff --git a/crates/tui/src/config.rs b/crates/tui/src/config.rs index 1582c456..d2605798 100644 --- a/crates/tui/src/config.rs +++ b/crates/tui/src/config.rs @@ -48,6 +48,33 @@ pub const DEFAULT_VOLCENGINE_BASE_URL: &str = "https://ark.cn-beijing.volces.com pub const DEFAULT_WANJIE_ARK_BASE_URL: &str = "https://maas-openapi.wanjiedata.com/api/v1"; pub const DEFAULT_OPENROUTER_MODEL: &str = "deepseek/deepseek-v4-pro"; pub const DEFAULT_OPENROUTER_FLASH_MODEL: &str = "deepseek/deepseek-v4-flash"; +pub const OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL: &str = "arcee-ai/trinity-large-thinking"; +pub const OPENROUTER_GEMMA_4_31B_MODEL: &str = "google/gemma-4-31b-it"; +pub const OPENROUTER_GEMMA_4_26B_A4B_MODEL: &str = "google/gemma-4-26b-a4b-it"; +pub const OPENROUTER_GLM_5_1_MODEL: &str = "z-ai/glm-5.1"; +pub const OPENROUTER_KIMI_K2_6_MODEL: &str = "moonshotai/kimi-k2.6"; +pub const OPENROUTER_NEMOTRON_3_NANO_OMNI_MODEL: &str = + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free"; +pub const OPENROUTER_QWEN_3_7_MAX_MODEL: &str = "qwen/qwen3.7-max"; +pub const OPENROUTER_QWEN_3_6_35B_A3B_MODEL: &str = "qwen/qwen3.6-35b-a3b"; +pub const OPENROUTER_QWEN_3_6_27B_MODEL: &str = "qwen/qwen3.6-27b"; +pub const OPENROUTER_TENCENT_HY3_PREVIEW_MODEL: &str = "tencent/hy3-preview"; +pub const OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL: &str = "xiaomi/mimo-v2.5-pro"; +pub const OPENROUTER_XIAOMI_MIMO_V2_5_MODEL: &str = "xiaomi/mimo-v2.5"; +pub const RECENT_OPENROUTER_LARGE_MODELS: &[&str] = &[ + OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL, + OPENROUTER_QWEN_3_7_MAX_MODEL, + OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL, + OPENROUTER_XIAOMI_MIMO_V2_5_MODEL, + OPENROUTER_QWEN_3_6_35B_A3B_MODEL, + OPENROUTER_QWEN_3_6_27B_MODEL, + OPENROUTER_KIMI_K2_6_MODEL, + OPENROUTER_GLM_5_1_MODEL, + OPENROUTER_TENCENT_HY3_PREVIEW_MODEL, + OPENROUTER_GEMMA_4_31B_MODEL, + OPENROUTER_GEMMA_4_26B_A4B_MODEL, + OPENROUTER_NEMOTRON_3_NANO_OMNI_MODEL, +]; pub const DEFAULT_OPENROUTER_BASE_URL: &str = "https://openrouter.ai/api/v1"; pub const DEFAULT_XIAOMI_MIMO_MODEL: &str = "mimo-v2.5-pro"; pub const DEFAULT_XIAOMI_MIMO_BASE_URL: &str = "https://api.xiaomimimo.com/v1"; @@ -339,12 +366,15 @@ pub fn provider_capability(provider: ApiProvider, resolved_model: &str) -> Provi let max_output = if is_v4_pro || is_v4_flash { 384_000 } else { - 4096 + crate::models::max_output_tokens_for_model(resolved_model).unwrap_or(4096) }; // Thinking support: V4 models support thinking on all providers, but // only when the model name matches the V4 family. - let thinking_supported = is_v4_pro || is_v4_flash || is_reasoner; + let thinking_supported = is_v4_pro + || is_v4_flash + || is_reasoner + || crate::models::model_supports_reasoning(resolved_model); // Cache telemetry: returned only by DeepSeek-native and NVIDIA NIM endpoints. let cache_telemetry_supported = matches!( @@ -457,6 +487,57 @@ fn canonical_official_deepseek_model_id(model: &str) -> Option<&'static str> { } } +fn canonical_openrouter_recent_model_id(model: &str) -> Option<&'static str> { + let normalized = model.trim().to_ascii_lowercase(); + let normalized = normalized.replace(['_', ' '], "-"); + match normalized.as_str() { + OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL + | "trinity" + | "trinity-large-thinking" + | "arcee-trinity" + | "arcee-trinity-large-thinking" => Some(OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL), + OPENROUTER_GEMMA_4_31B_MODEL | "gemma-4-31b" | "gemma-4-31b-it" => { + Some(OPENROUTER_GEMMA_4_31B_MODEL) + } + OPENROUTER_GEMMA_4_26B_A4B_MODEL | "gemma-4-26b-a4b" | "gemma-4-26b-a4b-it" => { + Some(OPENROUTER_GEMMA_4_26B_A4B_MODEL) + } + OPENROUTER_GLM_5_1_MODEL | "glm-5.1" | "glm-5-1" | "zai-glm-5.1" | "zai-glm-5-1" => { + Some(OPENROUTER_GLM_5_1_MODEL) + } + OPENROUTER_KIMI_K2_6_MODEL | "kimi-k2.6" | "kimi-k2-6" | "moonshot-kimi-k2.6" => { + Some(OPENROUTER_KIMI_K2_6_MODEL) + } + OPENROUTER_NEMOTRON_3_NANO_OMNI_MODEL + | "nemotron-3-nano-omni" + | "nemotron-3-nano-omni-reasoning" => Some(OPENROUTER_NEMOTRON_3_NANO_OMNI_MODEL), + OPENROUTER_QWEN_3_7_MAX_MODEL | "qwen3.7-max" | "qwen-3.7-max" | "qwen3-7-max" => { + Some(OPENROUTER_QWEN_3_7_MAX_MODEL) + } + OPENROUTER_QWEN_3_6_35B_A3B_MODEL + | "qwen3.6-35b-a3b" + | "qwen-3.6-35b-a3b" + | "qwen3-6-35b-a3b" => Some(OPENROUTER_QWEN_3_6_35B_A3B_MODEL), + OPENROUTER_QWEN_3_6_27B_MODEL | "qwen3.6-27b" | "qwen-3.6-27b" | "qwen3-6-27b" => { + Some(OPENROUTER_QWEN_3_6_27B_MODEL) + } + OPENROUTER_TENCENT_HY3_PREVIEW_MODEL | "hy3-preview" | "tencent-hy3-preview" => { + Some(OPENROUTER_TENCENT_HY3_PREVIEW_MODEL) + } + OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL + | "mimo-v2.5-pro" + | "mimo-v2-5-pro" + | "xiaomi-mimo-v2.5-pro" + | "xiaomi-mimo-v2-5-pro" => Some(OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL), + OPENROUTER_XIAOMI_MIMO_V2_5_MODEL + | "mimo-v2.5" + | "mimo-v2-5" + | "xiaomi-mimo-v2.5" + | "xiaomi-mimo-v2-5" => Some(OPENROUTER_XIAOMI_MIMO_V2_5_MODEL), + _ => None, + } +} + /// Normalize a model selected through the TUI for the active provider. /// /// Official DeepSeek endpoints require bare model IDs. Provider-prefixed @@ -469,6 +550,12 @@ fn canonical_official_deepseek_model_id(model: &str) -> Option<&'static str> { /// aliases like `deepseek-v4pro` → `deepseek-v4-pro`. #[must_use] pub fn normalize_model_name_for_provider(provider: ApiProvider, model: &str) -> Option { + if matches!(provider, ApiProvider::Openrouter) + && let Some(canonical) = canonical_openrouter_recent_model_id(model) + { + return Some(canonical.to_string()); + } + let normalized = normalize_model_name(model)?; if matches!(provider, ApiProvider::Deepseek | ApiProvider::DeepseekCN) && let Some(canonical) = canonical_official_deepseek_model_id(&normalized) @@ -500,7 +587,11 @@ pub fn model_completion_names_for_provider(provider: ApiProvider) -> Vec<&'stati match provider { ApiProvider::Deepseek | ApiProvider::DeepseekCN => OFFICIAL_DEEPSEEK_MODELS.to_vec(), ApiProvider::NvidiaNim => vec![DEFAULT_NVIDIA_NIM_MODEL, DEFAULT_NVIDIA_NIM_FLASH_MODEL], - ApiProvider::Openrouter => vec![DEFAULT_OPENROUTER_MODEL, DEFAULT_OPENROUTER_FLASH_MODEL], + ApiProvider::Openrouter => { + let mut models = vec![DEFAULT_OPENROUTER_MODEL, DEFAULT_OPENROUTER_FLASH_MODEL]; + models.extend_from_slice(RECENT_OPENROUTER_LARGE_MODELS); + models + } ApiProvider::XiaomiMimo => vec![DEFAULT_XIAOMI_MIMO_MODEL, "mimo-v2.5"], ApiProvider::Novita => vec![DEFAULT_NOVITA_MODEL, DEFAULT_NOVITA_FLASH_MODEL], ApiProvider::Fireworks => vec![DEFAULT_FIREWORKS_MODEL], @@ -3368,6 +3459,11 @@ fn normalize_model_config(config: &mut Config) { } fn normalize_model_for_provider(provider: ApiProvider, model: &str) -> Option { + if matches!(provider, ApiProvider::Openrouter) + && let Some(canonical) = canonical_openrouter_recent_model_id(model) + { + return Some(canonical.to_string()); + } if provider_passes_model_through(provider) { return None; } @@ -6099,6 +6195,27 @@ api_key = "old-openrouter-key" ); } + #[test] + fn normalize_model_name_for_provider_maps_recent_openrouter_aliases() { + for (alias, expected) in [ + ( + "trinity-large-thinking", + OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL, + ), + ("qwen3.7-max", OPENROUTER_QWEN_3_7_MAX_MODEL), + ("qwen3.6-35b-a3b", OPENROUTER_QWEN_3_6_35B_A3B_MODEL), + ("mimo-v2.5-pro", OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL), + ("kimi-k2.6", OPENROUTER_KIMI_K2_6_MODEL), + ("gemma-4-31b-it", OPENROUTER_GEMMA_4_31B_MODEL), + ("glm-5.1", OPENROUTER_GLM_5_1_MODEL), + ] { + assert_eq!( + normalize_model_name_for_provider(ApiProvider::Openrouter, alias).as_deref(), + Some(expected) + ); + } + } + #[test] fn model_completion_names_for_deepseek_api_are_deduplicated_bare_ids() { assert_eq!( @@ -6107,6 +6224,23 @@ api_key = "old-openrouter-key" ); } + #[test] + fn model_completion_names_for_openrouter_include_recent_large_models() { + let models = model_completion_names_for_provider(ApiProvider::Openrouter); + + for expected in [ + DEFAULT_OPENROUTER_MODEL, + DEFAULT_OPENROUTER_FLASH_MODEL, + OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL, + OPENROUTER_QWEN_3_7_MAX_MODEL, + OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL, + OPENROUTER_QWEN_3_6_35B_A3B_MODEL, + OPENROUTER_GEMMA_4_31B_MODEL, + ] { + assert!(models.contains(&expected), "missing {expected}"); + } + } + #[test] fn model_completion_names_for_moonshot_excludes_oauth_only_kimi_code_model() { assert_eq!( @@ -8181,6 +8315,30 @@ model = "deepseek-ai/deepseek-v4-pro" ); } + #[test] + fn provider_capability_openrouter_recent_large_models_are_reasoning_aware() { + for (model, expected_window, expected_output) in [ + ( + OPENROUTER_ARCEE_TRINITY_LARGE_THINKING_MODEL, + 262_144, + 262_144, + ), + (OPENROUTER_QWEN_3_7_MAX_MODEL, 1_000_000, 65_536), + (OPENROUTER_XIAOMI_MIMO_V2_5_PRO_MODEL, 1_000_000, 131_072), + ] { + let cap = provider_capability(ApiProvider::Openrouter, model); + + assert_eq!(cap.context_window, expected_window); + assert_eq!(cap.max_output, expected_output); + assert!(cap.thinking_supported); + assert!(!cap.cache_telemetry_supported); + assert_eq!( + cap.request_payload_mode, + RequestPayloadMode::ChatCompletions + ); + } + } + #[test] fn provider_capability_xiaomi_mimo_has_thinking_no_cache() { let cap = provider_capability(ApiProvider::XiaomiMimo, DEFAULT_XIAOMI_MIMO_MODEL); diff --git a/crates/tui/src/models.rs b/crates/tui/src/models.rs index 91c642e1..a94cd762 100644 --- a/crates/tui/src/models.rs +++ b/crates/tui/src/models.rs @@ -229,12 +229,79 @@ pub fn context_window_for_model(model: &str) -> Option { } return Some(LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS); } + if let Some(window) = known_context_window_for_model(&lower) { + return Some(window); + } if lower.contains("claude") { return Some(200_000); } None } +fn known_context_window_for_model(model_lower: &str) -> Option { + match model_lower { + "arcee-ai/trinity-large-thinking" => Some(262_144), + "google/gemma-4-31b-it" + | "google/gemma-4-31b-it:free" + | "google/gemma-4-26b-a4b-it" + | "google/gemma-4-26b-a4b-it:free" + | "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free" + | "qwen/qwen3.6-35b-a3b" + | "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), + "qwen/qwen3.7-max" | "xiaomi/mimo-v2.5-pro" | "xiaomi/mimo-v2.5" | "qwen/qwen3.6-plus" => { + Some(1_000_000) + } + _ => None, + } +} + +#[must_use] +pub fn max_output_tokens_for_model(model: &str) -> Option { + let lower = model.to_lowercase(); + if lower.contains("deepseek") && lower.contains("v4") { + return Some(384_000); + } + match lower.as_str() { + "arcee-ai/trinity-large-thinking" | "moonshotai/kimi-k2.6" => Some(262_144), + "qwen/qwen3.6-35b-a3b" | "qwen/qwen3.6-27b" => Some(262_140), + "xiaomi/mimo-v2.5-pro" | "xiaomi/mimo-v2.5" => Some(131_072), + "qwen/qwen3.7-max" | "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free" => Some(65_536), + "google/gemma-4-31b-it" => Some(16_384), + "google/gemma-4-31b-it:free" | "google/gemma-4-26b-a4b-it:free" => Some(32_768), + _ => None, + } +} + +#[must_use] +pub fn model_supports_reasoning(model: &str) -> bool { + let lower = model.to_lowercase(); + if lower.contains("deepseek") && lower.contains("v4") { + return true; + } + matches!( + lower.as_str(), + "arcee-ai/trinity-large-thinking" + | "google/gemma-4-31b-it" + | "google/gemma-4-31b-it:free" + | "google/gemma-4-26b-a4b-it" + | "google/gemma-4-26b-a4b-it:free" + | "moonshotai/kimi-k2.6" + | "moonshotai/kimi-k2.6:free" + | "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free" + | "qwen/qwen3.7-max" + | "qwen/qwen3.6-35b-a3b" + | "qwen/qwen3.6-27b" + | "tencent/hy3-preview" + | "xiaomi/mimo-v2.5-pro" + | "xiaomi/mimo-v2.5" + | "z-ai/glm-5.1" + ) +} + /// Parse an explicit `_Nk` context-window hint from a model name (vendor /// agnostic). Returns the window in tokens for `N` in `8..=1024`. fn explicit_context_window_hint(model_lower: &str) -> Option { @@ -419,6 +486,38 @@ mod tests { ); } + #[test] + fn recent_openrouter_large_models_have_static_windows() { + for (model, expected_window) in [ + ("arcee-ai/trinity-large-thinking", 262_144), + (concat!("qwen/", "qwen3.7-max"), 1_000_000), + (concat!("qwen/", "qwen3.6-35b-a3b"), 262_144), + (concat!("xiaomi/", "mimo-v2.5-pro"), 1_000_000), + ("moonshotai/kimi-k2.6", 262_144), + ("google/gemma-4-31b-it", 262_144), + ("z-ai/glm-5.1", 202_752), + ] { + assert_eq!(context_window_for_model(model), Some(expected_window)); + assert!(model_supports_reasoning(model)); + } + } + + #[test] + fn recent_openrouter_large_models_have_known_output_caps() { + assert_eq!( + max_output_tokens_for_model("arcee-ai/trinity-large-thinking"), + Some(262_144) + ); + assert_eq!( + max_output_tokens_for_model(concat!("qwen/", "qwen3.7-max")), + Some(65_536) + ); + assert_eq!( + max_output_tokens_for_model(concat!("xiaomi/", "mimo-v2.5-pro")), + Some(131_072) + ); + } + #[test] fn deepseek_models_with_k_suffix_use_hint() { assert_eq!(context_window_for_model("deepseek-v3.2-32k"), Some(32_000)); diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 6377b7fb..cc1f4c62 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -546,7 +546,7 @@ If you are upgrading from older releases: - `provider` (string, optional): `deepseek` (default), `nvidia-nim`, `openai`, `atlascloud`, `wanjie-ark`, `openrouter`, `xiaomi-mimo`, `novita`, `fireworks`, `siliconflow`, `moonshot`, `sglang`, `vllm`, or `ollama`. Legacy `deepseek-cn` configs are still accepted as an alias for `deepseek`; DeepSeek uses the same official host [`https://api.deepseek.com`](https://api-docs.deepseek.com/) worldwide. `nvidia-nim` targets NVIDIA's NIM-hosted DeepSeek endpoints through `https://integrate.api.nvidia.com/v1`; `openai` targets a generic OpenAI-compatible endpoint, defaulting to `https://api.openai.com/v1`; `atlascloud` targets AtlasCloud's OpenAI-compatible endpoint at `https://api.atlascloud.ai/v1`; `wanjie-ark` targets Wanjie Ark's OpenAI-compatible endpoint at `https://maas-openapi.wanjiedata.com/api/v1`; `openrouter` targets `https://openrouter.ai/api/v1`; `xiaomi-mimo` targets Xiaomi MiMo's OpenAI-compatible endpoint at `https://api.xiaomimimo.com/v1`; `novita` targets `https://api.novita.ai/v1`; `fireworks` targets `https://api.fireworks.ai/inference/v1`; `siliconflow` targets SiliconFlow, defaulting to `https://api.siliconflow.com/v1`; `moonshot` targets Moonshot/Kimi, defaulting to `https://api.moonshot.ai/v1`; `sglang` targets a self-hosted OpenAI-compatible endpoint, defaulting to `http://localhost:30000/v1`; `vllm` targets a self-hosted vLLM OpenAI-compatible endpoint, defaulting to `http://localhost:8000/v1`; `ollama` targets Ollama's OpenAI-compatible endpoint, defaulting to `http://localhost:11434/v1`. - `api_key` (string, required for hosted providers): must be non-empty for DeepSeek/hosted providers (or set the provider API key env var). Self-hosted SGLang, vLLM, and Ollama can omit it. - `base_url` (string, optional): defaults to `https://api.deepseek.com/beta` for DeepSeek's OpenAI-compatible Chat Completions API, including legacy `provider = "deepseek-cn"` configs. Other defaults are `https://integrate.api.nvidia.com/v1` for `nvidia-nim`, `https://api.openai.com/v1` for `openai`, `https://api.atlascloud.ai/v1` for `atlascloud`, `https://maas-openapi.wanjiedata.com/api/v1` for `wanjie-ark`, `https://openrouter.ai/api/v1` for `openrouter`, `https://api.xiaomimimo.com/v1` for `xiaomi-mimo`, `https://api.novita.ai/v1` for `novita`, `https://api.fireworks.ai/inference/v1` for `fireworks`, `https://api.siliconflow.com/v1` for `siliconflow`, `https://api.moonshot.ai/v1` for `moonshot`, `http://localhost:30000/v1` for `sglang`, `http://localhost:8000/v1` for `vllm`, and `http://localhost:11434/v1` for `ollama`. Set `https://api.deepseek.com` or `https://api.deepseek.com/v1` explicitly to opt out of DeepSeek beta features. -- `default_text_model` (string, optional): defaults to `deepseek-v4-pro` for DeepSeek and generic OpenAI-compatible endpoints, `deepseek-ai/deepseek-v4-pro` for NVIDIA NIM, `deepseek-ai/deepseek-v4-flash` for AtlasCloud, `deepseek-reasoner` for Wanjie Ark, `deepseek/deepseek-v4-pro` for OpenRouter and Novita, `mimo-v2.5-pro` for Xiaomi MiMo, `accounts/fireworks/models/deepseek-v4-pro` for Fireworks, `deepseek-ai/DeepSeek-V4-Pro` for SiliconFlow, `kimi-k2.6` for Moonshot, `deepseek-ai/DeepSeek-V4-Pro` for SGLang/vLLM, and `deepseek-coder:1.3b` for Ollama. Current public DeepSeek IDs are `deepseek-v4-pro` and `deepseek-v4-flash`, both with 1M context windows, 384K max output, and thinking mode enabled by default. Legacy `deepseek-chat` and `deepseek-reasoner` remain compatibility aliases for `deepseek-v4-flash` until July 24, 2026, except SiliconFlow maps `deepseek-reasoner` and `deepseek-r1` to its Pro model while `deepseek-chat` and `deepseek-v3` map to Flash. Provider-specific mappings translate `deepseek-v4-pro` / `deepseek-v4-flash` to each provider's model ID where supported. Generic `openai`, `atlascloud`, `wanjie-ark`, `xiaomi-mimo`, and Ollama model IDs are passed through unchanged. OpenRouter and SiliconFlow provider configs with a custom `base_url` also preserve explicit model values, which lets OpenAI-compatible gateways accept bare model IDs. Use `/models` or `codewhale models` to discover live IDs from your configured endpoint. `CODEWHALE_MODEL` overrides this for a single process; `DEEPSEEK_MODEL` is the legacy alias. +- `default_text_model` (string, optional): defaults to `deepseek-v4-pro` for DeepSeek and generic OpenAI-compatible endpoints, `deepseek-ai/deepseek-v4-pro` for NVIDIA NIM, `deepseek-ai/deepseek-v4-flash` for AtlasCloud, `deepseek-reasoner` for Wanjie Ark, `deepseek/deepseek-v4-pro` for OpenRouter and Novita, `mimo-v2.5-pro` for Xiaomi MiMo, `accounts/fireworks/models/deepseek-v4-pro` for Fireworks, `deepseek-ai/DeepSeek-V4-Pro` for SiliconFlow, `kimi-k2.6` for Moonshot, `deepseek-ai/DeepSeek-V4-Pro` for SGLang/vLLM, and `deepseek-coder:1.3b` for Ollama. Current public DeepSeek IDs are `deepseek-v4-pro` and `deepseek-v4-flash`, both with 1M context windows, 384K max output, and thinking mode enabled by default. Legacy `deepseek-chat` and `deepseek-reasoner` remain compatibility aliases for `deepseek-v4-flash` until July 24, 2026, except SiliconFlow maps `deepseek-reasoner` and `deepseek-r1` to its Pro model while `deepseek-chat` and `deepseek-v3` map to Flash. Provider-specific mappings translate `deepseek-v4-pro` / `deepseek-v4-flash` to each provider's model ID where supported. OpenRouter also recognizes recent large IDs such as `arcee-ai/trinity-large-thinking`, `qwen/qwen3.7-max`, `xiaomi/mimo-v2.5-pro`, `qwen/qwen3.6-35b-a3b`, `google/gemma-4-31b-it`, and `moonshotai/kimi-k2.6`. Generic `openai`, `atlascloud`, `wanjie-ark`, `xiaomi-mimo`, and Ollama model IDs are passed through unchanged. OpenRouter and SiliconFlow provider configs with a custom `base_url` also preserve explicit model values, which lets OpenAI-compatible gateways accept bare model IDs. Use `/models` or `codewhale models` to discover live IDs from your configured endpoint. `CODEWHALE_MODEL` overrides this for a single process; `DEEPSEEK_MODEL` is the legacy alias. - `reasoning_effort` (string, optional): `off`, `low`, `medium`, `high`, or `max`; defaults to the configured UI tier. DeepSeek Platform receives top-level `thinking` / `reasoning_effort` fields. NVIDIA NIM receives equivalent settings through `chat_template_kwargs`. - `allow_shell` (bool, optional): defaults to `true` (sandboxed). - `approval_policy` (string, optional): `on-request`, `untrusted`, or `never`. Runtime `approval_mode` editing in `/config` also accepts `on-request` and `untrusted` aliases. diff --git a/docs/PROVIDERS.md b/docs/PROVIDERS.md index abfa1bbe..82eacf40 100644 --- a/docs/PROVIDERS.md +++ b/docs/PROVIDERS.md @@ -117,7 +117,7 @@ endpoint. | `atlascloud` | `[providers.atlascloud]` | `ATLASCLOUD_API_KEY` | `ATLASCLOUD_BASE_URL`; default `https://api.atlascloud.ai/v1` | `deepseek-ai/deepseek-v4-flash`, `deepseek-ai/deepseek-v4-pro` | OpenAI-compatible hosted route. `ATLASCLOUD_MODEL` is accepted by the TUI config path, and the static `ModelRegistry` includes AtlasCloud fallback rows for CLI model resolution. | | `wanjie-ark` | `[providers.wanjie_ark]` | `WANJIE_ARK_API_KEY`, `WANJIE_API_KEY`, `WANJIE_MAAS_API_KEY` | `WANJIE_ARK_BASE_URL`, `WANJIE_BASE_URL`, `WANJIE_MAAS_BASE_URL`; default `https://maas-openapi.wanjiedata.com/api/v1` | `deepseek-reasoner` | OpenAI-compatible hosted route. `WANJIE_ARK_MODEL`, `WANJIE_MODEL`, and `WANJIE_MAAS_MODEL` are accepted. | | `volcengine` | `[providers.volcengine]` | `VOLCENGINE_API_KEY`, `VOLCENGINE_ARK_API_KEY`, `ARK_API_KEY` | `VOLCENGINE_BASE_URL`, `VOLCENGINE_ARK_BASE_URL`, `ARK_BASE_URL`; default `https://ark.cn-beijing.volces.com/api/coding/v3` | `DeepSeek-V4-Pro`, `DeepSeek-V4-Flash` | Volcengine/Volcano Engine Ark OpenAI-compatible coding endpoint. `VOLCENGINE_MODEL` and `VOLCENGINE_ARK_MODEL` are accepted. | -| `openrouter` | `[providers.openrouter]` | `OPENROUTER_API_KEY` | `OPENROUTER_BASE_URL`; default `https://openrouter.ai/api/v1` | `deepseek/deepseek-v4-pro`, `deepseek/deepseek-v4-flash` | Additive open-model routing layer. It does not replace DeepSeek; it lets users route supported model IDs through OpenRouter when they choose it. | +| `openrouter` | `[providers.openrouter]` | `OPENROUTER_API_KEY` | `OPENROUTER_BASE_URL`; default `https://openrouter.ai/api/v1` | `deepseek/deepseek-v4-pro`, `deepseek/deepseek-v4-flash`; recent large IDs include `arcee-ai/trinity-large-thinking`, `qwen/qwen3.7-max`, `xiaomi/mimo-v2.5-pro`, `qwen/qwen3.6-35b-a3b`, `google/gemma-4-31b-it`, `z-ai/glm-5.1`, `moonshotai/kimi-k2.6` | Additive open-model routing layer. It does not replace DeepSeek; it lets users route supported model IDs through OpenRouter when they choose it. | | `xiaomi-mimo` | `[providers.xiaomi_mimo]` | `XIAOMI_MIMO_API_KEY`, `MIMO_API_KEY` | `XIAOMI_MIMO_BASE_URL`, `MIMO_BASE_URL`; default `https://api.xiaomimimo.com/v1` | `mimo-v2.5-pro`, `mimo-v2.5` | Xiaomi MiMo OpenAI-compatible chat completions route. It sends `max_completion_tokens` and uses MiMo's `thinking` field for reasoning control. | | `novita` | `[providers.novita]` | `NOVITA_API_KEY` | `NOVITA_BASE_URL`; default `https://api.novita.ai/v1` | `deepseek/deepseek-v4-pro`, `deepseek/deepseek-v4-flash` | OpenAI-compatible hosted route for DeepSeek model IDs. Use config or `CODEWHALE_MODEL` / `DEEPSEEK_MODEL` for model overrides. | | `fireworks` | `[providers.fireworks]` | `FIREWORKS_API_KEY` | `FIREWORKS_BASE_URL`; default `https://api.fireworks.ai/inference/v1` | `accounts/fireworks/models/deepseek-v4-pro` | OpenAI-compatible hosted route. Use config or `CODEWHALE_MODEL` / `DEEPSEEK_MODEL` for model overrides. | @@ -136,6 +136,18 @@ includes `mimo-v2.5` for image input. CodeWhale exposes image analysis through t separate `[vision_model]` / `image_analyze` path; set that model to `mimo-v2.5` when using MiMo for vision. +### Recent OpenRouter Large Models + +OpenRouter completions and static registry rows include the April 2026 onward +large open-weight or open-labeled models verified through OpenRouter's model +metadata: `arcee-ai/trinity-large-thinking`, `qwen/qwen3.6-35b-a3b`, +`qwen/qwen3.6-27b`, `xiaomi/mimo-v2.5-pro`, `xiaomi/mimo-v2.5`, +`moonshotai/kimi-k2.6`, `z-ai/glm-5.1`, `tencent/hy3-preview`, +`google/gemma-4-31b-it`, `google/gemma-4-26b-a4b-it`, and +`nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free`. `qwen/qwen3.7-max` +is also included because it is a current user-requested large OpenRouter model, +but it is treated as a hosted Qwen model rather than documented as open-weight. + ## Static Model Registry `codewhale model list` and `codewhale model resolve` use the static registry in @@ -151,7 +163,7 @@ endpoint when the endpoint supports model listing. | `atlascloud` | `deepseek-ai/deepseek-v4-flash`, `deepseek-ai/deepseek-v4-pro` | yes | yes | | `wanjie-ark` | `deepseek-reasoner` | yes | yes | | `volcengine` | `DeepSeek-V4-Pro`, `DeepSeek-V4-Flash` | yes | yes | -| `openrouter` | `deepseek/deepseek-v4-pro`, `deepseek/deepseek-v4-flash` | yes | yes | +| `openrouter` | `deepseek/deepseek-v4-pro`, `deepseek/deepseek-v4-flash`, `arcee-ai/trinity-large-thinking`, `qwen/qwen3.7-max`, `xiaomi/mimo-v2.5-pro`, `xiaomi/mimo-v2.5`, `qwen/qwen3.6-35b-a3b`, `qwen/qwen3.6-27b`, `moonshotai/kimi-k2.6`, `z-ai/glm-5.1`, `tencent/hy3-preview`, `google/gemma-4-31b-it`, `google/gemma-4-26b-a4b-it`, `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free` | yes | yes | | `xiaomi-mimo` | `mimo-v2.5-pro`, `mimo-v2.5` | yes | yes | | `novita` | `deepseek/deepseek-v4-pro`, `deepseek/deepseek-v4-flash` | yes | yes | | `fireworks` | `accounts/fireworks/models/deepseek-v4-pro` | yes | yes |