限制 Raw prompt 原始字节大小
将 prompt UTF-8 原始字节限制为 16 KiB 补充超限回归测试并同步请求合同
This commit is contained in:
@@ -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` 必填。`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 原始字节长度不得超过 `16 KiB`;超限在扣费前返回 400。`quality`、`background` 和 `output_format` 采用 GPT Image 模型支持的值。
|
||||
|
||||
`width`、`height` 使用严格输出尺寸规则,均在扣费前校验:
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ pub(crate) struct RawImageEditResponse {
|
||||
pub(crate) data: Vec<RawImageEditItem>,
|
||||
}
|
||||
|
||||
const RAW_IMAGE_MAX_PROMPT_BYTES: usize = 16 * 1024;
|
||||
|
||||
pub(crate) async fn edit_raw_image(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
@@ -156,6 +158,11 @@ fn prepare_request(payload: RawImageEditRequest) -> Result<PreparedRawImageEdit,
|
||||
if payload.prompt.trim().is_empty() {
|
||||
return Err(bad_request("prompt 不能为空"));
|
||||
}
|
||||
if payload.prompt.len() > RAW_IMAGE_MAX_PROMPT_BYTES {
|
||||
return Err(bad_request(format!(
|
||||
"prompt 不能超过 {RAW_IMAGE_MAX_PROMPT_BYTES} 字节"
|
||||
)));
|
||||
}
|
||||
validate_raw_image_edit_dimensions(payload.width, payload.height)
|
||||
.map_err(|error| bad_request(error.to_string()))?;
|
||||
validate_optional_value(
|
||||
@@ -430,4 +437,21 @@ mod tests {
|
||||
|
||||
assert!(format!("{error:?}").contains("mask 尺寸必须与 image 一致"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prompt_uses_raw_utf8_byte_limit() {
|
||||
let payload = serde_json::json!({
|
||||
"image": {"data": encoded_png(1, 1), "mimeType": "image/png"},
|
||||
"prompt": "a".repeat(RAW_IMAGE_MAX_PROMPT_BYTES + 1),
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
});
|
||||
let parsed: RawImageEditRequest = serde_json::from_value(payload).expect("request");
|
||||
let error = match prepare_request(parsed) {
|
||||
Ok(_) => panic!("oversized prompt should fail before image decode and billing"),
|
||||
Err(error) => error,
|
||||
};
|
||||
|
||||
assert!(format!("{error:?}").contains("prompt 不能超过 16384 字节"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ base64 = { workspace = true }
|
||||
curl = { workspace = true }
|
||||
image = { workspace = true, features = ["jpeg", "png", "webp"] }
|
||||
reqwest = { workspace = true, features = ["json", "multipart", "rustls-tls"] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true, features = ["io-util", "macros", "net", "time"] }
|
||||
tracing = { workspace = true }
|
||||
|
||||
Reference in New Issue
Block a user