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 || s == models::zai::GLM_5_LEGACY {
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_1_PRO_PREVIEW => Ok(ModelId::Gemini31ProPreview),
25            s if s == models::GEMINI_3_1_PRO_PREVIEW_CUSTOMTOOLS => {
26                Ok(ModelId::Gemini31ProPreviewCustomTools)
27            }
28            s if s == models::GEMINI_3_1_FLASH_LITE_PREVIEW => {
29                Ok(ModelId::Gemini31FlashLitePreview)
30            }
31            s if s == models::GEMINI_3_FLASH_PREVIEW => Ok(ModelId::Gemini3FlashPreview),
32            // OpenAI models
33            s if s == models::GPT_5 => Ok(ModelId::GPT5),
34            s if s == models::GPT_5_2 => Ok(ModelId::GPT52),
35            s if s == models::GPT_5_MINI => Ok(ModelId::GPT5Mini),
36            s if s == models::GPT_5_NANO => Ok(ModelId::GPT5Nano),
37            s if s == models::openai::GPT_5_3_CODEX => Ok(ModelId::GPT53Codex),
38            s if s == models::openai::GPT_OSS_20B => Ok(ModelId::OpenAIGptOss20b),
39            s if s == models::openai::GPT_OSS_120B => Ok(ModelId::OpenAIGptOss120b),
40            // Anthropic models
41            s if s == models::CLAUDE_OPUS_4_6 => Ok(ModelId::ClaudeOpus46),
42            s if s == models::CLAUDE_SONNET_4_6 => Ok(ModelId::ClaudeSonnet46),
43            s if s == models::CLAUDE_HAIKU_4_5 => Ok(ModelId::ClaudeHaiku45),
44            s if s == models::CLAUDE_HAIKU_4_5_20251001 => Ok(ModelId::ClaudeHaiku45),
45            // DeepSeek models
46            s if s == models::DEEPSEEK_CHAT => Ok(ModelId::DeepSeekChat),
47            s if s == models::DEEPSEEK_REASONER => Ok(ModelId::DeepSeekReasoner),
48            // Z.AI models
49            s if s == models::zai::GLM_5 || s == models::zai::GLM_5_LEGACY => Ok(ModelId::ZaiGlm5),
50            // Moonshot models
51            s if s == models::moonshot::KIMI_K2_5 => Ok(ModelId::MoonshotKimiK25),
52            s if s == models::ollama::GPT_OSS_20B => Ok(ModelId::OllamaGptOss20b),
53            s if s == models::ollama::GPT_OSS_20B_CLOUD => Ok(ModelId::OllamaGptOss20bCloud),
54            s if s == models::ollama::GPT_OSS_120B_CLOUD => Ok(ModelId::OllamaGptOss120bCloud),
55            s if s == models::ollama::QWEN3_1_7B => Ok(ModelId::OllamaQwen317b),
56            s if s == models::ollama::QWEN3_CODER_NEXT => Ok(ModelId::OllamaQwen3CoderNext),
57            "qwen3-coder-next" => Ok(ModelId::OllamaQwen3CoderNext),
58            s if s == models::ollama::DEEPSEEK_V32_CLOUD => Ok(ModelId::OllamaDeepseekV32Cloud),
59            s if s == models::ollama::QWEN3_NEXT_80B_CLOUD => Ok(ModelId::OllamaQwen3Next80bCloud),
60            s if s == models::ollama::GLM_5_CLOUD => Ok(ModelId::OllamaGlm5Cloud),
61            s if s == models::ollama::GEMINI_3_FLASH_PREVIEW_CLOUD => {
62                Ok(ModelId::OllamaGemini3FlashPreviewCloud)
63            }
64            s if s == models::ollama::MINIMAX_M2_CLOUD => Ok(ModelId::OllamaMinimaxM2Cloud),
65            s if s == models::ollama::MINIMAX_M25_CLOUD => Ok(ModelId::OllamaMinimaxM25Cloud),
66            s if s == models::minimax::MINIMAX_M2_5 => Ok(ModelId::MinimaxM25),
67            // Hugging Face models
68            s if s == models::huggingface::DEEPSEEK_V32 => Ok(ModelId::HuggingFaceDeepseekV32),
69            s if s == models::huggingface::OPENAI_GPT_OSS_20B => {
70                Ok(ModelId::HuggingFaceOpenAIGptOss20b)
71            }
72            s if s == models::huggingface::OPENAI_GPT_OSS_120B => {
73                Ok(ModelId::HuggingFaceOpenAIGptOss120b)
74            }
75            s if s == models::huggingface::MINIMAX_M2_5_NOVITA => {
76                Ok(ModelId::HuggingFaceMinimaxM25Novita)
77            }
78            s if s == models::huggingface::DEEPSEEK_V32_NOVITA => {
79                Ok(ModelId::HuggingFaceDeepseekV32Novita)
80            }
81            s if s == models::huggingface::XIAOMI_MIMO_V2_FLASH_NOVITA => {
82                Ok(ModelId::HuggingFaceXiaomiMimoV2FlashNovita)
83            }
84            s if s == models::huggingface::ZAI_GLM_5_NOVITA => Ok(ModelId::HuggingFaceGlm5Novita),
85            s if s == models::huggingface::QWEN3_CODER_NEXT_NOVITA => {
86                Ok(ModelId::HuggingFaceQwen3CoderNextNovita)
87            }
88            s if s == models::huggingface::QWEN3_5_397B_A17B_TOGETHER => {
89                Ok(ModelId::HuggingFaceQwen35397BA17BTogether)
90            }
91            s if s == models::huggingface::STEP_3_5_FLASH
92                || s == models::huggingface::STEP_3_5_FLASH_BASE
93                || s == models::huggingface::STEP_3_5_FLASH_LEGACY_FASTEST =>
94            {
95                Ok(ModelId::HuggingFaceStep35Flash)
96            }
97            "minimax/minimax-m2.5" => Ok(ModelId::OpenRouterMinimaxM25),
98            "qwen/qwen3-coder-next" => Ok(ModelId::OpenRouterQwen3CoderNext),
99            _ => {
100                if let Some(model) = Self::parse_openrouter_model(s) {
101                    Ok(model)
102                } else {
103                    Err(ModelParseError::InvalidModel(s.to_string()))
104                }
105            }
106        }
107    }
108}