From d3407e8910220c9085992d3df94b474ab0344d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 19 Sep 2026 13:19:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=A2=84=E7=AE=97=E4=B8=8E?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E4=B8=8B=E8=BD=BD=E9=94=99=E8=AF=AF=E7=9A=84?= =?UTF-8?q?=20provider=20=E5=BD=92=E5=B1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit request_budget_exhausted_error 接收实际 provider,审计、日志与 Request 错误不再写死 vector-engine download_remote_image / download_images_from_urls / resolve_reference_images / parse_reference_image_data_url 逐层透传 provider executor 与 raw image edit 调用处传入各自 settings.provider,nanobanana 下载路径传入 VectorEngine --- .../src/image_provider/protocol/response.rs | 1 + .../src/image_provider/runtime/budget.rs | 11 +++-- .../src/image_provider/runtime/executor.rs | 4 ++ .../image_provider/runtime/image_source.rs | 45 +++++++++++++------ .../platform-image/src/raw_image_edit/mod.rs | 1 + 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/server-rs/crates/platform-image/src/image_provider/protocol/response.rs b/server-rs/crates/platform-image/src/image_provider/protocol/response.rs index fa7ad079c..4d5a10683 100644 --- a/server-rs/crates/platform-image/src/image_provider/protocol/response.rs +++ b/server-rs/crates/platform-image/src/image_provider/protocol/response.rs @@ -109,6 +109,7 @@ pub(crate) async fn handle_image_response( task_id, image_urls, candidate_count, + provider, request_deadline, failure_context, ) diff --git a/server-rs/crates/platform-image/src/image_provider/runtime/budget.rs b/server-rs/crates/platform-image/src/image_provider/runtime/budget.rs index d0f1bffc9..5876fa570 100644 --- a/server-rs/crates/platform-image/src/image_provider/runtime/budget.rs +++ b/server-rs/crates/platform-image/src/image_provider/runtime/budget.rs @@ -1,7 +1,7 @@ use std::time::{Duration, Instant}; use crate::image_provider::{ - audit::build_failure_audit, constants::VECTOR_ENGINE_PROVIDER, error::PlatformImageError, + audit::build_failure_audit, error::PlatformImageError, types::ImageProvider, }; pub(crate) fn effective_request_timeout_ms( @@ -51,6 +51,7 @@ fn retry_delay_fits_request_deadline_at( } pub(crate) fn request_budget_exhausted_error( + provider: ImageProvider, request_url: &str, operation: &str, image_model: Option<&'static str>, @@ -59,9 +60,10 @@ pub(crate) fn request_budget_exhausted_error( reference_image_count: Option, ) -> PlatformImageError { const ERROR_SOURCE: &str = "external request deadline elapsed"; + let provider_label = provider.as_str(); let message = format!("{operation}:外部图片请求执行预算已耗尽"); let audit = build_failure_audit( - VECTOR_ENGINE_PROVIDER, + provider_label, request_url, operation, "request_budget", @@ -78,7 +80,7 @@ pub(crate) fn request_budget_exhausted_error( image_model, ); tracing::warn!( - provider = VECTOR_ENGINE_PROVIDER, + provider = provider_label, endpoint = %request_url, failure_stage = "request_budget", timeout = true, @@ -90,7 +92,7 @@ pub(crate) fn request_budget_exhausted_error( "ImageProvider 图片请求执行预算已耗尽" ); PlatformImageError::Request { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider_label, message, endpoint: Some(request_url.to_string()), timeout: true, @@ -164,6 +166,7 @@ mod tests { #[test] fn exhausted_budget_maps_to_timeout_request_error_and_audit() { let error = request_budget_exhausted_error( + ImageProvider::VectorEngine, "https://vector.example/v1/images/generations", "生成图片失败", None, 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 6ae07470a..b4167f8ca 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 @@ -78,6 +78,7 @@ pub async fn create_image_generation_with_model( let resolved_references = resolve_reference_images( http_client, reference_images, + settings.provider, failure_context, settings.request_deadline, ) @@ -116,6 +117,7 @@ pub async fn create_image_generation_with_model( else { return Err(finish_image_model_fallback_error( request_budget_exhausted_error( + settings.provider, request_url.as_str(), failure_context, auditable_image_model(upstream_model), @@ -307,6 +309,7 @@ pub async fn create_nanobanana_generate_content( effective_request_timeout_ms(settings.request_timeout_ms, settings.request_deadline) else { return Err(request_budget_exhausted_error( + ImageProvider::VectorEngine, request_url.as_str(), failure_context, None, @@ -550,6 +553,7 @@ pub async fn create_image_edit_with_references_and_model( else { return Err(finish_image_model_fallback_error( request_budget_exhausted_error( + settings.provider, request_url.as_str(), failure_context, auditable_image_model(upstream_model), diff --git a/server-rs/crates/platform-image/src/image_provider/runtime/image_source.rs b/server-rs/crates/platform-image/src/image_provider/runtime/image_source.rs index 3e12b0055..87d957a23 100644 --- a/server-rs/crates/platform-image/src/image_provider/runtime/image_source.rs +++ b/server-rs/crates/platform-image/src/image_provider/runtime/image_source.rs @@ -4,19 +4,21 @@ use std::time::Instant; use crate::image_provider::{ budget::request_budget_exhausted_error, - constants::{VECTOR_ENGINE_IMAGE_EDIT_MAX_REFERENCE_IMAGES, VECTOR_ENGINE_PROVIDER}, + constants::VECTOR_ENGINE_IMAGE_EDIT_MAX_REFERENCE_IMAGES, error::PlatformImageError, - types::{DownloadedImage, GeneratedImages, ReferenceImage}, + types::{DownloadedImage, GeneratedImages, ImageProvider, ReferenceImage}, }; pub async fn download_remote_image( http_client: &reqwest::Client, image_url: &str, + provider: ImageProvider, ) -> Result { let response = http_client.get(image_url).send().await.map_err(|error| { map_simple_request_error( format!("下载生成图片失败:{error}"), Some(image_url.to_string()), + provider, ) })?; let status = response.status(); @@ -30,11 +32,12 @@ pub async fn download_remote_image( map_simple_request_error( format!("读取生成图片内容失败:{error}"), Some(image_url.to_string()), + provider, ) })?; if !status.is_success() { return Err(PlatformImageError::Request { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider.as_str(), message: "下载生成图片失败".to_string(), endpoint: Some(image_url.to_string()), timeout: false, @@ -58,15 +61,17 @@ pub async fn download_remote_image( async fn download_remote_image_with_deadline( http_client: &reqwest::Client, image_url: &str, + provider: ImageProvider, request_deadline: Option, operation: &str, ) -> Result { let Some(request_deadline) = request_deadline else { - return download_remote_image(http_client, image_url).await; + return download_remote_image(http_client, image_url, provider).await; }; let started_at = Instant::now(); if request_deadline <= started_at { return Err(request_budget_exhausted_error( + provider, image_url, operation, None, @@ -77,11 +82,12 @@ async fn download_remote_image_with_deadline( } tokio::time::timeout_at( tokio::time::Instant::from_std(request_deadline), - download_remote_image(http_client, image_url), + download_remote_image(http_client, image_url, provider), ) .await .map_err(|_| { request_budget_exhausted_error( + provider, image_url, operation, None, @@ -97,6 +103,7 @@ pub(crate) async fn download_images_from_urls( task_id: String, image_urls: Vec, candidate_count: u32, + provider: ImageProvider, request_deadline: Option, operation: &str, ) -> Result { @@ -109,6 +116,7 @@ pub(crate) async fn download_images_from_urls( download_remote_image_with_deadline( http_client, image_url.as_str(), + provider, request_deadline, operation, ) @@ -126,6 +134,7 @@ pub(crate) async fn download_images_from_urls( pub(crate) async fn resolve_reference_images( http_client: &reqwest::Client, reference_images: &[String], + provider: ImageProvider, failure_context: &str, request_deadline: Option, ) -> Result, PlatformImageError> { @@ -135,7 +144,7 @@ pub(crate) async fn resolve_reference_images( .count(); if reference_count > VECTOR_ENGINE_IMAGE_EDIT_MAX_REFERENCE_IMAGES { return Err(PlatformImageError::InvalidRequest { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider.as_str(), message: format!( "{failure_context}:参考图最多允许 {VECTOR_ENGINE_IMAGE_EDIT_MAX_REFERENCE_IMAGES} 张,当前提交 {reference_count} 张。" ), @@ -147,7 +156,7 @@ pub(crate) async fn resolve_reference_images( if source.is_empty() { continue; } - if let Some(reference_image) = parse_reference_image_data_url(source, index)? { + if let Some(reference_image) = parse_reference_image_data_url(source, index, provider)? { resolved.push(reference_image); continue; } @@ -155,6 +164,7 @@ pub(crate) async fn resolve_reference_images( let downloaded = download_remote_image_with_deadline( http_client, source, + provider, request_deadline, failure_context, ) @@ -171,14 +181,14 @@ pub(crate) async fn resolve_reference_images( continue; } return Err(PlatformImageError::InvalidRequest { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider.as_str(), message: format!("{failure_context}:参考图必须是图片 Data URL 或 HTTP(S) URL。"), }); } if resolved.is_empty() { return Err(PlatformImageError::InvalidRequest { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider.as_str(), message: format!("{failure_context}:图片编辑需要至少一张参考图。"), }); } @@ -189,25 +199,26 @@ pub(crate) async fn resolve_reference_images( pub(crate) fn parse_reference_image_data_url( source: &str, index: usize, + provider: ImageProvider, ) -> Result, PlatformImageError> { let Some(body) = source.strip_prefix("data:") else { return Ok(None); }; let Some((mime_type, data)) = body.split_once(";base64,") else { return Err(PlatformImageError::InvalidRequest { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider.as_str(), message: "参考图 Data URL 必须是 base64 图片。".to_string(), }); }; if !mime_type.starts_with("image/") { return Err(PlatformImageError::InvalidRequest { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider.as_str(), message: "参考图 Data URL 必须是图片类型。".to_string(), }); } let bytes = BASE64_STANDARD.decode(data.trim()).map_err(|error| { PlatformImageError::InvalidRequest { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider.as_str(), message: format!("参考图 Data URL 解码失败:{error}"), } })?; @@ -290,9 +301,13 @@ pub(crate) fn infer_image_mime_type(bytes: &[u8]) -> Option { None } -fn map_simple_request_error(message: String, endpoint: Option) -> PlatformImageError { +fn map_simple_request_error( + message: String, + endpoint: Option, + provider: ImageProvider, +) -> PlatformImageError { PlatformImageError::Request { - provider: VECTOR_ENGINE_PROVIDER, + provider: provider.as_str(), message, endpoint, timeout: false, @@ -349,6 +364,7 @@ mod tests { let error = download_remote_image_with_deadline( &http_client, "http://127.0.0.1:9/not-called.png", + ImageProvider::VectorEngine, Some(Instant::now()), "下载测试图片失败", ) @@ -387,6 +403,7 @@ mod tests { let error = download_remote_image_with_deadline( &reqwest::Client::new(), format!("http://{address}/pending.png").as_str(), + ImageProvider::VectorEngine, Some(Instant::now() + Duration::from_millis(100)), "下载测试图片失败", ) diff --git a/server-rs/crates/platform-image/src/raw_image_edit/mod.rs b/server-rs/crates/platform-image/src/raw_image_edit/mod.rs index f4383d87e..bdede2d54 100644 --- a/server-rs/crates/platform-image/src/raw_image_edit/mod.rs +++ b/server-rs/crates/platform-image/src/raw_image_edit/mod.rs @@ -134,6 +134,7 @@ pub async fn create_raw_image_edit( effective_request_timeout_ms(settings.request_timeout_ms, settings.request_deadline) else { return Err(request_budget_exhausted_error( + settings.provider, url.as_str(), failure_context, Some(GPT_IMAGE_2_5_EDIT_MODEL),