fix(image): 图生图参考图过大时降采样,根治 yunqi 400 invalid_image_file
杂鱼煲商品三视图本地/线上都 400:主图 6000×4000 / 5.8MB,yunqi(gpt-image edits)拒收过大参考图 报 invalid_image_file(实测缩到 ≤2048 即过)。image_edit 发参考图前按需降采样:最长边 >2048 或 >4MB 才缩(否则原图直发不损质量);三视图成品 ≤1536px,2048 已 ≥ 成品分辨率 → 对成品无损。 保留透明通道(有 alpha 存 PNG,否则 JPEG q92),解析失败原样放行。商品三视图/故事板/模特上身等 所有 image_edit 路径一并受益。 验证:用真实 6000×4000 主图走打补丁的 provider.image_edit → 当场出图(原必 400);探测 yunqi 接受 ≥4096、6000 拒;48 测全绿。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
be4bc1c8f4
commit
304a083184
@@ -1,10 +1,35 @@
|
||||
from typing import Any
|
||||
import io
|
||||
|
||||
import requests
|
||||
|
||||
from .volcano import VolcanoArkProvider
|
||||
|
||||
|
||||
def _downscale_ref_for_edit(data: bytes, content_type: str, max_edge: int = 2048) -> tuple[bytes, str]:
|
||||
"""图生图参考图过大时按需降采样,避免中转站(yunqi/gpt-image edits)拒收大图报 400
|
||||
「invalid_image_file」(实测原图 6000×4000 / 5.8MB 被拒,缩到 ≤2048 即通过)。
|
||||
|
||||
只在「最长边 > max_edge 或体积 > 4MB」时才处理,否则原样返回(不损质量)。
|
||||
三视图等成品输出 ≤1536px,参考图 2048 已 ≥ 输出分辨率 → 降到 2048 对成品零损失,只是去掉
|
||||
成品用不上的冗余像素。保留透明通道(有 alpha 存 PNG,否则 JPEG q92)。解析失败则原样放行。"""
|
||||
try:
|
||||
from PIL import Image
|
||||
|
||||
im = Image.open(io.BytesIO(data))
|
||||
if max(im.size) <= max_edge and len(data) <= 4_000_000:
|
||||
return data, content_type
|
||||
im.thumbnail((max_edge, max_edge))
|
||||
out = io.BytesIO()
|
||||
if im.mode in ("RGBA", "LA", "P"):
|
||||
im.convert("RGBA").save(out, format="PNG", optimize=True)
|
||||
return out.getvalue(), "image/png"
|
||||
im.convert("RGB").save(out, format="JPEG", quality=92)
|
||||
return out.getvalue(), "image/jpeg"
|
||||
except Exception: # noqa: BLE001 — 非图/解析失败:原样发,让上游/中转站决定,不因压缩环节中断生成
|
||||
return data, content_type
|
||||
|
||||
|
||||
class OpenAICompatibleProvider(VolcanoArkProvider):
|
||||
"""通用 OpenAI 兼容中转站适配器(tokenssr / yunqi / 任意 New-API 网关)。
|
||||
|
||||
@@ -78,12 +103,14 @@ class OpenAICompatibleProvider(VolcanoArkProvider):
|
||||
for idx, ref in enumerate(images or []):
|
||||
fileobj, content_type = self.media_to_bytes(ref)
|
||||
content_type = content_type or "image/png"
|
||||
# 过大参考图按需降采样(≤2048,对成品零损失),否则中转站拒收报 400 invalid_image_file
|
||||
img_bytes, content_type = _downscale_ref_for_edit(fileobj.getvalue(), content_type)
|
||||
ext = "png"
|
||||
if "jpeg" in content_type or "jpg" in content_type:
|
||||
ext = "jpg"
|
||||
elif "webp" in content_type:
|
||||
ext = "webp"
|
||||
files.append(("image[]", (f"ref{idx + 1}.{ext}", fileobj.getvalue(), content_type)))
|
||||
files.append(("image[]", (f"ref{idx + 1}.{ext}", img_bytes, content_type)))
|
||||
if not files:
|
||||
raise ValueError("image_edit 至少需要一张参考图")
|
||||
data = {"model": model, "prompt": prompt, "size": size, "n": "1"}
|
||||
|
||||
Reference in New Issue
Block a user