Skip to main content

vtcode_config/models/model_id/
parse.rs

1use std::str::FromStr;
2
3use crate::models::ModelParseError;
4
5use super::ModelId;
6
7impl FromStr for ModelId {
8    type Err = ModelParseError;
9
10    fn from_str(s: &str) -> Result<Self, Self::Err> {
11        use crate::constants::models;
12
13        // Explicitly handle built-in models that might be shadowed by OpenRouter
14        if s == models::zai::GLM_5 {
15            return Ok(ModelId::ZaiGlm5);
16        }
17
18        if let Some(model) = Self::parse_openrouter_model(s) {
19            return Ok(model);
20        }
21
22        match s {
23            // Gemini models
24            s if s == models::GEMINI_3_PRO_PREVIEW => Ok(ModelId::Gemini3ProPreview),
25            s if s == models::GEMINI_3_FLASH_PREVIEW => Ok(ModelId::Gemini3FlashPreview),
26            // OpenAI models
27            s if s == models::GPT_5 => Ok(ModelId::GPT5),
28            s if s == models::openai::GPT_5_2 => Ok(ModelId::GPT52),
29            s if s == models::openai::GPT_5_2_ALIAS => Ok(ModelId::GPT52),
30            s if s == models::openai::GPT_5_2_CODEX => Ok(ModelId::GPT52Codex),
31            s if s == models::GPT_5_CODEX => Ok(ModelId::GPT5Codex),
32            s if s == models::GPT_5_MINI => Ok(ModelId::GPT5Mini),
33            s if s == models::GPT_5_NANO => Ok(ModelId::GPT5Nano),
34            s if s == models::openai::GPT_5_1 => Ok(ModelId::GPT51),
35            s if s == models::openai::GPT_5_1_CODEX => Ok(ModelId::GPT51Codex),
36            s if s == models::openai::GPT_5_1_CODEX_MAX => Ok(ModelId::GPT51CodexMax),
37            s if s == models::openai::GPT_5_1_MINI => Ok(ModelId::GPT51Mini),
38            s if s == models::CODEX_MINI_LATEST => Ok(ModelId::CodexMiniLatest),
39            s if s == models::openai::GPT_OSS_20B => Ok(ModelId::OpenAIGptOss20b),
40            s if s == models::openai::GPT_OSS_120B => Ok(ModelId::OpenAIGptOss120b),
41            // Anthropic models
42            s if s == models::CLAUDE_OPUS_4_6 => Ok(ModelId::ClaudeOpus46),
43            s if s == models::CLAUDE_OPUS_4_1_20250805 => Ok(ModelId::ClaudeOpus41),
44            s if s == models::CLAUDE_OPUS_4_1 => Ok(ModelId::ClaudeOpus41),
45            s if s == models::CLAUDE_OPUS_4_5_20251101 => Ok(ModelId::ClaudeOpus45),
46            s if s == models::CLAUDE_OPUS_4_5 => Ok(ModelId::ClaudeOpus45),
47            s if s == models::CLAUDE_SONNET_4_5 => Ok(ModelId::ClaudeSonnet45),
48            s if s == models::CLAUDE_HAIKU_4_5 => Ok(ModelId::ClaudeHaiku45),
49            s if s == models::CLAUDE_SONNET_4_5_20250929 => Ok(ModelId::ClaudeSonnet45),
50            s if s == models::CLAUDE_SONNET_4_20250514 => Ok(ModelId::ClaudeSonnet4),
51            s if s == models::CLAUDE_SONNET_4_0 => Ok(ModelId::ClaudeSonnet4),
52            s if s == models::CLAUDE_OPUS_4_20250514 => Ok(ModelId::ClaudeOpus4),
53            s if s == models::CLAUDE_OPUS_4_0 => Ok(ModelId::ClaudeOpus4),
54            s if s == models::CLAUDE_3_7_SONNET_20250219 => Ok(ModelId::ClaudeSonnet37),
55            s if s == models::CLAUDE_3_7_SONNET_LATEST => Ok(ModelId::ClaudeSonnet37),
56            s if s == models::CLAUDE_3_5_HAIKU_20241022 => Ok(ModelId::ClaudeHaiku35),
57            s if s == models::CLAUDE_3_5_HAIKU_LATEST => Ok(ModelId::ClaudeHaiku35),
58            // DeepSeek models
59            s if s == models::DEEPSEEK_CHAT => Ok(ModelId::DeepSeekChat),
60            s if s == models::DEEPSEEK_REASONER => Ok(ModelId::DeepSeekReasoner),
61            // xAI models
62            s if s == models::xai::GROK_4 => Ok(ModelId::XaiGrok4),
63            s if s == models::xai::GROK_4_MINI => Ok(ModelId::XaiGrok4Mini),
64            s if s == models::xai::GROK_4_CODE => Ok(ModelId::XaiGrok4Code),
65            s if s == models::xai::GROK_4_CODE_LATEST => Ok(ModelId::XaiGrok4CodeLatest),
66            s if s == models::xai::GROK_4_VISION => Ok(ModelId::XaiGrok4Vision),
67            // Z.AI models
68            s if s == models::zai::GLM_5 => Ok(ModelId::ZaiGlm5),
69            // Moonshot models
70            s if s == models::moonshot::MINIMAX_M2_5 => Ok(ModelId::MoonshotMinimaxM25),
71            s if s == models::moonshot::QWEN3_CODER_NEXT => Ok(ModelId::MoonshotQwen3CoderNext),
72            s if s == models::ollama::GPT_OSS_20B => Ok(ModelId::OllamaGptOss20b),
73            s if s == models::ollama::GPT_OSS_20B_CLOUD => Ok(ModelId::OllamaGptOss20bCloud),
74            s if s == models::ollama::GPT_OSS_120B_CLOUD => Ok(ModelId::OllamaGptOss120bCloud),
75            s if s == models::ollama::QWEN3_1_7B => Ok(ModelId::OllamaQwen317b),
76            s if s == models::ollama::DEEPSEEK_V32_CLOUD => Ok(ModelId::OllamaDeepseekV32Cloud),
77            s if s == models::ollama::QWEN3_NEXT_80B_CLOUD => Ok(ModelId::OllamaQwen3Next80bCloud),
78            s if s == models::ollama::MISTRAL_LARGE_3_675B_CLOUD => {
79                Ok(ModelId::OllamaMistralLarge3675bCloud)
80            }
81            s if s == models::ollama::QWEN3_CODER_480B_CLOUD => {
82                Ok(ModelId::OllamaQwen3Coder480bCloud)
83            }
84            s if s == models::ollama::GLM_5_CLOUD => Ok(ModelId::OllamaGlm5Cloud),
85            s if s == models::ollama::GEMINI_3_PRO_PREVIEW_LATEST_CLOUD => {
86                Ok(ModelId::OllamaGemini3ProPreviewLatestCloud)
87            }
88            s if s == models::ollama::GEMINI_3_FLASH_PREVIEW_CLOUD => {
89                Ok(ModelId::OllamaGemini3FlashPreviewCloud)
90            }
91            s if s == models::ollama::MINIMAX_M2_CLOUD => Ok(ModelId::OllamaMinimaxM2Cloud),
92            s if s == models::ollama::MINIMAX_M25_CLOUD => Ok(ModelId::OllamaMinimaxM25Cloud),
93            s if s == models::ollama::DEVSTRAL_2_123B_CLOUD => {
94                Ok(ModelId::OllamaDevstral2123bCloud)
95            }
96            s if s == models::ollama::NEMOTRON_3_NANO_30B_CLOUD => {
97                Ok(ModelId::OllamaNemotron3Nano30bCloud)
98            }
99            s if s == models::lmstudio::META_LLAMA_3_8B_INSTRUCT => {
100                Ok(ModelId::LmStudioMetaLlama38BInstruct)
101            }
102            s if s == models::lmstudio::META_LLAMA_31_8B_INSTRUCT => {
103                Ok(ModelId::LmStudioMetaLlama318BInstruct)
104            }
105            s if s == models::lmstudio::QWEN25_7B_INSTRUCT => Ok(ModelId::LmStudioQwen257BInstruct),
106            s if s == models::lmstudio::GEMMA_2_2B_IT => Ok(ModelId::LmStudioGemma22BIt),
107            s if s == models::lmstudio::GEMMA_2_9B_IT => Ok(ModelId::LmStudioGemma29BIt),
108            s if s == models::lmstudio::PHI_31_MINI_4K_INSTRUCT => {
109                Ok(ModelId::LmStudioPhi31Mini4kInstruct)
110            }
111            s if s == models::minimax::MINIMAX_M2_5 => Ok(ModelId::MinimaxM25),
112            s if s == models::minimax::MINIMAX_M2 => Ok(ModelId::MinimaxM2),
113            // Hugging Face models
114            s if s == models::huggingface::DEEPSEEK_V32 => Ok(ModelId::HuggingFaceDeepseekV32),
115            s if s == models::huggingface::OPENAI_GPT_OSS_20B => {
116                Ok(ModelId::HuggingFaceOpenAIGptOss20b)
117            }
118            s if s == models::huggingface::OPENAI_GPT_OSS_120B => {
119                Ok(ModelId::HuggingFaceOpenAIGptOss120b)
120            }
121            s if s == models::huggingface::MINIMAX_M2_5_NOVITA => {
122                Ok(ModelId::HuggingFaceMinimaxM25Novita)
123            }
124            s if s == models::huggingface::DEEPSEEK_V32_NOVITA => {
125                Ok(ModelId::HuggingFaceDeepseekV32Novita)
126            }
127            s if s == models::huggingface::XIAOMI_MIMO_V2_FLASH_NOVITA => {
128                Ok(ModelId::HuggingFaceXiaomiMimoV2FlashNovita)
129            }
130            s if s == models::huggingface::ZAI_GLM_5_NOVITA => Ok(ModelId::HuggingFaceGlm5Novita),
131            s if s == models::huggingface::QWEN3_CODER_NEXT_NOVITA => {
132                Ok(ModelId::HuggingFaceQwen3CoderNextNovita)
133            }
134            "minimax/minimax-m2.5" => Ok(ModelId::OpenRouterMinimaxM25),
135            "qwen/qwen3-coder-next" => Ok(ModelId::OpenRouterQwen3CoderNext),
136            _ => {
137                if let Some(model) = Self::parse_openrouter_model(s) {
138                    Ok(model)
139                } else {
140                    Err(ModelParseError::InvalidModel(s.to_string()))
141                }
142            }
143        }
144    }
145}