From c049d68b8eeaf98e407deecfb77bd2f012b3ee50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 9 Sep 2026 12:03:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E7=B4=A7=20Raw=20=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E5=93=8D=E5=BA=94=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 要求 data 与 b64_json 必填并移除无用 provider id prompt 上限调整为 4 KiB 原始字节并同步文档 --- ...案】Raw GPT Image 2图片编辑代理-2026-09-07.md | 2 +- server-rs/crates/api-server/src/raw_image.rs | 7 +++---- .../src/vector_engine/raw_edit.rs | 19 ++++++++++--------- .../platform-image/src/vector_engine/types.rs | 1 - 4 files changed, 14 insertions(+), 15 deletions(-) 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 4c319b003..f8696987b 100644 --- a/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md +++ b/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md @@ -35,7 +35,7 @@ POST /api/raw/v1/images/edit } ``` -`image` 是必填的单图结构 `{ data, mimeType }`;`mask` 可选并使用相同结构。`image` 和 `mask` 的 `mimeType` 必须为 `image/png`,base64 解码后必须是可完整解码的有效 PNG 文件,提供 mask 时其宽高必须与 image 完全一致;空数据、非 PNG 字节、MIME 不匹配或尺寸不一致均在扣费前返回 400。服务端不把输入格式另建成请求参数。`prompt` 必填,UTF-8 原始字节长度不得超过 `16 KiB`;超限在扣费前返回 400。`quality`、`background` 和 `output_format` 采用 GPT Image 模型支持的值。 +`image` 是必填的单图结构 `{ data, mimeType }`;`mask` 可选并使用相同结构。`image` 和 `mask` 的 `mimeType` 必须为 `image/png`,base64 解码后必须是可完整解码的有效 PNG 文件,提供 mask 时其宽高必须与 image 完全一致;空数据、非 PNG 字节、MIME 不匹配或尺寸不一致均在扣费前返回 400。服务端不把输入格式另建成请求参数。`prompt` 必填,UTF-8 原始字节长度不得超过 `4 KiB`;超限在扣费前返回 400。`quality`、`background` 和 `output_format` 采用 GPT Image 模型支持的值。 `width`、`height` 使用严格输出尺寸规则,均在扣费前校验: diff --git a/server-rs/crates/api-server/src/raw_image.rs b/server-rs/crates/api-server/src/raw_image.rs index 1c46e7fbc..9dc066ae1 100644 --- a/server-rs/crates/api-server/src/raw_image.rs +++ b/server-rs/crates/api-server/src/raw_image.rs @@ -59,7 +59,7 @@ pub(crate) struct RawImageEditResponse { pub(crate) data: Vec, } -const RAW_IMAGE_MAX_PROMPT_BYTES: usize = 16 * 1024; +const RAW_IMAGE_MAX_PROMPT_BYTES: usize = 4 * 1024; pub(crate) async fn edit_raw_image( State(state): State, @@ -109,7 +109,6 @@ pub(crate) async fn edit_raw_image( return Err(map_platform_image_error(error)); } }; - let task_id = generated.task_id.clone(); let data: Vec = generated .b64_images .into_iter() @@ -125,7 +124,7 @@ pub(crate) async fn edit_raw_image( started_at_micros, true, None, - Some(task_id), + Some("raw-image-edit".to_string()), Some(json!({ "imageCount": data.len() })), ) .await; @@ -452,6 +451,6 @@ mod tests { Err(error) => error, }; - assert!(format!("{error:?}").contains("prompt 不能超过 16384 字节")); + assert!(format!("{error:?}").contains("prompt 不能超过 4096 字节")); } } 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 13f41e7e6..77c386322 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 @@ -28,13 +28,12 @@ pub struct RawImageEditOptions { #[derive(Debug, Deserialize)] struct RawImageEditResponsePayload { - id: Option, - data: Option>, + data: Vec, } #[derive(Debug, Deserialize)] struct RawImageEditResponseEntry { - b64_json: Option, + b64_json: String, } pub const RAW_IMAGE_MAX_EDGE: u32 = GPT_IMAGE_2_MAX_EDGE; @@ -262,9 +261,8 @@ pub async fn create_vector_engine_raw_image_edit( }; let b64_images = payload .data - .unwrap_or_default() .into_iter() - .filter_map(|entry| entry.b64_json) + .map(|entry| entry.b64_json) .collect::>(); if b64_images.is_empty() { let message = format!("{failure_context}:上游未返回 b64_json 图片"); @@ -291,7 +289,6 @@ pub async fn create_vector_engine_raw_image_edit( }); } Ok(RawImageEditResult { - task_id: payload.id.unwrap_or_else(|| "raw-image-edit".to_string()), b64_images, recovered_failure_audits: Vec::new(), }) @@ -387,7 +384,6 @@ fn status_class(status: u16) -> &'static str { #[cfg(test)] mod tests { use super::*; - use serde_json::json; #[test] fn raw_result_forwards_b64_without_decoding_or_using_output_format() { @@ -401,14 +397,19 @@ mod tests { assert_eq!( payload .data - .unwrap_or_default() .into_iter() - .filter_map(|entry| entry.b64_json) + .map(|entry| entry.b64_json) .collect::>(), vec!["not-base64-but-forwarded".to_string()] ); } + #[test] + fn raw_success_response_requires_typed_data_and_b64_json_fields() { + assert!(serde_json::from_str::(r#"{}"#).is_err()); + assert!(serde_json::from_str::(r#"{"data":[{}]}"#).is_err()); + } + #[test] fn raw_image_edit_dimensions_enforce_strict_contract() { assert!(validate_raw_image_edit_dimensions(1024, 640).is_ok()); diff --git a/server-rs/crates/platform-image/src/vector_engine/types.rs b/server-rs/crates/platform-image/src/vector_engine/types.rs index 82a69bb4a..d25ad5c12 100644 --- a/server-rs/crates/platform-image/src/vector_engine/types.rs +++ b/server-rs/crates/platform-image/src/vector_engine/types.rs @@ -18,7 +18,6 @@ pub struct GeneratedImages { #[derive(Clone, Debug)] pub struct RawImageEditResult { - pub task_id: String, pub b64_images: Vec, pub recovered_failure_audits: Vec, }