恢复旧图片链路字节类型
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 2m43s
Project CI / Native shell tests (pull_request) Failing after 14m6s

撤回非 raw_edit 的全局 Bytes 迁移

保留既有 VectorEngine 与编辑器路径的 Vec 字节契约
This commit is contained in:
2026-09-12 11:06:05 +08:00
parent afc9ecbd51
commit 7edf4a8f8b
7 changed files with 24 additions and 52 deletions
@@ -19,7 +19,7 @@ use module_assets::{
AssetObjectAccessPolicy, AssetObjectFieldError, AssetObjectUpsertInput,
build_asset_object_upsert_input, generate_asset_object_id,
};
use platform_image::{DownloadedImage, GPT_IMAGE_2_2K_LONG_EDGE_THRESHOLD};
use platform_image::DownloadedImage;
use platform_oss::{
LegacyAssetPrefix, OssHeadObjectRequest, OssObjectAccess, OssSignedGetObjectUrlRequest,
};
@@ -3841,11 +3841,7 @@ fn editor_image_price_size_from_pixels(size: &str) -> &'static str {
let Ok(height) = height.parse::<u32>() else {
return "1K";
};
if width.max(height) > GPT_IMAGE_2_2K_LONG_EDGE_THRESHOLD {
"2K"
} else {
"1K"
}
if width.max(height) > 1536 { "2K" } else { "1K" }
}
fn editor_image_edit_uses_nanobanana_generate_content(model: &str) -> bool {
@@ -3894,11 +3890,7 @@ fn infer_editor_image_edit_size_tier(model: &str, size: Option<&str>) -> Option<
if model == EDITOR_IMAGE_MODEL_NANOBANANA2 && long_edge <= 768 {
return Some("0.5K");
}
Some(if long_edge > GPT_IMAGE_2_2K_LONG_EDGE_THRESHOLD {
"2K"
} else {
"1K"
})
Some(if long_edge > 1536 { "2K" } else { "1K" })
}
fn parse_editor_image_edit_pixel_size(size: &str) -> Option<(u32, u32)> {
@@ -4646,7 +4638,7 @@ fn prepare_editor_image_edit_references(
);
}
for (index, reference) in reference_images.iter_mut().enumerate() {
let decoded = image::load_from_memory(reference.bytes.as_ref()).map_err(|error| {
let decoded = image::load_from_memory(reference.bytes.as_slice()).map_err(|error| {
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
"provider": "editor-image-edit",
"message": format!("图片改造参考图不是有效图片:{error}"),
@@ -4677,8 +4669,7 @@ fn prepare_editor_image_edit_references(
image::DynamicImage::ImageRgba8(aligned),
StatusCode::BAD_REQUEST,
"图片改造参考图 16 对齐失败",
)?
.into();
)?;
reference.mime_type = "image/png".to_string();
reference.file_name = format!("editor-image-edit-reference-{}.png", index + 1);
}
@@ -13522,7 +13513,7 @@ pub(crate) async fn read_editor_reference_image_object_with_client(
.to_string();
let extension = editor_reference_image_extension(mime_type.as_str());
Ok(OpenAiReferenceImage {
bytes: bytes.into(),
bytes,
mime_type,
file_name: format!("editor-reference.{extension}"),
})
@@ -13536,7 +13527,7 @@ async fn download_editor_persisted_image_object(
Ok(DownloadedOpenAiImage {
extension: editor_reference_image_extension(image.mime_type.as_str()).to_string(),
mime_type: image.mime_type,
bytes: image.bytes.to_vec(),
bytes: image.bytes,
})
}
@@ -19064,7 +19055,7 @@ mod tests {
#[tokio::test]
async fn icon_spritesheet_expired_deadline_skips_blocking_image_work() {
let source = DownloadedImage {
bytes: bytes::Bytes::from_static(b"not-an-image"),
bytes: b"not-an-image".to_vec(),
mime_type: "image/png".to_string(),
extension: "png".to_string(),
};
@@ -1107,7 +1107,7 @@ mod tests {
fn reference_image(index: usize) -> ReferenceImage {
ReferenceImage {
bytes: bytes::Bytes::from(vec![index as u8]),
bytes: vec![index as u8],
mime_type: "image/png".to_string(),
file_name: format!("reference-{index}.png"),
}
@@ -5,10 +5,3 @@ pub const VECTOR_ENGINE_GPT_IMAGE_2_MODEL: &str = GPT_IMAGE_2_MODEL;
pub const VECTOR_ENGINE_PROVIDER: &str = "vector-engine";
pub const VECTOR_ENGINE_IMAGE_EDIT_MAX_REFERENCE_IMAGES: usize = 5;
pub const VECTOR_ENGINE_NANOBANANA_MAX_REFERENCE_IMAGES: usize = 14;
/// GPT-Image-2 pricing uses 1K through this inclusive long-edge threshold.
pub const GPT_IMAGE_2_2K_LONG_EDGE_THRESHOLD: u32 = 1536;
pub(crate) const GPT_IMAGE_2_MIN_PIXELS: u64 = 655_360;
pub(crate) const GPT_IMAGE_2_MAX_PIXELS: u64 = 8_294_400;
pub(crate) const GPT_IMAGE_2_MAX_EDGE: u32 = 3_840;
pub(crate) const GPT_IMAGE_2_DIMENSION_ALIGNMENT: u32 = 16;
@@ -257,7 +257,7 @@ fn send_multipart_edit_request_with_curl_blocking(
form.part("image")
.buffer(
reference_image.file_name.as_str(),
reference_image.bytes.to_vec(),
reference_image.bytes.clone(),
)
.content_type(reference_image.mime_type.as_str())
.add()?;
@@ -338,7 +338,7 @@ mod tests {
"1024x1024",
1,
&[ReferenceImage {
bytes: bytes::Bytes::from_static(b"reference"),
bytes: b"reference".to_vec(),
mime_type: "image/png".to_string(),
file_name: "reference.png".to_string(),
}],
@@ -1,5 +1,4 @@
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
use bytes::Bytes;
use reqwest::header;
use std::time::Instant;
@@ -52,7 +51,7 @@ pub async fn download_remote_image(
Ok(DownloadedImage {
extension: mime_to_extension(normalized_mime_type.as_str()).to_string(),
mime_type: normalized_mime_type,
bytes: body,
bytes: body.to_vec(),
})
}
@@ -214,7 +213,7 @@ pub(crate) fn parse_reference_image_data_url(
})?;
let mime_type = normalize_downloaded_image_mime_type(mime_type);
Ok(Some(ReferenceImage {
bytes: Bytes::from(bytes),
bytes,
file_name: format!(
"reference-{index}.{}",
mime_to_extension(mime_type.as_str())
@@ -248,7 +247,7 @@ pub(crate) fn decode_generated_image_base64(raw: &str) -> Option<DownloadedImage
Some(DownloadedImage {
extension: mime_to_extension(mime_type.as_str()).to_string(),
mime_type,
bytes: bytes.into(),
bytes,
})
}
@@ -1,10 +1,7 @@
use serde_json::{Map, Value, json};
use super::{
constants::{
GPT_IMAGE_2_C_MODEL, GPT_IMAGE_2_DIMENSION_ALIGNMENT, GPT_IMAGE_2_MAX_EDGE,
GPT_IMAGE_2_MAX_PIXELS, GPT_IMAGE_2_MIN_PIXELS, GPT_IMAGE_2_MODEL,
},
constants::{GPT_IMAGE_2_C_MODEL, GPT_IMAGE_2_MODEL},
types::{ReferenceImage, VectorEngineImageSettings},
};
@@ -65,7 +62,7 @@ pub fn build_vector_engine_nanobanana_generate_content_request_body(
"mime_type": reference_image.mime_type,
"data": base64::Engine::encode(
&base64::engine::general_purpose::STANDARD,
reference_image.bytes.as_ref()
reference_image.bytes.as_slice()
),
},
}));
@@ -132,10 +129,10 @@ fn normalize_explicit_pixel_size(value: &str) -> String {
}
fn clamp_gpt_image_2_pixel_size(size: &str) -> String {
const MIN_PIXELS: u64 = GPT_IMAGE_2_MIN_PIXELS;
const MAX_PIXELS: u64 = GPT_IMAGE_2_MAX_PIXELS;
const MAX_EDGE: u32 = GPT_IMAGE_2_MAX_EDGE;
const DIMENSION_ALIGNMENT: u32 = GPT_IMAGE_2_DIMENSION_ALIGNMENT;
const MIN_PIXELS: u64 = 655_360;
const MAX_PIXELS: u64 = 8_294_400;
const MAX_EDGE: u32 = 3_840;
const DIMENSION_ALIGNMENT: u32 = 16;
const MAX_ASPECT_RATIO: f64 = 3.0;
// 中文注释:这里是 VectorEngine 的共享发送边界,只处理 gpt-image-2 的显式像素尺寸。
@@ -346,12 +343,12 @@ mod tests {
9,
&[
ReferenceImage {
bytes: bytes::Bytes::from_static(&[1, 2, 3, 4, 5]),
bytes: vec![1, 2, 3, 4, 5],
mime_type: "image/png".to_string(),
file_name: "reference-a.png".to_string(),
},
ReferenceImage {
bytes: bytes::Bytes::from(vec![8; 7]),
bytes: vec![8; 7],
mime_type: "image/jpeg".to_string(),
file_name: "reference-b.jpg".to_string(),
},
@@ -1,5 +1,4 @@
use super::audit::PlatformImageFailureAudit;
use bytes::Bytes;
#[derive(Clone, Debug)]
pub struct VectorEngineImageSettings {
@@ -17,23 +16,16 @@ pub struct GeneratedImages {
pub recovered_failure_audits: Vec<PlatformImageFailureAudit>,
}
#[derive(Clone, Debug)]
pub struct RawImageEditResult {
pub b64_images: Vec<String>,
pub recovered_failure_audits: Vec<PlatformImageFailureAudit>,
}
#[derive(Clone, Debug)]
pub struct DownloadedImage {
/// Shared immutable image bytes so callers can pass downloaded buffers downstream without copying.
pub bytes: Bytes,
pub bytes: Vec<u8>,
pub mime_type: String,
pub extension: String,
}
#[derive(Clone, Debug)]
pub struct ReferenceImage {
pub bytes: Bytes,
pub bytes: Vec<u8>,
pub mime_type: String,
pub file_name: String,
}