vtcode_config/models/model_id/
capabilities.rs1use super::ModelId;
2
3impl ModelId {
4 pub fn non_reasoning_variant(&self) -> Option<Self> {
6 if let Some(meta) = self.openrouter_metadata() {
7 if !meta.reasoning {
8 return None;
9 }
10
11 let vendor = meta.vendor;
12 let mut candidates: Vec<Self> = Self::openrouter_vendor_groups()
13 .into_iter()
14 .find(|(candidate_vendor, _)| *candidate_vendor == vendor)
15 .map(|(_, models)| {
16 models
17 .iter()
18 .copied()
19 .filter(|candidate| candidate != self)
20 .filter(|candidate| {
21 candidate
22 .openrouter_metadata()
23 .map(|other| !other.reasoning)
24 .unwrap_or(false)
25 })
26 .collect()
27 })
28 .unwrap_or_default();
29
30 if candidates.is_empty() {
31 return None;
32 }
33
34 candidates.sort_by_key(|candidate| {
35 candidate
36 .openrouter_metadata()
37 .map(|data| (!data.efficient, data.display))
38 .unwrap_or((true, ""))
39 });
40
41 return candidates.into_iter().next();
42 }
43
44 let direct = match self {
45 ModelId::Gemini31ProPreview | ModelId::Gemini31ProPreviewCustomTools => {
46 Some(ModelId::Gemini3FlashPreview)
47 }
48 ModelId::GPT52 | ModelId::GPT5 => Some(ModelId::GPT5Mini),
49 ModelId::DeepSeekReasoner => Some(ModelId::DeepSeekChat),
50 ModelId::ZaiGlm5 => Some(ModelId::OllamaGlm5Cloud),
51 ModelId::ClaudeOpus46
52 | ModelId::ClaudeSonnet46
53 | ModelId::ClaudeOpus45
54 | ModelId::ClaudeOpus41 => Some(ModelId::ClaudeSonnet45),
55 ModelId::ClaudeSonnet4 => Some(ModelId::ClaudeSonnet45),
56 ModelId::MinimaxM25 => Some(ModelId::MinimaxM2),
57 _ => None,
58 };
59
60 direct.and_then(|candidate| {
61 if candidate.supports_reasoning_effort() {
62 None
63 } else {
64 Some(candidate)
65 }
66 })
67 }
68
69 pub fn is_flash_variant(&self) -> bool {
71 matches!(
72 self,
73 ModelId::Gemini3FlashPreview
74 | ModelId::OpenRouterStepfunStep35FlashFree
75 | ModelId::OllamaGemini3FlashPreviewCloud
76 )
77 }
78
79 pub fn is_pro_variant(&self) -> bool {
81 matches!(
82 self,
83 ModelId::Gemini31ProPreview
84 | ModelId::Gemini31ProPreviewCustomTools
85 | ModelId::OpenRouterGoogleGemini31ProPreview
86 | ModelId::GPT5
87 | ModelId::GPT52
88 | ModelId::GPT53Codex
89 | ModelId::ClaudeOpus46
90 | ModelId::ClaudeSonnet46
91 | ModelId::ClaudeOpus41
92 | ModelId::DeepSeekReasoner
93 | ModelId::ZaiGlm5
94 | ModelId::OpenRouterStepfunStep35FlashFree
95 | ModelId::MinimaxM25
96 | ModelId::OllamaGlm5Cloud
97 | ModelId::OllamaMinimaxM25Cloud
98 | ModelId::HuggingFaceQwen3CoderNextNovita
99 | ModelId::HuggingFaceQwen35397BA17BTogether
100 )
101 }
102
103 pub fn is_efficient_variant(&self) -> bool {
105 if let Some(meta) = self.openrouter_metadata() {
106 return meta.efficient;
107 }
108 matches!(
109 self,
110 ModelId::Gemini3FlashPreview
111 | ModelId::GPT5Mini
112 | ModelId::GPT5Nano
113 | ModelId::ClaudeHaiku45
114 | ModelId::DeepSeekChat
115 )
116 }
117
118 pub fn is_top_tier(&self) -> bool {
120 if let Some(meta) = self.openrouter_metadata() {
121 return meta.top_tier;
122 }
123 matches!(
124 self,
125 ModelId::Gemini31ProPreview
126 | ModelId::Gemini31ProPreviewCustomTools
127 | ModelId::OpenRouterGoogleGemini31ProPreview
128 | ModelId::Gemini3FlashPreview
129 | ModelId::GPT5
130 | ModelId::GPT52
131 | ModelId::GPT53Codex
132 | ModelId::ClaudeOpus46
133 | ModelId::ClaudeSonnet46
134 | ModelId::ClaudeOpus45
135 | ModelId::ClaudeOpus41
136 | ModelId::ClaudeSonnet45
137 | ModelId::ClaudeSonnet4
138 | ModelId::DeepSeekReasoner
139 | ModelId::ZaiGlm5
140 | ModelId::OpenRouterStepfunStep35FlashFree
141 | ModelId::HuggingFaceQwen3CoderNextNovita
142 | ModelId::HuggingFaceQwen35397BA17BTogether
143 )
144 }
145
146 pub fn is_reasoning_variant(&self) -> bool {
148 if let Some(meta) = self.openrouter_metadata() {
149 return meta.reasoning;
150 }
151 self.provider().supports_reasoning_effort(self.as_str())
152 }
153
154 pub fn supports_tool_calls(&self) -> bool {
156 if let Some(meta) = self.openrouter_metadata() {
157 return meta.tool_call;
158 }
159 true
160 }
161
162 pub fn generation(&self) -> &'static str {
164 if let Some(meta) = self.openrouter_metadata() {
165 return meta.generation;
166 }
167 match self {
168 ModelId::Gemini31ProPreview | ModelId::Gemini31ProPreviewCustomTools => "3.1",
170 ModelId::Gemini3FlashPreview => "3",
171 ModelId::GPT52 => "5.2",
173 ModelId::GPT53Codex => "5.3",
174 ModelId::GPT5
175 | ModelId::GPT5Mini
176 | ModelId::GPT5Nano
177 | ModelId::OpenAIGptOss20b
178 | ModelId::OpenAIGptOss120b => "5",
179 ModelId::ClaudeOpus46 | ModelId::ClaudeSonnet46 => "4.6",
181 ModelId::ClaudeOpus45 | ModelId::ClaudeSonnet45 | ModelId::ClaudeHaiku45 => "4.5",
182 ModelId::ClaudeOpus41 => "4.1",
183 ModelId::ClaudeSonnet4 => "4",
184 ModelId::DeepSeekChat | ModelId::DeepSeekReasoner => "V3.2-Exp",
186 ModelId::ZaiGlm5 => "5",
188 ModelId::OllamaGptOss20b => "oss",
189 ModelId::OllamaGptOss20bCloud => "oss-cloud",
190 ModelId::OllamaGptOss120bCloud => "oss-cloud",
191 ModelId::OllamaQwen317b => "oss",
192 ModelId::OllamaQwen3CoderNext => "qwen3-coder-next:cloud",
193 ModelId::OllamaDeepseekV32Cloud => "deepseek-v3.2",
194 ModelId::OllamaQwen3Next80bCloud => "qwen3-next",
195 ModelId::OllamaMistralLarge3675bCloud => "mistral-large-3",
196 ModelId::OllamaQwen3Coder480bCloud => "qwen3",
197 ModelId::OllamaDevstral2123bCloud => "devstral-2",
198 ModelId::OllamaMinimaxM2Cloud => "minimax-m2",
199 ModelId::OllamaNemotron3Nano30bCloud => "nemotron-3",
200 ModelId::OllamaGlm5Cloud => "glm-5",
201 ModelId::OllamaMinimaxM25Cloud => "minimax-m2.5",
202 ModelId::OllamaGemini3FlashPreviewCloud => "gemini-3",
203 ModelId::MinimaxM25 => "M2.5",
205 ModelId::MinimaxM2 => "m2",
206 ModelId::MoonshotMinimaxM25 | ModelId::OpenRouterMinimaxM25 => "M2.5",
208 ModelId::HuggingFaceDeepseekV32 => "V3.2-Exp",
210 ModelId::HuggingFaceOpenAIGptOss20b => "oss",
211 ModelId::HuggingFaceOpenAIGptOss120b => "oss",
212 ModelId::HuggingFaceMinimaxM25Novita => "m2.5",
213 ModelId::HuggingFaceDeepseekV32Novita => "v3.2",
214 ModelId::HuggingFaceXiaomiMimoV2FlashNovita => "v2-flash",
215 ModelId::HuggingFaceGlm5Novita => "5",
216 ModelId::HuggingFaceQwen3CoderNextNovita
217 | ModelId::OpenRouterQwen3CoderNext
218 | ModelId::MoonshotQwen3CoderNext => "qwen3-coder-next",
219 _ => "unknown",
220 }
221 }
222
223 pub fn supports_shell_tool(&self) -> bool {
225 matches!(self, ModelId::GPT52 | ModelId::GPT53Codex)
226 }
227
228 pub fn supports_apply_patch_tool(&self) -> bool {
230 false }
232}