122 lines
4.3 KiB
Python
122 lines
4.3 KiB
Python
"""Probe: seedream 5.0 i2i 是否接受 base64 data: URI 作 image_urls 项。
|
|
|
|
跑法: .venv/Scripts/python.exe scripts/probe_seedream_i2i.py
|
|
**base64 接受 -> 真出图,产生 ~0.22 CNY 费用**;base64 被拒 -> ArkError,不计费。
|
|
|
|
目的:确认 i2i 走 base64 通路是否可行(zcbot 内网部署,无法提供公网 URL 给 ARK 反向 fetch)。
|
|
如果 base64 通 → 后续 seedream i2i tool 可以直接用 base64,工程简单;
|
|
如果 base64 拒 → 要查 ARK 是否有"上传文件拿 file_id"接口或外接对象存储。
|
|
|
|
输出:
|
|
- HTTP 状态 / 响应 JSON(节选)
|
|
- 结论标签 [RESULT] base64-supported / base64-rejected / unclear
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import io
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
# 读 .env
|
|
env_file = ROOT / ".env"
|
|
if env_file.exists():
|
|
for line in env_file.read_text(encoding="utf-8").splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
k, _, v = line.partition("=")
|
|
os.environ.setdefault(k.strip(), v.strip())
|
|
|
|
from PIL import Image
|
|
|
|
from core.ark_client import ArkClient, ArkConfig, ArkError
|
|
|
|
|
|
def make_test_png() -> bytes:
|
|
"""256x256 蓝底 PNG,中间一个红圆 —— prompt 改色易验证 i2i 真生效。"""
|
|
img = Image.new("RGB", (256, 256), (50, 100, 200))
|
|
# 中心红圆
|
|
from PIL import ImageDraw
|
|
draw = ImageDraw.Draw(img)
|
|
draw.ellipse((80, 80, 176, 176), fill=(220, 60, 60))
|
|
buf = io.BytesIO()
|
|
img.save(buf, format="PNG")
|
|
return buf.getvalue()
|
|
|
|
|
|
def main() -> int:
|
|
cfg = ArkConfig.load()
|
|
if cfg is None:
|
|
print("[SKIP] ARK_API_KEY 未设 / config/media/doubao.yaml 缺,无法测真接口")
|
|
return 0
|
|
|
|
image_cfg = (cfg.raw.get("image") or {})
|
|
if not image_cfg:
|
|
print("[SKIP] doubao.yaml 无 image 段")
|
|
return 0
|
|
variant_key, variant_cfg = next(iter(image_cfg.items()))
|
|
model_id = variant_cfg["model_id"]
|
|
print(f"[setup] model={model_id} endpoint=/images/generations")
|
|
|
|
png_bytes = make_test_png()
|
|
b64 = base64.b64encode(png_bytes).decode("ascii")
|
|
data_uri = f"data:image/png;base64,{b64}"
|
|
print(f"[setup] reference image: {len(png_bytes)} bytes -> data URI len={len(data_uri)}")
|
|
|
|
body = {
|
|
"model": model_id,
|
|
"prompt": "Keep the layout but change the red circle to a yellow star, blue background unchanged.",
|
|
"image_urls": [data_uri],
|
|
"size": "2048x2048", # ARK 最小要求 3686400 像素 (~1920²),1024² 会被拒
|
|
"response_format": "url",
|
|
"watermark": False,
|
|
}
|
|
|
|
print("[probe] POST /images/generations with image_urls=[data:image/png;base64,...]")
|
|
try:
|
|
with ArkClient(cfg, timeout_s=120.0) as client:
|
|
resp = client.post_json("/images/generations", body, timeout_s=120.0)
|
|
except ArkError as e:
|
|
msg = str(e)
|
|
print(f"[ArkError] {msg}")
|
|
low = msg.lower()
|
|
if "base64" in low or "image_url" in low or "invalid_url" in low or "url" in low:
|
|
print("\n[RESULT] base64-rejected (error mentions image_url/base64/url)")
|
|
print(" -> i2i 需要走公网 URL(对象存储)或 ARK file 上传 endpoint(待查)")
|
|
else:
|
|
print("\n[RESULT] unclear (ArkError but not obviously a base64 format issue)")
|
|
return 2
|
|
|
|
print(f"[response keys] {list(resp.keys())}")
|
|
snippet = json.dumps(resp, ensure_ascii=False)[:600]
|
|
print(f"[response (first 600ch)]\n{snippet}")
|
|
|
|
# 解析出图 URL
|
|
data = resp.get("data")
|
|
new_url = None
|
|
if isinstance(data, list) and data:
|
|
first = data[0]
|
|
if isinstance(first, dict):
|
|
new_url = first.get("url") or first.get("image_url")
|
|
elif isinstance(data, dict):
|
|
imgs = data.get("images")
|
|
if isinstance(imgs, list) and imgs and isinstance(imgs[0], dict):
|
|
new_url = imgs[0].get("url")
|
|
|
|
if new_url:
|
|
print(f"\n[RESULT] base64-supported (got new image url: {new_url[:90]}...)")
|
|
print(f" estimated cost ~{variant_cfg.get('price_cny_per_image', 0.22)} CNY")
|
|
return 0
|
|
print("\n[RESULT] unclear (HTTP 200 but no image url in response — check JSON above)")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|