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 d1d07867a..e5132386d 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,14 +99,19 @@ pub fn normalize_image_model(model: &str) -> &str { } } -/// Resolves a persisted/concrete model to its owning provider without rewriting -/// the model string. Legacy GPT Image 2 values remain readable, but are routed -/// to the current Tiantoken task boundary instead of VectorEngine. +/// Resolves a concrete provider model to its owning provider without rewriting the +/// model string. +/// +/// 中文注释:白名单是 provider 边界,不放业务名和已退役模型—— +/// `gpt-image-2.5-flare-c` / `gpt-image-2.5-sunburst-c` 走 Tiantoken, +/// nanobanana 走 VectorEngine。业务模型名 `gpt-image-2.5` +/// (`GPT_IMAGE_2_5_BUSINESS_NAME`) 必须在任务边界解析成具体 key;`gpt-image-2-c` +/// 只是历史 fallback / 审计字符串,永远不会作为 provider 或业务 model 传入, +/// 命中即拒绝。`gpt-image-2` 仍按历史可读值接受。 pub fn resolve_image_provider(model: &str) -> Result { match normalize_image_model(model) { GPT_IMAGE_2_MODEL | GPT_IMAGE_2_C_MODEL - | GPT_IMAGE_2_5_BUSINESS_NAME | GPT_IMAGE_2_5_GENERATION_MODEL | GPT_IMAGE_2_5_EDIT_MODEL => Ok(ImageProvider::Tiantoken), NANOBANANA_2_MODEL => Ok(ImageProvider::VectorEngine), diff --git a/server-rs/crates/platform-image/tests/image_provider.rs b/server-rs/crates/platform-image/tests/image_provider.rs index 893fab966..f4d84c3f4 100644 --- a/server-rs/crates/platform-image/tests/image_provider.rs +++ b/server-rs/crates/platform-image/tests/image_provider.rs @@ -1,10 +1,11 @@ use platform_image::image_provider::{ - GPT_IMAGE_2_5_EDIT_MODEL, GPT_IMAGE_2_5_GENERATION_MODEL, GPT_IMAGE_2_C_MODEL, - GPT_IMAGE_2_MODEL, ImageProvider, ImageProviderSettings, PlatformImageError, ReferenceImage, - VECTOR_ENGINE_PROVIDER, build_image_http_client, build_image_request_body, - build_image_request_body_with_model, build_nanobanana_generate_content_request_body, - create_image_edit, create_image_generation, create_nanobanana_generate_content, - images_edit_url, images_generation_url, nanobanana_generate_content_url, + GPT_IMAGE_2_5_BUSINESS_NAME, GPT_IMAGE_2_5_EDIT_MODEL, GPT_IMAGE_2_5_GENERATION_MODEL, + GPT_IMAGE_2_C_MODEL, GPT_IMAGE_2_MODEL, ImageProvider, ImageProviderSettings, + PlatformImageError, ReferenceImage, VECTOR_ENGINE_PROVIDER, build_image_http_client, + build_image_request_body, build_image_request_body_with_model, + build_nanobanana_generate_content_request_body, create_image_edit, create_image_generation, + create_nanobanana_generate_content, images_edit_url, images_generation_url, + nanobanana_generate_content_url, resolve_image_provider, }; use std::{ sync::{ @@ -227,6 +228,26 @@ fn nanobanana_generate_content_url_blank_model_falls_back_to_nanobanana_model() ); } +#[test] +fn resolve_image_provider_rejects_business_model_and_accepts_concrete_models() { + // 中文注释:业务模型名不是 provider model,必须在 api-server 任务边界先解析成具体 model。 + assert!(resolve_image_provider(GPT_IMAGE_2_5_BUSINESS_NAME).is_err()); + + assert_eq!( + resolve_image_provider(GPT_IMAGE_2_5_GENERATION_MODEL), + Ok(ImageProvider::Tiantoken) + ); + assert_eq!( + resolve_image_provider(GPT_IMAGE_2_5_EDIT_MODEL), + Ok(ImageProvider::Tiantoken) + ); + assert_eq!( + resolve_image_provider("gemini-3.1-flash-image-preview"), + Ok(ImageProvider::VectorEngine) + ); + assert!(resolve_image_provider("unknown-model").is_err()); +} + #[tokio::test] async fn image_edit_retries_send_timeout_once_and_succeeds() { let listener = TcpListener::bind("127.0.0.1:0")