google_ai: Add Gemini 3.7 Flash (#62670)

Follow-up to #62010, which added Gemini 3.6 Flash.

Google released Gemini 3.7 Flash on August 13, 2026. It keeps the 1
million token context window and the 64k output limit, and supports
thinking.

Unlike 3.5 and 3.6 Flash, it does not accept `thinking_level: MINIMAL`
(the API returns a validation error). So it exposes Low, Medium and
High, defaulting to Medium, and `disabled_thinking_level` in
`completion.rs` returns Low for it instead of Minimal when the user
turns thinking off.

The provider builds its model list from `google_ai::Model::iter()`, so
adding the enum variant is enough for it to show up in the model
dropdown.

Release Notes:

- Added Gemini 3.7 Flash to the Google AI models
This commit is contained in:
Arnesh 2026-08-17 08:28:00 +00:00 committed by GitHub
parent dbf7f63875
commit 30f73707a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 2 deletions

View file

@ -249,7 +249,12 @@ fn is_google_thinking_model(model_id: &str) -> bool {
fn disabled_thinking_level(model_id: &str) -> Option<ThinkingLevel> {
match model_id {
model_id if model_id.starts_with("gemini-3") && model_id.contains("-pro") => {
// `gemini-3.7-flash` rejects `MINIMAL` with a validation error, so `LOW` is
// the lowest level available to it.
model_id
if model_id.starts_with("gemini-3.7-flash")
|| (model_id.starts_with("gemini-3") && model_id.contains("-pro")) =>
{
Some(ThinkingLevel::Low)
}
model_id if model_id.starts_with("gemini-3") => Some(ThinkingLevel::Minimal),
@ -704,6 +709,23 @@ mod tests {
assert_eq!(mapper.stop_reason, StopReason::Refusal);
}
#[test]
fn test_disabled_thinking_level_per_model() {
assert_eq!(
disabled_thinking_level("gemini-3.7-flash"),
Some(ThinkingLevel::Low)
);
assert_eq!(
disabled_thinking_level("gemini-3.1-pro-preview"),
Some(ThinkingLevel::Low)
);
assert_eq!(
disabled_thinking_level("gemini-3.6-flash"),
Some(ThinkingLevel::Minimal)
);
assert_eq!(disabled_thinking_level("gemini-2.5-flash"), None);
}
#[test]
fn test_function_call_with_signature_creates_tool_use_with_signature() {
let mut mapper = GoogleEventMapper::new();

View file

@ -533,6 +533,8 @@ pub enum Model {
Gemini35Flash,
#[serde(rename = "gemini-3.6-flash")]
Gemini36Flash,
#[serde(rename = "gemini-3.7-flash")]
Gemini37Flash,
#[serde(rename = "gemini-3.1-pro-preview", alias = "gemini-3-pro-preview")]
Gemini31Pro,
#[serde(rename = "custom")]
@ -560,6 +562,7 @@ impl Model {
Self::Gemini3Flash => "gemini-3-flash-preview",
Self::Gemini35Flash => "gemini-3.5-flash",
Self::Gemini36Flash => "gemini-3.6-flash",
Self::Gemini37Flash => "gemini-3.7-flash",
Self::Gemini31Pro => "gemini-3.1-pro-preview",
Self::Custom { name, .. } => name,
}
@ -573,6 +576,7 @@ impl Model {
Self::Gemini3Flash => "gemini-3-flash-preview",
Self::Gemini35Flash => "gemini-3.5-flash",
Self::Gemini36Flash => "gemini-3.6-flash",
Self::Gemini37Flash => "gemini-3.7-flash",
Self::Gemini31Pro => "gemini-3.1-pro-preview",
Self::Custom { name, .. } => name,
}
@ -587,6 +591,7 @@ impl Model {
Self::Gemini3Flash => "Gemini 3 Flash",
Self::Gemini35Flash => "Gemini 3.5 Flash",
Self::Gemini36Flash => "Gemini 3.6 Flash",
Self::Gemini37Flash => "Gemini 3.7 Flash",
Self::Gemini31Pro => "Gemini 3.1 Pro",
Self::Custom {
name, display_name, ..
@ -603,6 +608,7 @@ impl Model {
| Self::Gemini3Flash
| Self::Gemini35Flash
| Self::Gemini36Flash
| Self::Gemini37Flash
| Self::Gemini31Pro => 1_048_576,
Self::Custom { max_tokens, .. } => *max_tokens,
}
@ -617,6 +623,7 @@ impl Model {
| Model::Gemini3Flash
| Model::Gemini35Flash
| Model::Gemini36Flash
| Model::Gemini37Flash
| Model::Gemini31Pro => Some(65_536),
Model::Custom { .. } => None,
}
@ -640,6 +647,7 @@ impl Model {
| Self::Gemini3Flash
| Self::Gemini35Flash
| Self::Gemini36Flash
| Self::Gemini37Flash
| Self::Gemini31Pro
| Self::Custom {
mode: GoogleModelMode::Thinking { .. },
@ -659,7 +667,7 @@ impl Model {
ThinkingLevel::Medium,
ThinkingLevel::High,
],
Self::Gemini31Pro => &[
Self::Gemini37Flash | Self::Gemini31Pro => &[
ThinkingLevel::Low,
ThinkingLevel::Medium,
ThinkingLevel::High,
@ -674,6 +682,7 @@ impl Model {
Self::Gemini3Flash => Some(ThinkingLevel::High),
Self::Gemini35Flash => Some(ThinkingLevel::Medium),
Self::Gemini36Flash => Some(ThinkingLevel::Medium),
Self::Gemini37Flash => Some(ThinkingLevel::Medium),
Self::Gemini31Pro => Some(ThinkingLevel::High),
_ => None,
}
@ -692,6 +701,7 @@ impl Model {
| Self::Gemini3Flash
| Self::Gemini35Flash
| Self::Gemini36Flash
| Self::Gemini37Flash
| Self::Gemini31Pro => GoogleModelMode::Thinking {
budget_tokens: None,
},
@ -724,6 +734,27 @@ mod tests {
assert_eq!(deserialized, Model::Gemini36Flash);
}
#[test]
fn test_gemini_3_7_flash_model_metadata() {
let model = Model::Gemini37Flash;
assert_eq!(model.id(), "gemini-3.7-flash");
assert_eq!(model.request_id(), "gemini-3.7-flash");
assert_eq!(model.display_name(), "Gemini 3.7 Flash");
assert_eq!(
model.supported_thinking_levels(),
&[
ThinkingLevel::Low,
ThinkingLevel::Medium,
ThinkingLevel::High
]
);
let serialized = serde_json::to_value(&model).unwrap();
assert_eq!(serialized, json!("gemini-3.7-flash"));
let deserialized: Model = serde_json::from_value(json!("gemini-3.7-flash")).unwrap();
assert_eq!(deserialized, Model::Gemini37Flash);
}
#[test]
fn test_function_call_part_with_signature_serializes_correctly() {
let part = FunctionCallPart {