fix: 大样图生成偶发失败加固(前端草图压缩1536px+后端超时5xx自动重试)
This commit is contained in:
@@ -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"]}},
|
||||
|
||||
Reference in New Issue
Block a user