fix: 优化AI生成失败提示与素材失效处理
This commit is contained in:
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user