fix: 优化AI生成失败提示与素材失效处理

This commit is contained in:
hh
2026-07-21 14:02:10 +08:00
parent d0c3690d47
commit a99aae8231
12 changed files with 746 additions and 20 deletions
+62 -9
View File
@@ -109,8 +109,9 @@ def build_content_items(*, team, prompt: str, mode: str, references: list) -> di
"""
content_items: list[dict] = []
snapshots: list[dict] = []
resolved_library_assets: list[dict[str, str]] = []
seen_urls: set[str] = set()
group_cache: dict[str, list[tuple[str, str, float]]] = {}
group_cache: dict[str, list[FreeAsset]] = {}
label_to_placeholder: dict[str, str] = {}
image_n = video_n = audio_n = 0
video_duration_total = 0.0 # 输入参考视频总时长(token 公式的输入项 + ≤15s 校验)
@@ -147,18 +148,28 @@ def build_content_items(*, team, prompt: str, mode: str, references: list) -> di
content_items.append(item)
return "Image"
def _resolve_group_assets(group: FreeAssetGroup) -> list[tuple[str, str, float]]:
resolved: list[tuple[str, str, float]] = []
def _resolve_group_assets(group: FreeAssetGroup) -> list[FreeAsset]:
resolved: list[FreeAsset] = []
for fa in group.assets.exclude(remote_asset_id="").order_by("created_at"):
if fa.status == FreeAsset.Status.PROCESSING and not _refresh_processing_free_asset(fa):
continue # 未就绪的跳过
if fa.status != FreeAsset.Status.ACTIVE:
continue
resolved.append(
(f"asset://{_normalize_remote_asset_id(fa.remote_asset_id)}", fa.asset_type, fa.duration or 0.0)
)
resolved.append(fa)
return resolved
def _remember_library_asset(fa: FreeAsset) -> None:
if any(item["local_asset_id"] == str(fa.id) for item in resolved_library_assets):
return
resolved_library_assets.append(
{
"local_asset_id": str(fa.id),
"remote_asset_id": fa.remote_asset_id,
"submitted_remote_asset_id": _normalize_remote_asset_id(fa.remote_asset_id),
"asset_name": fa.name,
}
)
for ref in references or []:
url = str(ref.get("url") or "")
ref_type = str(ref.get("type") or "image")
@@ -199,6 +210,7 @@ def build_content_items(*, team, prompt: str, mode: str, references: list) -> di
if fa.status != FreeAsset.Status.ACTIVE or not fa.remote_asset_id:
raise ValueError(f"素材「{label or fa.name}」尚未就绪,请稍后重试")
resolved_url = f"asset://{_normalize_remote_asset_id(fa.remote_asset_id)}"
_remember_library_asset(fa)
kind = {"Video": "video", "Audio": "audio"}.get(fa.asset_type, "image")
if mode == "keyframe":
if kind != "image":
@@ -220,9 +232,16 @@ def build_content_items(*, team, prompt: str, mode: str, references: list) -> di
asset_list = group_cache[gid]
if not asset_list:
raise ValueError(f"素材「{label or '未命名'}」尚未就绪,请在素材库中确认状态为「可用」后重试")
for asset_url, asset_type, dur in asset_list:
kind = {"Video": "video", "Audio": "audio"}.get(asset_type, "image")
_push(kind, asset_url, "reference_video" if kind == "video" else ("reference_audio" if kind == "audio" else "reference_image"), dur)
for fa in asset_list:
kind = {"Video": "video", "Audio": "audio"}.get(fa.asset_type, "image")
asset_url = f"asset://{_normalize_remote_asset_id(fa.remote_asset_id)}"
_push(
kind,
asset_url,
"reference_video" if kind == "video" else ("reference_audio" if kind == "audio" else "reference_image"),
fa.duration or 0.0,
)
_remember_library_asset(fa)
continue
# 直传素材(已上传 TOS 的直链)
@@ -260,6 +279,7 @@ def build_content_items(*, team, prompt: str, mode: str, references: list) -> di
"content_items": content_items,
"api_prompt": api_prompt,
"snapshots": snapshots,
"resolved_library_assets": resolved_library_assets,
"image_n": image_n,
"video_n": video_n,
"audio_n": audio_n,
@@ -267,6 +287,23 @@ def build_content_items(*, team, prompt: str, mode: str, references: list) -> di
}
def _unavailable_library_asset_target(resolved_assets: list[dict], raw_message: str) -> dict | None:
"""从本次已解析引用中精确定位 Provider 指出的失效素材;不按陌生字符串模糊查库。"""
message = str(raw_message or "").casefold()
matches = []
for item in resolved_assets:
submitted_id = str(item.get("submitted_remote_asset_id") or "").casefold()
original_id = str(item.get("remote_asset_id") or "").casefold()
if (submitted_id and submitted_id in message) or (original_id and original_id in message):
matches.append(item)
if len(matches) == 1:
return matches[0]
if not matches and len(resolved_assets) == 1:
return resolved_assets[0]
return None
def _reap_stale_free_video_tasks(*, team) -> None:
"""僵尸回收(趁每次新提交顺手做,无需定时任务):
· RESERVED 超 10 分钟:没提交到火山就死(worker 崩溃/进程重启)→ 标失败退费;
@@ -494,6 +531,22 @@ def submit_free_video(*, team, user, params: dict) -> AITask:
provider_code=code,
reference_id=str(task.id),
)
if public_error.code == "asset_unavailable":
target = _unavailable_library_asset_target(
built["resolved_library_assets"],
raw_message,
)
if target is not None:
try:
from apps.assets.free_asset_state import mark_remote_asset_unavailable
mark_remote_asset_unavailable(
team=team,
local_asset_id=target["local_asset_id"],
remote_asset_id=target["remote_asset_id"],
)
except Exception: # noqa: BLE001 - 素材状态同步是附带自愈,不能遮蔽任务失败与退费。
logger.exception("failed to mark unavailable free asset for task %s", task.id)
task.status = AITask.Status.FAILED
task.error_code = (code or "CreateTaskError")[:64]
task.error_message = raw_message[:2000]