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