import type { ToolDefinition } from './types'; export const validateStoryboard: ToolDefinition = { name: 'validate_storyboard', description: '校验分镜脚本质量:TTS 估算 ≤ 6s、ratio 预检、script 拼接校验。返回校验结果和问题列表。', input_schema: { type: 'object', properties: { items: { type: 'string', description: '分镜 JSON 数组字符串,每个元素需包含 shotDesc、script 字段,可选 duration、directorRef' }, videoModelDuration: { type: 'number', description: '视频模型固定时长(秒),默认 6' }, }, required: ['items'], }, execute: async (params) => { const { items, videoModelDuration = 6 } = params as { items: string; videoModelDuration?: number }; let parsed: any[]; try { parsed = JSON.parse(items); } catch { return '错误: items 不是合法 JSON'; } if (!Array.isArray(parsed) || parsed.length === 0) return '错误: items 必须是非空数组'; const errors: string[] = []; const warnings: string[] = []; const TTS_SPEED = 1.15; const CHARS_PER_SEC = 5; for (const item of parsed) { const idx = item.id ?? parsed.indexOf(item) + 1; const script: string = item.script || ''; const charCount = script.length; const ttsEstimate = charCount / CHARS_PER_SEC; const audioDur = ttsEstimate * TTS_SPEED; const ratio = videoModelDuration / audioDur; if (!item.shotDesc) errors.push(`Shot ${idx}: 缺少 shotDesc`); if (!script) errors.push(`Shot ${idx}: 缺少 script`); // TTS 估算检查 if (ttsEstimate > 6) { errors.push(`Shot ${idx}: TTS 估算 ${ttsEstimate.toFixed(1)}s > 6s,必须拆分 (script: ${script.slice(0, 30)}...)`); } // ratio 预检 if (ratio < 0.9) { errors.push(`Shot ${idx}: ratio ${ratio.toFixed(2)} < 0.9,音频太长需拆分 (audio=${audioDur.toFixed(1)}s, video=${videoModelDuration}s)`); } if (!item.directorRef) { warnings.push(`Shot ${idx}: 建议填写 directorRef`); } } // script 拼接校验 - 返回统计而非原文比对(原文由用户提供) const totalChars = parsed.reduce((sum: number, i: any) => sum + (i.script?.length || 0), 0); const totalAudio = (totalChars / CHARS_PER_SEC) * TTS_SPEED; const result = { valid: errors.length === 0, shotCount: parsed.length, totalChars, estimatedTotalAudio: `${totalAudio.toFixed(1)}s`, errors, warnings, }; if (errors.length > 0) { return `❌ 分镜校验未通过 (${errors.length} 个问题):\n\n${errors.map((e, i) => `${i + 1}. ${e}`).join('\n')}${warnings.length ? `\n\n⚠️ 警告:\n${warnings.map((w, i) => `${i + 1}. ${w}`).join('\n')}` : ''}\n\n统计: ${parsed.length} 个镜头, 总字数 ${totalChars}, 音频估算 ${totalAudio.toFixed(1)}s`; } return `✅ 分镜校验通过\n\n统计: ${parsed.length} 个镜头, 总字数 ${totalChars}, 音频估算 ${totalAudio.toFixed(1)}s${warnings.length ? `\n\n⚠️ 警告:\n${warnings.map((w, i) => `${i + 1}. ${w}`).join('\n')}` : ''}`; }, };