修正 nanobanana 空模型被误归一为 GPT 生成模型

- 新增 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 单测
This commit is contained in:
2026-09-22 12:17:00 +08:00
parent e901937bb4
commit a4be08d511
3 changed files with 30 additions and 8 deletions
@@ -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)
}
@@ -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<GeneratedImages, PlatformImageError> {
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 {
@@ -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!(