From ffd881fca58a3f4dd8827c6c0d15e01dfa04f7a5 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 19 Feb 2026 12:10:50 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=9D=E7=95=99=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E6=8B=92=E7=BB=9D=E5=93=8D=E5=BA=94=E4=BD=93=E5=B9=B6=E5=87=86?= =?UTF-8?q?=E7=A1=AE=E6=98=A0=E5=B0=84=E8=A2=AB=E6=8B=92=E7=BB=9D=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/grok/services/image_edit.py | 5 ++ app/services/reverse/assets_upload.py | 58 ++++++++++++++++++++---- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/app/services/grok/services/image_edit.py b/app/services/grok/services/image_edit.py index ed880e2..5d94abd 100644 --- a/app/services/grok/services/image_edit.py +++ b/app/services/grok/services/image_edit.py @@ -50,11 +50,16 @@ def _is_upload_rejected_error(exc: Exception) -> bool: details = getattr(exc, "details", None) if isinstance(details, dict): + status = details.get("status") body = str(details.get("body") or "").lower() + err = str(details.get("error") or "").lower() if "content moderated" in body or "content-moderated" in body: return True if '"code":3' in body or "'code': 3" in body: return True + # 某些链路只返回 400 + '"code"' 关键词,按拒绝处理。 + if status == 400 and ('"code"' in err or "moderated" in err): + return True return False diff --git a/app/services/reverse/assets_upload.py b/app/services/reverse/assets_upload.py index f2295f1..6c1f24b 100644 --- a/app/services/reverse/assets_upload.py +++ b/app/services/reverse/assets_upload.py @@ -4,6 +4,7 @@ Reverse interface: upload asset. import asyncio import json +import urllib.error import urllib.request from dataclasses import dataclass from typing import Any @@ -45,21 +46,44 @@ class AssetsUploadReverse: req = urllib.request.Request(url=url, data=body, headers=headers, method="POST") def _do_post(): + def _read_http_error(err: urllib.error.HTTPError): + status = int(getattr(err, "code", 500) or 500) + hdrs = {} + try: + hdrs = { + str(k).lower(): str(v) + for k, v in dict((err.headers or {}).items()).items() + } + except Exception: + hdrs = {} + try: + body = err.read().decode("utf-8", errors="replace") + except Exception: + body = "" + return status, hdrs, body + if opener is not None: - with opener.open(req, timeout=timeout) as resp: + try: + with opener.open(req, timeout=timeout) as resp: + status = int(getattr(resp, "status", 200) or 200) + raw_headers = { + str(k).lower(): str(v) + for k, v in dict(resp.headers.items()).items() + } + text = resp.read().decode("utf-8", errors="replace") + return status, raw_headers, text + except urllib.error.HTTPError as e: + return _read_http_error(e) + try: + with urllib.request.urlopen(req, timeout=timeout) as resp: status = int(getattr(resp, "status", 200) or 200) raw_headers = { str(k).lower(): str(v) for k, v in dict(resp.headers.items()).items() } text = resp.read().decode("utf-8", errors="replace") return status, raw_headers, text - with urllib.request.urlopen(req, timeout=timeout) as resp: - status = int(getattr(resp, "status", 200) or 200) - raw_headers = { - str(k).lower(): str(v) for k, v in dict(resp.headers.items()).items() - } - text = resp.read().decode("utf-8", errors="replace") - return status, raw_headers, text + except urllib.error.HTTPError as e: + return _read_http_error(e) status, raw_headers, text = await asyncio.to_thread(_do_post) return AssetsUploadReverse._SimpleResponse( @@ -192,6 +216,17 @@ class AssetsUploadReverse: "AssetsUpload recovered by forced direct fallback after transient error" ) return response + body_preview = "" + try: + body_preview = (response.text or "").strip().replace("\n", " ") + except Exception: + body_preview = "" + if len(body_preview) > 300: + body_preview = f"{body_preview[:300]}...(len={len(body_preview)})" + raise UpstreamException( + message=f"AssetsUpload forced direct failed: {response.status_code}", + details={"status": response.status_code, "body": body_preview}, + ) except Exception as forced_direct_err: logger.warning( "AssetsUpload forced direct fallback failed, " @@ -211,6 +246,13 @@ class AssetsUploadReverse: "AssetsUpload recovered by forced urllib fallback after transient error" ) return response + body_preview = (response.text or "").strip().replace("\n", " ") + if len(body_preview) > 300: + body_preview = f"{body_preview[:300]}...(len={len(body_preview)})" + raise UpstreamException( + message=f"AssetsUpload forced urllib failed: {response.status_code}", + details={"status": response.status_code, "body": body_preview}, + ) except Exception as forced_urllib_err: logger.warning( "AssetsUpload forced urllib fallback failed, "