vtcode_llm/providers/gemini/helpers/capabilities.rs
1//! Gemini model capability predicates and token limits.
2
3use super::*;
4
5impl GeminiProvider {
6 pub(crate) fn is_gemini_3_pro_model(model: &str) -> bool {
7 let rest = match model.strip_prefix("gemini-") {
8 Some(r) => r,
9 None => return false,
10 };
11 let version_part = rest.split('-').next().unwrap_or("");
12 let Some(major_str) = version_part.split('.').next() else {
13 return false;
14 };
15 major_str == "3" && model.contains("pro") && !model.contains("flash")
16 }
17
18 /// Check if model supports context caching
19 pub fn supports_caching(model: &str) -> bool {
20 models::google::CACHING_MODELS.contains(&model)
21 }
22
23 /// Check if model supports code execution
24 pub fn supports_code_execution(model: &str) -> bool {
25 models::google::CODE_EXECUTION_MODELS.contains(&model)
26 }
27
28 /// Get maximum input token limit for a model
29 pub fn max_input_tokens(model: &str) -> usize {
30 if model.contains("gemini-3.1") {
31 1_048_576 // 1M tokens for Gemini 3.1 models
32 } else if model.contains("3") || model.contains("1.5-pro") {
33 2_097_152 // 2M tokens for Gemini 1.5 Pro and 3.x models
34 } else {
35 1_048_576 // 1M tokens for other current models
36 }
37 }
38
39 /// Get maximum output token limit for a model
40 pub fn max_output_tokens(model: &str) -> usize {
41 if model.contains("3") {
42 65_536 // 65K tokens for Gemini 3 models
43 } else {
44 8_192 // Conservative default
45 }
46 }
47
48 /// Check if model supports extended thinking levels (minimal, medium)
49 /// Only Gemini 3 Flash Preview supports the `minimal` level; stable
50 /// Gemini 3.7/3.8 Flash support low/medium/high with medium as default.
51 pub(crate) fn supports_extended_thinking(model: &str) -> bool {
52 model.contains("gemini-3-flash-preview")
53 }
54
55 /// Determine whether a Gemini model uses the latest API behavior that
56 /// deprecates sampling parameters (`temperature`, `top_p`, `top_k`) and
57 /// disallows prefilled model turns (last turn with role `"model"`).
58 ///
59 /// Affected: all `gemini-3.5+` models and all future model releases.
60 pub(crate) fn uses_latest_gemini_api(model: &str) -> bool {
61 // Extract version from model name like "gemini-3.7-flash" -> "3.5"
62 let rest = match model.strip_prefix("gemini-") {
63 Some(r) => r,
64 None => return false,
65 };
66 let version_part = rest.split('-').next().unwrap_or("");
67 let mut parts = version_part.splitn(2, '.');
68 let major: u32 = match parts.next().and_then(|s| s.parse().ok()) {
69 Some(m) => m,
70 None => return false,
71 };
72 if major > 3 {
73 return true;
74 }
75 if major < 3 {
76 return false;
77 }
78 // major == 3: check minor >= 5 (3.5, 3.6, and all future 3.x)
79 match parts.next().and_then(|s| s.parse::<u32>().ok()) {
80 Some(minor) => minor >= 5,
81 None => false,
82 }
83 }
84
85 /// Get supported thinking levels for a model
86 /// Reference: <https://ai.google.dev/gemini-api/docs/gemini-3>
87 /// Gemini 3.8 Flash: low, medium (default), high — `minimal` is not supported and will error.
88 pub(crate) fn supported_thinking_levels(model: &str) -> Vec<&'static str> {
89 if model.contains("gemini-3-flash-preview") {
90 // Preview supports all levels including minimal
91 vec!["minimal", "low", "medium", "high"]
92 } else if model.contains("gemini-3") && model.contains("flash") {
93 // Stable Flash models (3.6, 3.7, 3.8) support low/medium/high (medium default)
94 vec!["low", "medium", "high"]
95 } else if model.contains("gemini-3") {
96 // Gemini 3 Pro supports low and high
97 vec!["low", "high"]
98 } else {
99 // Unknown model, conservative default
100 vec!["low", "high"]
101 }
102 }
103}