import type { AgentRunSnapshot } from "./protocol"; import { videoNodeType, videoNodeUrl, type VideoRuntimeNode } from "./video-protocol"; export async function verifyVideoAgentResults(params: { snapshot: AgentRunSnapshot; nodes: VideoRuntimeNode[]; inspect: ( node: VideoRuntimeNode, requirements: string, ) => Promise<{ passed: boolean; issues: string[]; duration?: number; width?: number; height?: number; }>; }) { const { snapshot } = params; const requirements = snapshot.videoRequirements; const produced = snapshot.resultNodeIds.map((id) => params.nodes.find((node) => node.id === id)); const issues: string[] = []; if (produced.some((node) => !node)) issues.push("部分结果节点已不存在,请核对画布"); const videos = produced.filter((node): node is VideoRuntimeNode => Boolean(node && videoNodeType(node) === "video"), ); const scripts = produced.filter((node) => node && ["script", "text"].includes(node.kind)); const plan = snapshot.calls.find( (call) => call.name === "planCanvasTask" && call.status === "succeeded", )?.output; const expected = requirements?.expectedVideoCount ?? Number(plan?.expectedVideoCount || 1); if (requirements?.expectsVideo && videos.length !== expected) issues.push(`需要 ${expected} 条视频,当前仅完成 ${videos.length} 条;脚本和关键帧不计为成片`); if (requirements?.expectsScript && !scripts.length) issues.push("尚未交付分镜脚本节点"); if ( !produced.length && !snapshot.calls.some( (call) => ["analyzeVideoMaterials", "editVideoCanvas", "listVideoCanvasCapabilities"].includes( call.name, ) && call.status === "succeeded", ) ) issues.push("尚未交付任何创作或分析结果"); for (const video of videos) { if (!videoNodeUrl(video)) { issues.push(`${video.title} 缺少可用视频地址`); continue; } if (requirements?.duration && Number(video.draftValues?.duration) !== requirements.duration) issues.push(`${video.title} 生成时长未采用最新要求 ${requirements.duration} 秒`); if (requirements?.ratio && video.draftValues?.ratio !== requirements.ratio) issues.push(`${video.title} 生成比例未采用最新要求 ${requirements.ratio}`); try { const result = await params.inspect( video, [snapshot.objective, ...snapshot.inputs.map((input) => input.text)].join("\n"), ); if (!result.duration || !result.width || !result.height) issues.push(`${video.title} 缺少实际视频时长或尺寸,不能仅按请求参数验收`); const duration = requirements?.duration || (video.draftValues?.mediaOperation ? undefined : snapshot.videoSettings?.duration); if (duration && result.duration && Math.abs(result.duration - duration) > 1) issues.push( `${video.title} 实际时长 ${result.duration.toFixed(1)} 秒,不符合 ${duration} 秒要求`, ); const ratio = ( requirements?.ratio || (video.draftValues?.mediaOperation ? undefined : snapshot.videoSettings?.ratio) || "" ) .split(":") .map(Number); if ( result.width && result.height && ratio.length === 2 && Math.abs(result.width / result.height - ratio[0] / ratio[1]) > 0.05 ) issues.push(`${video.title} 实际尺寸 ${result.width}×${result.height} 不符合指定比例`); if (!result.passed || result.issues.length) issues.push( ...(result.issues.length ? result.issues : [`${video.title} 的画面内容需要检查`]), ); } catch { issues.push(`${video.title} 暂时无法读取或完成画面检查;已保留生成结果,请稍后核对`); } } // A failed visual review must never trigger another paid video automatically. return { passed: issues.length === 0, issues: issues.slice(0, 12), retryable: false }; }