fix: 大样图生成偶发失败加固(前端草图压缩1536px+后端超时5xx自动重试)

This commit is contained in:
HHY
2026-10-07 00:59:40 -07:00
parent acf1586ccc
commit 5795bf5352
4 changed files with 66 additions and 37 deletions
+52 -35
View File
@@ -124,41 +124,58 @@ async def generate(body: AiRenderBody, user: CurrentUser, db: DbSession):
base = settings.ai_base_url.rstrip("/")
headers = {"Authorization": f"Bearer {settings.ai_api_key}"}
timeout = httpx.Timeout(settings.ai_timeout, connect=15)
try:
async with httpx.AsyncClient(timeout=timeout) as client:
if body.mode == "blueprint":
img_bytes = base64.b64decode(body.sketch_b64.split(",")[-1])
mime = "image/jpeg" if img_bytes[:2] == b"\xff\xd8" else "image/png"
ext = "jpg" if mime == "image/jpeg" else "png"
resp = await client.post(
f"{base}/v1/images/edits", headers=headers,
data={"model": settings.ai_model, "prompt": prompt, "size": size, "n": "1"},
files={"image[]": (f"drawing.{ext}", img_bytes, mime)})
elif body.mode == "photo":
img_bytes = base64.b64decode(body.photo_b64.split(",")[-1])
mime = "image/jpeg" if img_bytes[:2] == b"\xff\xd8" else "image/png"
ext = "jpg" if mime == "image/jpeg" else "png"
resp = await client.post(
f"{base}/v1/images/edits", headers=headers,
data={"model": settings.ai_model, "prompt": prompt, "size": size, "n": "1"},
files={"image[]": (f"photo.{ext}", img_bytes, mime)})
else:
resp = await client.post(
f"{base}/v1/images/generations", headers=headers,
json={"model": settings.ai_model, "prompt": prompt, "n": 1, "size": size})
resp.raise_for_status()
data = resp.json()
item = (data.get("data") or [{}])[0]
b64 = item.get("b64_json")
if not b64:
return fail(f"生图服务返回异常: {str(data)[:200]}")
except httpx.TimeoutException:
return fail("AI 生图超时,请稍后重试")
except httpx.HTTPStatusError as e:
detail = e.response.text[:200] if e.response is not None else ""
return fail(f"生图服务错误({e.response.status_code if e.response else '?'}): {detail}")
except Exception as e:
return fail(f"生图失败: {str(e)[:150]}")
async def _post_image(client: httpx.AsyncClient) -> httpx.Response:
if body.mode == "blueprint":
img_bytes = base64.b64decode(body.sketch_b64.split(",")[-1])
mime = "image/jpeg" if img_bytes[:2] == b"\xff\xd8" else "image/png"
ext = "jpg" if mime == "image/jpeg" else "png"
return await client.post(
f"{base}/v1/images/edits", headers=headers,
data={"model": settings.ai_model, "prompt": prompt, "size": size, "n": "1"},
files={"image[]": (f"drawing.{ext}", img_bytes, mime)})
if body.mode == "photo":
img_bytes = base64.b64decode(body.photo_b64.split(",")[-1])
mime = "image/jpeg" if img_bytes[:2] == b"\xff\xd8" else "image/png"
ext = "jpg" if mime == "image/jpeg" else "png"
return await client.post(
f"{base}/v1/images/edits", headers=headers,
data={"model": settings.ai_model, "prompt": prompt, "size": size, "n": "1"},
files={"image[]": (f"photo.{ext}", img_bytes, mime)})
return await client.post(
f"{base}/v1/images/generations", headers=headers,
json={"model": settings.ai_model, "prompt": prompt, "n": 1, "size": size})
b64 = None
last_err = ""
# 中转偶发超时/5xx 自动重试一次(生图耗时长, 失败重试显著提升成功率)
for attempt in range(2):
try:
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await _post_image(client)
resp.raise_for_status()
data = resp.json()
item = (data.get("data") or [{}])[0]
b64 = item.get("b64_json")
if b64:
break
last_err = f"生图服务返回异常: {str(data)[:200]}"
except httpx.TimeoutException:
last_err = "AI 生图超时,请稍后重试"
except httpx.HTTPStatusError as e:
detail = e.response.text[:200] if e.response is not None else ""
last_err = f"生图服务错误({e.response.status_code if e.response else '?'}): {detail}"
# 4xx(如鉴权/参数)重试无意义
if e.response is not None and e.response.status_code < 500:
break
except Exception as e: # noqa: BLE001
last_err = f"生图失败: {str(e)[:150]}"
if attempt == 0 and b64 is None:
stage_wait = 3 # 重试前短暂等待
import asyncio
await asyncio.sleep(stage_wait)
if not b64:
return fail(last_err or "生成失败")
return ok(data={"image_b64": b64, "prompt": prompt, "size": size,
"features": {"width_mm": info["width_mm"], "height_mm": info["height_mm"],
"summary": info["summary"]}},