diff --git a/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md b/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md index 393eb6fb4..c6a4cd3e1 100644 --- a/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md +++ b/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md @@ -85,7 +85,7 @@ raw 操作使用独立的 operation / ledger 命名空间,例如 `raw-image-ed `platform-image` 保留 VectorEngine 协议细节。raw handler 只负责:认证、multipart 字段解析、PNG 预检查、计费编排和响应映射。provider 请求仍由 `platform-image` 统一构造,并携带 `model`、`n`、`quality`、`background`、`output_format`、尺寸及图片参考字节。 -provider 响应只提取并透传 `data[].b64_json` 字符串,不在服务端解码图片 base64,也不读取或回传 provider 的 `output_format`(该字段只是请求参数回显)。GPT-Image-2 原生只返回 `b64_json`,因此不发送 `response_format` 参数,也不实现 URL 响应下载或兼容分支。发送、响应读取、上游状态、响应解析和缺图失败必须生成 `PlatformImageFailureAudit`,由 api-server 写入现有外部 API 失败审计链;成功结果同时写入统一的 `external_generation_run` 追踪事件。raw handler 只将上游 `b64_json` 原样写入 `data[].b64_json`。 +provider 响应只提取并透传 `data[].b64_json` 字符串,不在服务端解码图片 base64,也不读取或回传 provider 的 `output_format`(该字段只是请求参数回显)。GPT-Image-2 原生只返回 `b64_json`,因此不发送 `response_format` 参数,也不实现 URL 响应下载或兼容分支。发送、响应读取、上游状态、响应解析和缺图失败必须生成 `PlatformImageFailureAudit`,由 api-server 写入现有外部 API 失败审计链;成功结果同时写入统一的 `external_generation_run` 追踪事件。raw handler 只将上游 `b64_json` 原样写入 `data[].b64_json`。`RawImageEditResult.recovered_failure_audits` 保留共享结果契约,但 raw-edit 当前只有一次同步 provider 调用、没有 fallback,因此成功结果该列表恒为空;若后续增加 fallback,api-server 必须先落库其中的每条失败审计,再写成功运行摘要。 ## 代码拆分 diff --git a/server-rs/crates/api-server/src/openai_image_generation.rs b/server-rs/crates/api-server/src/openai_image_generation.rs index f0fa7591c..e1debf606 100644 --- a/server-rs/crates/api-server/src/openai_image_generation.rs +++ b/server-rs/crates/api-server/src/openai_image_generation.rs @@ -492,7 +492,7 @@ pub(crate) async fn record_openai_image_failure_if_configured( record_openai_image_failure_audit_if_configured(settings, audit).await; } -async fn record_openai_image_failure_audit_if_configured( +pub(crate) async fn record_openai_image_failure_audit_if_configured( settings: &OpenAiImageSettings, audit: &platform_image::PlatformImageFailureAudit, ) { diff --git a/server-rs/crates/api-server/src/raw_image.rs b/server-rs/crates/api-server/src/raw_image.rs index 1af3d4c91..873b0398c 100644 --- a/server-rs/crates/api-server/src/raw_image.rs +++ b/server-rs/crates/api-server/src/raw_image.rs @@ -21,8 +21,8 @@ use crate::{ auth::AuthenticatedAccessToken, http_error::AppError, openai_image_generation::{ - map_platform_image_error, record_openai_image_failure_if_configured, - require_openai_image_settings, + map_platform_image_error, record_openai_image_failure_audit_if_configured, + record_openai_image_failure_if_configured, require_openai_image_settings, }, request_context::RequestContext, state::AppState, @@ -125,6 +125,10 @@ pub(crate) async fn edit_raw_image( return Err(map_platform_image_error(error)); } }; + for audit in &generated.recovered_failure_audits { + record_openai_image_failure_audit_if_configured(&audit_settings, audit).await; + } + let recovered_failure_count = generated.recovered_failure_audits.len(); let data: Vec = generated .b64_images .into_iter() @@ -141,7 +145,10 @@ pub(crate) async fn edit_raw_image( true, None, Some("raw-image-edit".to_string()), - Some(json!({ "imageCount": data.len() })), + Some(json!({ + "imageCount": data.len(), + "recoveredFailureCount": recovered_failure_count, + })), ) .await; } diff --git a/server-rs/crates/platform-image/src/vector_engine/raw_edit.rs b/server-rs/crates/platform-image/src/vector_engine/raw_edit.rs index 5d722ffa8..ea944a190 100644 --- a/server-rs/crates/platform-image/src/vector_engine/raw_edit.rs +++ b/server-rs/crates/platform-image/src/vector_engine/raw_edit.rs @@ -34,8 +34,15 @@ pub struct RawImageEditImage { } #[derive(Clone, Debug)] +/// Successful response from the synchronous raw GPT Image 2 edit call. pub struct RawImageEditResult { pub b64_images: Vec, + /// Failure audits recovered by an internal fallback attempt. + /// + /// Raw-edit currently performs one provider call and has no fallback, so + /// this is empty on every successful result. The field remains part of the + /// shared result contract so adding a fallback later cannot silently drop + /// its audit trail from callers that already handle recovered failures. pub recovered_failure_audits: Vec, }