From a4be08d511ca82664027421bf91af33cbb40c989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Tue, 22 Sep 2026 12:17:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20nanobanana=20=E7=A9=BA?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E8=A2=AB=E8=AF=AF=E5=BD=92=E4=B8=80=E4=B8=BA?= =?UTF-8?q?=20GPT=20=E7=94=9F=E6=88=90=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 normalize_nanobanana_model,把空模型字符串统一回落到 gemini-3.1-flash-image-preview - create_nanobanana_generate_content 改用该判据,空模型不再被 normalize_image_model 改写成 gpt-image-2.5-flare-c 后被 provider 校验拒绝 - nanobanana_generate_content_url 复用同一判据,去掉重复的空值分支 - 新增 nanobanana_model_resolution_defaults_blank_values_to_nanobanana 单测 --- .../src/image_provider/protocol/request.rs | 20 +++++++++++++------ .../src/image_provider/runtime/executor.rs | 4 ++-- .../src/image_provider/tests.rs | 14 +++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/server-rs/crates/platform-image/src/image_provider/protocol/request.rs b/server-rs/crates/platform-image/src/image_provider/protocol/request.rs index 12c5af908..f93f23705 100644 --- a/server-rs/crates/platform-image/src/image_provider/protocol/request.rs +++ b/server-rs/crates/platform-image/src/image_provider/protocol/request.rs @@ -99,6 +99,19 @@ pub fn normalize_image_model(model: &str) -> &str { } } +/// nanobanana endpoint 只服务 Gemini 模型:空模型字符串回落到 nanobanana, +/// 而不是 `normalize_image_model` 默认的 GPT 生成模型。 +/// +/// 中文注释:`nanobanana_generate_content_url` 与 `create_nanobanana_generate_content` +/// 必须共用这一判据,否则空模型会先被归一成 GPT 生成模型、再被 provider 校验拒绝, +/// 或用一个 GPT 模型名去拼 Gemini 的 generateContent URL。 +pub(crate) fn normalize_nanobanana_model(model: &str) -> &str { + match model.trim() { + "" => NANOBANANA_2_MODEL, + value => value, + } +} + /// Resolves a concrete provider model to its owning provider without rewriting the /// model string. /// @@ -288,12 +301,7 @@ pub fn nanobanana_generate_content_url(settings: &ImageProviderSettings, model: .base_url .trim_end_matches("/v1") .trim_end_matches('/'); - // 中文注释:nanobanana endpoint 只服务 Gemini 模型,空模型字符串应回落到 - // nanobanana 模型,而不是 normalize_image_model 默认的 GPT 生成模型。 - let model = match model.trim() { - "" => NANOBANANA_2_MODEL, - value => value, - }; + let model = normalize_nanobanana_model(model); format!("{}/v1beta/models/{}:generateContent", base_url, model) } diff --git a/server-rs/crates/platform-image/src/image_provider/runtime/executor.rs b/server-rs/crates/platform-image/src/image_provider/runtime/executor.rs index 5b4926924..8cec1ae9d 100644 --- a/server-rs/crates/platform-image/src/image_provider/runtime/executor.rs +++ b/server-rs/crates/platform-image/src/image_provider/runtime/executor.rs @@ -24,7 +24,7 @@ use crate::image_provider::{ build_image_edit_request_log_params, build_image_request_body_with_model, build_nanobanana_generate_content_request_body, images_edit_url, images_generation_url, nanobanana_generate_content_url, normalize_image_model, normalize_image_size_for_model, - resolve_image_provider, + normalize_nanobanana_model, resolve_image_provider, }, response::handle_image_response, types::{GeneratedImages, ImageProvider, ImageProviderSettings, ReferenceImage}, @@ -262,7 +262,7 @@ pub async fn create_nanobanana_generate_content( reference_images: &[ReferenceImage], failure_context: &str, ) -> Result { - let model = normalize_image_model(model); + let model = normalize_nanobanana_model(model); ensure_provider_matches_model(settings, model, failure_context)?; if reference_images.len() > VECTOR_ENGINE_NANOBANANA_MAX_REFERENCE_IMAGES { return Err(PlatformImageError::InvalidRequest { diff --git a/server-rs/crates/platform-image/src/image_provider/tests.rs b/server-rs/crates/platform-image/src/image_provider/tests.rs index 4e745ebe6..5d96e0a8e 100644 --- a/server-rs/crates/platform-image/src/image_provider/tests.rs +++ b/server-rs/crates/platform-image/src/image_provider/tests.rs @@ -5,6 +5,7 @@ use serde_json::json; use super::image_source::{decode_generated_image_base64, parse_reference_image_data_url}; use super::payload::{extract_b64_images, extract_image_urls}; +use super::request::normalize_nanobanana_model; #[test] fn request_body_normalizes_size_prompt_and_candidate_count() { @@ -58,6 +59,19 @@ fn provider_urls_normalize_root_and_v1_base_urls() { ); } +#[test] +fn nanobanana_model_resolution_defaults_blank_values_to_nanobanana() { + assert_eq!(normalize_nanobanana_model(" "), NANOBANANA_2_MODEL); + assert_eq!( + normalize_nanobanana_model(" gemini-3.1-flash-image-preview "), + NANOBANANA_2_MODEL + ); + assert_eq!( + normalize_nanobanana_model(GPT_IMAGE_2_5_EDIT_MODEL), + GPT_IMAGE_2_5_EDIT_MODEL + ); +} + #[test] fn data_url_and_base64_image_decoding_preserves_image_metadata() { let data_url = format!(