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