vtcode_config/models/
provider.rs1pub use vtcode_commons::provider::Provider;
8
9use super::{ModelId, model_catalog_entry};
10use crate::core::OpenAIServiceTier;
11use std::str::FromStr;
12
13const GENERIC_REASONING_EFFORTS: &[&str] = &["low", "medium", "high"];
14
15pub trait ProviderModelSupport: AsRef<str> {
20 fn supports_reasoning(&self, model: &str) -> bool {
26 model_catalog_entry(self.as_ref(), model)
27 .map(|entry| entry.reasoning)
28 .unwrap_or_else(|| self.supports_reasoning_effort(model))
29 }
30
31 fn supports_reasoning_effort(&self, model: &str) -> bool;
33
34 fn supported_reasoning_efforts(&self, model: &str) -> &'static [&'static str] {
36 if let Some(entry) = model_catalog_entry(self.as_ref(), model) {
37 return entry.reasoning_efforts;
38 }
39 if self.supports_reasoning_effort(model) {
40 GENERIC_REASONING_EFFORTS
41 } else {
42 &[]
43 }
44 }
45
46 fn supports_service_tier(&self, model: &str) -> bool;
48
49 fn supports_service_tier_value(&self, model: &str, tier: OpenAIServiceTier) -> bool {
54 match tier {
55 OpenAIServiceTier::Ultrafast => {
56 const ULTRAFAST_MODELS: &[&str] = &[
57 crate::constants::models::openai::GPT_6_ASTRA,
58 crate::constants::models::openai::GPT_6_1_SOL,
59 ];
60 self.as_ref() == Provider::OpenAI.as_ref() && ULTRAFAST_MODELS.contains(&model)
61 }
62 _ => self.supports_service_tier(model),
63 }
64 }
65}
66
67impl ProviderModelSupport for Provider {
68 fn supports_reasoning_effort(&self, model: &str) -> bool {
69 if let Some(entry) = model_catalog_entry(self.as_ref(), model) {
70 return !entry.reasoning_efforts.is_empty();
75 }
76
77 use crate::constants::models;
78
79 match self {
80 Provider::Gemini => models::google::REASONING_MODELS.contains(&model),
81 Provider::OpenAI => models::openai::REASONING_MODELS.contains(&model),
82 Provider::Anthropic => models::anthropic::REASONING_MODELS.contains(&model),
83 Provider::Copilot => false,
84 Provider::DeepSeek => model == models::deepseek::DEEPSEEK_FLASH || model == "deepseek-reasoner",
85 Provider::Meta => models::meta::REASONING_MODELS.contains(&model),
86 Provider::OpenRouter => {
87 if let Ok(model_id) = ModelId::from_str(model) {
88 if let Some(meta) = crate::models::openrouter_generated::metadata_for(model_id) {
89 return meta.reasoning;
90 }
91 return false;
92 }
93 models::openrouter::REASONING_MODELS.contains(&model)
94 }
95 Provider::Ollama => models::ollama::REASONING_LEVEL_MODELS.contains(&model),
96 Provider::OllamaCloud => models::ollama::REASONING_LEVEL_MODELS.contains(&model),
97 Provider::LmStudio => models::lmstudio::REASONING_MODELS.contains(&model),
98 Provider::LlamaCpp => models::llamacpp::REASONING_MODELS.contains(&model),
99 Provider::Moonshot => models::moonshot::REASONING_MODELS.contains(&model),
100 Provider::ZAI => models::zai::REASONING_MODELS.contains(&model),
101 Provider::Minimax => models::minimax::SUPPORTED_MODELS.contains(&model),
102 Provider::MiMo => models::mimo::SUPPORTED_MODELS.contains(&model),
103 Provider::Mistral => models::mistral::SUPPORTED_MODELS.contains(&model),
104 Provider::HuggingFace => models::huggingface::REASONING_MODELS.contains(&model),
105 Provider::OpenCodeZen => {
106 if models::opencode_zen::OPENAI_MODELS.contains(&model) {
107 Provider::OpenAI.supports_reasoning_effort(model)
108 } else if models::opencode_zen::ANTHROPIC_MODELS.contains(&model) {
109 Provider::Anthropic.supports_reasoning_effort(model)
110 } else {
111 false
112 }
113 }
114 Provider::OpenCodeGo => false,
115 Provider::Qwen => models::qwen::REASONING_MODELS.contains(&model),
116 Provider::StepFun => models::stepfun::REASONING_MODELS.contains(&model),
117 Provider::Evolink => models::evolink::REASONING_MODELS.contains(&model),
118 Provider::Poolside => false,
119 Provider::Vercel => !models::vercel::NON_REASONING_MODELS.contains(&model),
120 Provider::XAI => models::xai::REASONING_MODELS.contains(&model),
121 Provider::NVIDIA => models::nvidia::REASONING_MODELS.contains(&model),
122 Provider::MergeGateway => models::merge_gateway::route_supports_reasoning(model),
123 }
124 }
125
126 fn supports_service_tier(&self, model: &str) -> bool {
127 use crate::constants::models;
128
129 match self {
130 Provider::OpenAI => models::openai::SERVICE_TIER_MODELS.contains(&model),
131 Provider::MergeGateway => model.trim().starts_with("openai/"),
132 Provider::DeepSeek
135 | Provider::Meta
136 | Provider::OpenRouter
137 | Provider::Ollama
138 | Provider::OllamaCloud
139 | Provider::LmStudio
140 | Provider::LlamaCpp
141 | Provider::Moonshot
142 | Provider::ZAI
143 | Provider::Minimax
144 | Provider::MiMo
145 | Provider::Mistral
146 | Provider::HuggingFace
147 | Provider::OpenCodeZen
148 | Provider::OpenCodeGo
149 | Provider::Qwen
150 | Provider::StepFun
151 | Provider::Evolink
152 | Provider::Poolside
153 | Provider::XAI
154 | Provider::NVIDIA
155 | Provider::Vercel => true,
156 Provider::Gemini | Provider::Anthropic | Provider::Copilot => false,
157 }
158 }
159}