|
|
@@ -487,6 +487,21 @@ class FlowLedgerService:
|
|
|
decision = _decision_for_content(content, bundle["decisions"])
|
|
|
status = _media_status(media)
|
|
|
stats = _record(content.get("statistics"))
|
|
|
+ decision_summary = self._decision_summary(decision, debug=debug) if decision else None
|
|
|
+ technical_retry_detail = _technical_retry_detail(decision, evidence, media)
|
|
|
+ # 内容审核拦截从"技术问题"里单独分出来:重试无效、原因不同,展示成独立类别。
|
|
|
+ if (
|
|
|
+ decision_summary
|
|
|
+ and decision_summary.get("action") == "TECHNICAL_RETRY_REQUIRED"
|
|
|
+ and _is_content_inspection_blocked(technical_retry_detail)
|
|
|
+ ):
|
|
|
+ decision_summary = {
|
|
|
+ **decision_summary,
|
|
|
+ "label": "模型内容审核拦截",
|
|
|
+ "reason": "content_inspection_blocked",
|
|
|
+ "reason_label": "视频被大模型内容安全审核拦截(非系统故障,重试无效)",
|
|
|
+ "content_inspection_blocked": True,
|
|
|
+ }
|
|
|
return {
|
|
|
"id": platform_id or _text(content.get("content_discovery_id"), "content"),
|
|
|
"content_discovery_id": _text(content.get("content_discovery_id")),
|
|
|
@@ -508,9 +523,9 @@ class FlowLedgerService:
|
|
|
"media_status": status,
|
|
|
"media_status_label": MEDIA_STATUS_LABELS.get(status, "视频状态待确认"),
|
|
|
"media_failure_reason": _media_failure_reason(media),
|
|
|
- "decision": self._decision_summary(decision, debug=debug) if decision else None,
|
|
|
+ "decision": decision_summary,
|
|
|
"gemini": self._evidence_summary(evidence, debug=debug) if evidence else None,
|
|
|
- "technical_retry_detail": _technical_retry_detail(decision, evidence, media),
|
|
|
+ "technical_retry_detail": technical_retry_detail,
|
|
|
"debug": {"content": content, "media": media, "evidence": evidence} if debug else None,
|
|
|
}
|
|
|
|
|
|
@@ -931,6 +946,18 @@ def _media_failure_reason(media: dict[str, Any] | None) -> str:
|
|
|
return _text(media.get("failure_reason") or raw.get("upload_failure_reason") or raw.get("failure_reason"))
|
|
|
|
|
|
|
|
|
+def _is_content_inspection_blocked(detail: dict[str, Any] | None) -> bool:
|
|
|
+ # 内容审核拦截:新数据 failure_type=content_inspection_blocked;旧数据从响应体文本兜底识别。
|
|
|
+ if not isinstance(detail, dict):
|
|
|
+ return False
|
|
|
+ if _text(detail.get("failure_type")) == "content_inspection_blocked":
|
|
|
+ return True
|
|
|
+ text = _text(
|
|
|
+ _record(_record(detail.get("openrouter")).get("response_summary")).get("text_excerpt")
|
|
|
+ ).lower()
|
|
|
+ return "datainspection" in text or "inappropriate content" in text
|
|
|
+
|
|
|
+
|
|
|
def _technical_retry_detail(
|
|
|
decision: dict[str, Any] | None,
|
|
|
evidence: dict[str, Any] | None,
|
|
|
@@ -1021,10 +1048,19 @@ def _technical_retry_brief_reason(
|
|
|
payload: dict[str, Any],
|
|
|
media_raw: dict[str, Any],
|
|
|
) -> str:
|
|
|
+ if failure_type == "content_inspection_blocked":
|
|
|
+ return "视频被大模型内容安全审核拦截(非系统故障,重试无效)"
|
|
|
if failure_type == "no_valid_play_url":
|
|
|
return "平台结果未找到可用正片 URL"
|
|
|
if failure_type == "no_play_url":
|
|
|
return "平台结果没有返回可用 play_url"
|
|
|
+ if failure_type == "qwen_client_timeout":
|
|
|
+ return "通义千问请求写入超时"
|
|
|
+ if failure_type == "qwen_http_error":
|
|
|
+ status = payload.get("http_status_code")
|
|
|
+ return f"通义千问返回 HTTP {status}" if status else "通义千问请求失败"
|
|
|
+ if failure_type == "qwen_response_invalid":
|
|
|
+ return "通义千问返回格式无法解析"
|
|
|
if failure_type == "gemini_client_timeout":
|
|
|
return "OpenRouter/Gemini 请求写入超时"
|
|
|
if failure_type == "gemini_http_error":
|
|
|
@@ -1042,6 +1078,8 @@ def _technical_retry_brief_reason(
|
|
|
|
|
|
|
|
|
def _technical_retry_stage(failure_type: str) -> str:
|
|
|
+ if failure_type == "content_inspection_blocked":
|
|
|
+ return "content_inspection"
|
|
|
if failure_type.startswith("oss_"):
|
|
|
return "oss"
|
|
|
if failure_type in {"no_play_url", "no_valid_play_url"}:
|
|
|
@@ -1050,6 +1088,8 @@ def _technical_retry_stage(failure_type: str) -> str:
|
|
|
return "ffmpeg"
|
|
|
if failure_type in {"video_fetch_failed", "video_download_failed"}:
|
|
|
return "video_download"
|
|
|
+ if failure_type.startswith("qwen_"):
|
|
|
+ return "dashscope"
|
|
|
if failure_type.startswith("gemini_"):
|
|
|
return "openrouter"
|
|
|
return "unknown"
|
|
|
@@ -1061,13 +1101,19 @@ def _technical_retry_stage_label(failure_type: str) -> str:
|
|
|
"play_url": "平台 play_url",
|
|
|
"video_download": "视频下载",
|
|
|
"ffmpeg": "ffmpeg",
|
|
|
+ "dashscope": "通义千问",
|
|
|
"openrouter": "OpenRouter/Gemini",
|
|
|
+ "content_inspection": "内容审核拦截",
|
|
|
"unknown": "未知阶段",
|
|
|
}[_technical_retry_stage(failure_type)]
|
|
|
|
|
|
|
|
|
def _technical_retry_failure_label(failure_type: str) -> str:
|
|
|
return {
|
|
|
+ "content_inspection_blocked": "模型内容审核拦截",
|
|
|
+ "qwen_client_timeout": "通义千问 客户端超时",
|
|
|
+ "qwen_http_error": "通义千问 HTTP 错误",
|
|
|
+ "qwen_response_invalid": "通义千问 响应无法解析",
|
|
|
"gemini_client_timeout": "Gemini 客户端超时",
|
|
|
"gemini_http_error": "Gemini HTTP 错误",
|
|
|
"gemini_response_invalid": "Gemini 响应无法解析",
|