From 078d171ebc08d29bb97c5dc377cb7fec03710239 Mon Sep 17 00:00:00 2001 From: Ayush <63203492+macayu17@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:24:18 +0000 Subject: [PATCH] google_ai: Add Gemini 3.8 Flash (#63643) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective Add Gemini 3.8 Flash to Zed's built-in Google AI provider so it can be selected from the model dropdown. ## Solution Register `gemini-3.8-flash` with its 1 million token context window, 64k output limit, and Low, Medium, and High thinking levels. Medium is the default, and Low is used when thinking is disabled because the model rejects Minimal. Add tests covering the model metadata, serialization, token limits, thinking levels, and disabled-thinking behavior. ## Testing Tested on Windows using Rust 1.98 GNU: - `cargo test -p google_ai` — 23 passed - `cargo fmt --all --check` - `cargo +stable-x86_64-pc-windows-gnu clippy -p google_ai --release --all-targets --all-features -- --deny warnings` Reviewers can run `cargo test -p google_ai`. The pinned MSVC toolchain was not tested locally because `link.exe` was unavailable. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Added Gemini 3.8 Flash to the Google AI models --- crates/google_ai/src/completion.rs | 9 ++++++-- crates/google_ai/src/google_ai.rs | 37 +++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/crates/google_ai/src/completion.rs b/crates/google_ai/src/completion.rs index bc28b5b9ed..60ee81751b 100644 --- a/crates/google_ai/src/completion.rs +++ b/crates/google_ai/src/completion.rs @@ -250,10 +250,11 @@ fn is_google_thinking_model(model_id: &str) -> bool { fn disabled_thinking_level(model_id: &str) -> Option { match model_id { - // `gemini-3.7-flash` rejects `MINIMAL` with a validation error, so `LOW` is - // the lowest level available to it. + // Gemini 3.7 and 3.8 Flash reject `MINIMAL` with a validation error, so + // `LOW` is the lowest level available to them. model_id if model_id.starts_with("gemini-3.7-flash") + || model_id.starts_with("gemini-3.8-flash") || (model_id.starts_with("gemini-3") && model_id.contains("-pro")) => { Some(ThinkingLevel::Low) @@ -749,6 +750,10 @@ mod tests { #[test] fn test_disabled_thinking_level_per_model() { + assert_eq!( + disabled_thinking_level("gemini-3.8-flash"), + Some(ThinkingLevel::Low) + ); assert_eq!( disabled_thinking_level("gemini-3.7-flash"), Some(ThinkingLevel::Low) diff --git a/crates/google_ai/src/google_ai.rs b/crates/google_ai/src/google_ai.rs index 5734bb2c15..00a195b43d 100644 --- a/crates/google_ai/src/google_ai.rs +++ b/crates/google_ai/src/google_ai.rs @@ -517,6 +517,8 @@ pub enum Model { Gemini36Flash, #[serde(rename = "gemini-3.7-flash")] Gemini37Flash, + #[serde(rename = "gemini-3.8-flash")] + Gemini38Flash, #[serde(rename = "gemini-3.1-pro-preview")] Gemini31Pro, #[serde(rename = "custom")] @@ -542,6 +544,7 @@ impl Model { Self::Gemini35Flash => "gemini-3.5-flash", Self::Gemini36Flash => "gemini-3.6-flash", Self::Gemini37Flash => "gemini-3.7-flash", + Self::Gemini38Flash => "gemini-3.8-flash", Self::Gemini31Pro => "gemini-3.1-pro-preview", Self::Custom { name, .. } => name, } @@ -553,6 +556,7 @@ impl Model { Self::Gemini35Flash => "gemini-3.5-flash", Self::Gemini36Flash => "gemini-3.6-flash", Self::Gemini37Flash => "gemini-3.7-flash", + Self::Gemini38Flash => "gemini-3.8-flash", Self::Gemini31Pro => "gemini-3.1-pro-preview", Self::Custom { name, .. } => name, } @@ -565,6 +569,7 @@ impl Model { Self::Gemini35Flash => "Gemini 3.5 Flash", Self::Gemini36Flash => "Gemini 3.6 Flash", Self::Gemini37Flash => "Gemini 3.7 Flash", + Self::Gemini38Flash => "Gemini 3.8 Flash", Self::Gemini31Pro => "Gemini 3.1 Pro", Self::Custom { name, display_name, .. @@ -579,6 +584,7 @@ impl Model { | Self::Gemini35Flash | Self::Gemini36Flash | Self::Gemini37Flash + | Self::Gemini38Flash | Self::Gemini31Pro => 1_048_576, Self::Custom { max_tokens, .. } => *max_tokens, } @@ -591,6 +597,7 @@ impl Model { | Model::Gemini35Flash | Model::Gemini36Flash | Model::Gemini37Flash + | Model::Gemini38Flash | Model::Gemini31Pro => Some(65_536), Model::Custom { .. } => None, } @@ -612,6 +619,7 @@ impl Model { | Self::Gemini35Flash | Self::Gemini36Flash | Self::Gemini37Flash + | Self::Gemini38Flash | Self::Gemini31Pro | Self::Custom { mode: GoogleModelMode::Thinking { .. }, @@ -631,7 +639,7 @@ impl Model { ThinkingLevel::Medium, ThinkingLevel::High, ], - Self::Gemini37Flash | Self::Gemini31Pro => &[ + Self::Gemini37Flash | Self::Gemini38Flash | Self::Gemini31Pro => &[ ThinkingLevel::Low, ThinkingLevel::Medium, ThinkingLevel::High, @@ -647,6 +655,7 @@ impl Model { Self::Gemini35Flash => Some(ThinkingLevel::Medium), Self::Gemini36Flash => Some(ThinkingLevel::Medium), Self::Gemini37Flash => Some(ThinkingLevel::Medium), + Self::Gemini38Flash => Some(ThinkingLevel::Medium), Self::Gemini31Pro => Some(ThinkingLevel::High), _ => None, } @@ -659,6 +668,7 @@ impl Model { | Self::Gemini35Flash | Self::Gemini36Flash | Self::Gemini37Flash + | Self::Gemini38Flash | Self::Gemini31Pro => GoogleModelMode::Thinking { budget_tokens: None, }, @@ -735,6 +745,31 @@ mod tests { assert_eq!(deserialized, Model::Gemini37Flash); } + #[test] + fn test_gemini_3_8_flash_model_metadata() { + let model = Model::Gemini38Flash; + assert_eq!(model.id(), "gemini-3.8-flash"); + assert_eq!(model.request_id(), "gemini-3.8-flash"); + assert_eq!(model.display_name(), "Gemini 3.8 Flash"); + assert_eq!(model.max_token_count(), 1_048_576); + assert_eq!(model.max_output_tokens(), Some(65_536)); + assert!(model.supports_thinking()); + assert_eq!( + model.supported_thinking_levels(), + &[ + ThinkingLevel::Low, + ThinkingLevel::Medium, + ThinkingLevel::High + ] + ); + assert_eq!(model.default_thinking_level(), Some(ThinkingLevel::Medium)); + + let serialized = serde_json::to_value(&model).unwrap(); + assert_eq!(serialized, json!("gemini-3.8-flash")); + let deserialized: Model = serde_json::from_value(json!("gemini-3.8-flash")).unwrap(); + assert_eq!(deserialized, Model::Gemini38Flash); + } + #[test] fn test_function_call_part_with_signature_serializes_correctly() { let part = FunctionCallPart {