cerevox 2.43.1 → 3.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/ai.d.ts +26 -3
- package/dist/core/ai.d.ts.map +1 -1
- package/dist/core/ai.js +14 -12
- package/dist/core/ai.js.map +1 -1
- package/dist/mcp/servers/prompts/actions/storyboard_optimization.md +5 -5
- package/dist/{utils/videoproject-schema.json → mcp/servers/prompts/draft_content-schema.json} +3 -3
- package/dist/mcp/servers/prompts/rules/creative-ad.md +6 -6
- package/dist/mcp/servers/prompts/rules/expert.md +25 -25
- package/dist/mcp/servers/prompts/rules/freeform.md +2 -3
- package/dist/mcp/servers/prompts/rules/general-video.md +10 -10
- package/dist/mcp/servers/prompts/rules/material-creation.md +2 -2
- package/dist/mcp/servers/prompts/rules/music-video.md +3 -3
- package/dist/mcp/servers/prompts/rules/stage-play.md +4 -4
- package/dist/mcp/servers/prompts/rules/story-telling.md +8 -8
- package/dist/mcp/servers/prompts/skills/storyboard/storyboard-optimization-skill.md +5 -5
- package/dist/mcp/servers/prompts/skills/video/continuity-techniques.md +1 -1
- package/dist/mcp/servers/prompts/skills/workflows/general-video.md +10 -10
- package/dist/mcp/servers/prompts/skills/workflows/music-video.md +3 -3
- package/dist/mcp/servers/prompts/zerocut-core.md +26 -27
- package/dist/mcp/servers/zerocut.d.ts.map +1 -1
- package/dist/mcp/servers/zerocut.js +383 -427
- package/dist/mcp/servers/zerocut.js.map +1 -1
- package/dist/utils/coze.d.ts +1 -0
- package/dist/utils/coze.d.ts.map +1 -1
- package/dist/utils/coze.js +19 -0
- package/dist/utils/coze.js.map +1 -1
- package/package.json +2 -2
- package/dist/timeline-editor/index.d.ts +0 -42
- package/dist/timeline-editor/index.d.ts.map +0 -1
- package/dist/timeline-editor/index.js +0 -82
- package/dist/timeline-editor/index.js.map +0 -1
- package/dist/timeline-editor/public/app.js +0 -2086
- package/dist/timeline-editor/public/index.html +0 -141
- package/dist/timeline-editor/public/style.css +0 -695
- package/dist/timeline-editor/server.d.ts +0 -137
- package/dist/timeline-editor/server.d.ts.map +0 -1
- package/dist/timeline-editor/server.js +0 -418
- package/dist/timeline-editor/server.js.map +0 -1
- /package/dist/{utils → mcp/servers/prompts}/storyboard-schema.json +0 -0
|
@@ -211,7 +211,7 @@ async function initProject(session) {
|
|
|
211
211
|
// 创建素材目录和成品目录 materials、output
|
|
212
212
|
await (await terminal.run('mkdir materials output')).end();
|
|
213
213
|
// 文件包括(大部分不需要此时创建)
|
|
214
|
-
//
|
|
214
|
+
// storyboard.json 故事版
|
|
215
215
|
// draft_content.json 草稿内容,Agent在创作过程中,会根据项目规范,自动生成和修改该文件
|
|
216
216
|
return workDir;
|
|
217
217
|
}
|
|
@@ -1158,7 +1158,7 @@ server.registerTool('generate-image', {
|
|
|
1158
1158
|
type: zod_1.z.enum(['banana', 'seedream']).optional().default('seedream'),
|
|
1159
1159
|
prompt: zod_1.z
|
|
1160
1160
|
.string()
|
|
1161
|
-
.describe('The prompt to generate. 一般要严格对应
|
|
1161
|
+
.describe('The prompt to generate. 一般要严格对应 storyboard 中当前场景的 start_frame 或 end_frame 中的字段描述'),
|
|
1162
1162
|
sceneIndex: zod_1.z
|
|
1163
1163
|
.number()
|
|
1164
1164
|
.min(1)
|
|
@@ -1167,7 +1167,7 @@ server.registerTool('generate-image', {
|
|
|
1167
1167
|
storyBoardFile: zod_1.z
|
|
1168
1168
|
.string()
|
|
1169
1169
|
.optional()
|
|
1170
|
-
.default('
|
|
1170
|
+
.default('storyboard.json')
|
|
1171
1171
|
.describe('故事板文件路径'),
|
|
1172
1172
|
skipConsistencyCheck: zod_1.z
|
|
1173
1173
|
.boolean()
|
|
@@ -1252,7 +1252,7 @@ server.registerTool('generate-image', {
|
|
|
1252
1252
|
\`\`\`
|
|
1253
1253
|
`),
|
|
1254
1254
|
},
|
|
1255
|
-
}, async ({ type = 'seedream', prompt, sceneIndex, storyBoardFile = '
|
|
1255
|
+
}, async ({ type = 'seedream', prompt, sceneIndex, storyBoardFile = 'storyboard.json', skipConsistencyCheck = false, size = '720x1280', saveToFileName, watermark, referenceImages, optimizePrompt, }) => {
|
|
1256
1256
|
try {
|
|
1257
1257
|
// 验证session状态
|
|
1258
1258
|
const currentSession = await validateSession('generate-image');
|
|
@@ -1260,10 +1260,10 @@ server.registerTool('generate-image', {
|
|
|
1260
1260
|
// 检查 storyboard 标志
|
|
1261
1261
|
if (!checkStoryboardFlag && (0, node_fs_1.existsSync)(storyBoardPath)) {
|
|
1262
1262
|
checkStoryboardFlag = true;
|
|
1263
|
-
return createErrorResponse('必须先审查生成的
|
|
1263
|
+
return createErrorResponse('必须先审查生成的 storyboard.json 内容,确保每个场景中的stage_atmosphere内容按照规则被正确融合到start_frame和video_prompt中,不得遗漏,检查完成后先汇报,如果有问题,应当先修改 storyboard.json 内容,然后再调用 generate-image 生成图片。注意修改 storyboard 内容时,仅修改相应字段的字符串值,不要破坏JSON格式!', 'generate-image');
|
|
1264
1264
|
}
|
|
1265
1265
|
const validatedFileName = validateFileName(saveToFileName);
|
|
1266
|
-
// 校验 prompt 与
|
|
1266
|
+
// 校验 prompt 与 storyboard.json 中场景设定的一致性
|
|
1267
1267
|
if (sceneIndex && !skipConsistencyCheck) {
|
|
1268
1268
|
try {
|
|
1269
1269
|
if ((0, node_fs_1.existsSync)(storyBoardPath)) {
|
|
@@ -1283,9 +1283,9 @@ server.registerTool('generate-image', {
|
|
|
1283
1283
|
const endFrame = scene.end_frame;
|
|
1284
1284
|
// 检查 prompt 是否严格等于 start_frame 或 end_frame
|
|
1285
1285
|
if (prompt !== startFrame && prompt !== endFrame) {
|
|
1286
|
-
return createErrorResponse('图片提示词必须严格遵照
|
|
1286
|
+
return createErrorResponse('图片提示词必须严格遵照storyboard的设定,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-image');
|
|
1287
1287
|
}
|
|
1288
|
-
// 校验 size 参数与
|
|
1288
|
+
// 校验 size 参数与 storyboard 的 orientation 属性一致性
|
|
1289
1289
|
if (size && storyBoard.orientation) {
|
|
1290
1290
|
const isLandscapeSize = [
|
|
1291
1291
|
'1152x864',
|
|
@@ -1330,7 +1330,7 @@ server.registerTool('generate-image', {
|
|
|
1330
1330
|
}
|
|
1331
1331
|
}
|
|
1332
1332
|
else {
|
|
1333
|
-
console.warn(`Scene index ${sceneIndex} not found in
|
|
1333
|
+
console.warn(`Scene index ${sceneIndex} not found in storyboard.json`);
|
|
1334
1334
|
}
|
|
1335
1335
|
}
|
|
1336
1336
|
}
|
|
@@ -1340,7 +1340,7 @@ server.registerTool('generate-image', {
|
|
|
1340
1340
|
}
|
|
1341
1341
|
catch (error) {
|
|
1342
1342
|
console.error('Failed to validate prompt with story board:', error);
|
|
1343
|
-
// 如果读取或解析
|
|
1343
|
+
// 如果读取或解析 storyboard.json 失败,继续执行但记录警告
|
|
1344
1344
|
}
|
|
1345
1345
|
}
|
|
1346
1346
|
// 检查并替换英文单引号包裹的中文内容为中文双引号
|
|
@@ -1647,7 +1647,7 @@ server.registerTool('generate-video', {
|
|
|
1647
1647
|
inputSchema: {
|
|
1648
1648
|
prompt: zod_1.z
|
|
1649
1649
|
.string()
|
|
1650
|
-
.describe('The prompt to generate. 一般要严格对应
|
|
1650
|
+
.describe('The prompt to generate. 一般要严格对应 storyboard 中当前场景的 video_prompt 字段描述'),
|
|
1651
1651
|
sceneIndex: zod_1.z
|
|
1652
1652
|
.number()
|
|
1653
1653
|
.min(1)
|
|
@@ -1656,7 +1656,7 @@ server.registerTool('generate-video', {
|
|
|
1656
1656
|
storyBoardFile: zod_1.z
|
|
1657
1657
|
.string()
|
|
1658
1658
|
.optional()
|
|
1659
|
-
.default('
|
|
1659
|
+
.default('storyboard.json')
|
|
1660
1660
|
.describe('故事板文件路径'),
|
|
1661
1661
|
skipConsistencyCheck: zod_1.z
|
|
1662
1662
|
.boolean()
|
|
@@ -1691,7 +1691,7 @@ server.registerTool('generate-video', {
|
|
|
1691
1691
|
.describe('The image file name of the start frame.'),
|
|
1692
1692
|
duration: zod_1.z
|
|
1693
1693
|
.number()
|
|
1694
|
-
.min(
|
|
1694
|
+
.min(1)
|
|
1695
1695
|
.max(23)
|
|
1696
1696
|
.describe('The duration of the video. 一般与 tts 配音音频时长向上取整秒(ceil)一致,或者与 timeline_analysis 中确定的歌曲片段时长一致'),
|
|
1697
1697
|
end_frame: zod_1.z
|
|
@@ -1718,7 +1718,7 @@ server.registerTool('generate-video', {
|
|
|
1718
1718
|
.default(false)
|
|
1719
1719
|
.describe('Whether to optimize the prompt.'),
|
|
1720
1720
|
},
|
|
1721
|
-
}, async ({ prompt, sceneIndex, storyBoardFile = '
|
|
1721
|
+
}, async ({ prompt, sceneIndex, storyBoardFile = 'storyboard.json', skipConsistencyCheck = false, saveToFileName, start_frame, end_frame, duration, watermark, resolution, type, optimizePrompt, saveLastFrameAs, }, context) => {
|
|
1722
1722
|
try {
|
|
1723
1723
|
// 验证session状态
|
|
1724
1724
|
const currentSession = await validateSession('generate-video');
|
|
@@ -1739,7 +1739,7 @@ server.registerTool('generate-video', {
|
|
|
1739
1739
|
console.warn(`zero 模型的视频仅支持 1080p 分辨率,用户指定的分辨率为 %s,已自动将 ${resolution} 转换为 1080p`, resolution);
|
|
1740
1740
|
resolution = '1080p';
|
|
1741
1741
|
}
|
|
1742
|
-
// 校验 prompt 与
|
|
1742
|
+
// 校验 prompt 与 storyboard.json 中场景设定的一致性以及视频时长与 timeline_analysis.json 中 proposed_video_scenes 的匹配
|
|
1743
1743
|
if (sceneIndex && !skipConsistencyCheck) {
|
|
1744
1744
|
try {
|
|
1745
1745
|
const storyBoardPath = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), projectLocalDir, storyBoardFile);
|
|
@@ -1758,7 +1758,7 @@ server.registerTool('generate-video', {
|
|
|
1758
1758
|
if (scene) {
|
|
1759
1759
|
const videoPrompt = scene.video_prompt;
|
|
1760
1760
|
if (videoPrompt && prompt !== videoPrompt) {
|
|
1761
|
-
return createErrorResponse('视频提示词必须严格遵照
|
|
1761
|
+
return createErrorResponse('视频提示词必须严格遵照storyboard的设定,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-video');
|
|
1762
1762
|
}
|
|
1763
1763
|
if (scene.is_continuous && !end_frame) {
|
|
1764
1764
|
return createErrorResponse('连续场景必须指定end_frame参数,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-video');
|
|
@@ -1771,7 +1771,7 @@ server.registerTool('generate-video', {
|
|
|
1771
1771
|
}
|
|
1772
1772
|
}
|
|
1773
1773
|
else {
|
|
1774
|
-
console.warn(`Scene index ${sceneIndex} not found in
|
|
1774
|
+
console.warn(`Scene index ${sceneIndex} not found in storyboard.json`);
|
|
1775
1775
|
}
|
|
1776
1776
|
}
|
|
1777
1777
|
}
|
|
@@ -1781,7 +1781,7 @@ server.registerTool('generate-video', {
|
|
|
1781
1781
|
}
|
|
1782
1782
|
catch (error) {
|
|
1783
1783
|
console.error('Failed to validate prompt with story board:', error);
|
|
1784
|
-
// 如果读取或解析
|
|
1784
|
+
// 如果读取或解析 storyboard.json 失败,继续执行但记录警告
|
|
1785
1785
|
}
|
|
1786
1786
|
// 校验视频时长与 timeline_analysis.json 中 proposed_video_scenes 的匹配
|
|
1787
1787
|
try {
|
|
@@ -1818,10 +1818,10 @@ server.registerTool('generate-video', {
|
|
|
1818
1818
|
if (!checkAudioVideoDurationFlag) {
|
|
1819
1819
|
checkAudioVideoDurationFlag = true;
|
|
1820
1820
|
if (scene.audio_mode === 'vo_sync') {
|
|
1821
|
-
return createErrorResponse('请先自我检查 media_logs 中的音频时长,确保
|
|
1821
|
+
return createErrorResponse('请先自我检查 media_logs 中的音频时长,确保 storyboard 中视频时长为音频时长向上取整 即 ceil(音频时长),然后再按照正确的视频时长创建视频', 'generate-video');
|
|
1822
1822
|
}
|
|
1823
1823
|
else if (scene.audio_mode === 'dialogue') {
|
|
1824
|
-
return createErrorResponse('请先自我检查 media_logs 中的音频时长,确保
|
|
1824
|
+
return createErrorResponse('请先自我检查 media_logs 中的音频时长,确保 storyboard 中视频时长**不小于**音频时长向上取整 即 ceil(音频时长),然后再按照正确的视频时长创建视频', 'generate-video');
|
|
1825
1825
|
}
|
|
1826
1826
|
}
|
|
1827
1827
|
}
|
|
@@ -1946,10 +1946,10 @@ server.registerTool('generate-video', {
|
|
|
1946
1946
|
type: 'string',
|
|
1947
1947
|
description: 'kenburns 视频 宽x高',
|
|
1948
1948
|
},
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1949
|
+
duration: {
|
|
1950
|
+
type: 'number',
|
|
1951
|
+
description: 'kenburns 视频 时长',
|
|
1952
|
+
},
|
|
1953
1953
|
},
|
|
1954
1954
|
required: ['camera_motion', 'size', 'duration'],
|
|
1955
1955
|
},
|
|
@@ -2423,7 +2423,7 @@ server.registerTool('generate-scene-tts', {
|
|
|
2423
2423
|
storyBoardFile: zod_1.z
|
|
2424
2424
|
.string()
|
|
2425
2425
|
.optional()
|
|
2426
|
-
.default('
|
|
2426
|
+
.default('storyboard.json')
|
|
2427
2427
|
.describe('故事板文件路径'),
|
|
2428
2428
|
skipConsistencyCheck: zod_1.z
|
|
2429
2429
|
.boolean()
|
|
@@ -2489,7 +2489,7 @@ server.registerTool('generate-scene-tts', {
|
|
|
2489
2489
|
const finalSpeed = speed ?? 1;
|
|
2490
2490
|
volume = volume ?? 1;
|
|
2491
2491
|
let scene = null;
|
|
2492
|
-
// 校验 text 与
|
|
2492
|
+
// 校验 text 与 storyboard.json 中场景设定的一致性
|
|
2493
2493
|
if (sceneIndex && !skipConsistencyCheck) {
|
|
2494
2494
|
try {
|
|
2495
2495
|
const storyBoardPath = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), projectLocalDir, storyBoardFile);
|
|
@@ -2524,11 +2524,11 @@ server.registerTool('generate-scene-tts', {
|
|
|
2524
2524
|
}
|
|
2525
2525
|
}
|
|
2526
2526
|
if (!isValidText) {
|
|
2527
|
-
return createErrorResponse('配音文本必须严格遵照
|
|
2527
|
+
return createErrorResponse('配音文本必须严格遵照storyboard的设定,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-scene-tts');
|
|
2528
2528
|
}
|
|
2529
2529
|
}
|
|
2530
2530
|
else {
|
|
2531
|
-
console.warn(`Scene index ${sceneIndex} not found in
|
|
2531
|
+
console.warn(`Scene index ${sceneIndex} not found in storyboard.json`);
|
|
2532
2532
|
}
|
|
2533
2533
|
}
|
|
2534
2534
|
}
|
|
@@ -2538,7 +2538,7 @@ server.registerTool('generate-scene-tts', {
|
|
|
2538
2538
|
}
|
|
2539
2539
|
catch (error) {
|
|
2540
2540
|
console.error('Failed to validate text with story board:', error);
|
|
2541
|
-
// 如果读取或解析
|
|
2541
|
+
// 如果读取或解析 storyboard.json 失败,继续执行但记录警告
|
|
2542
2542
|
}
|
|
2543
2543
|
}
|
|
2544
2544
|
console.log(`Generating TTS with voice: ${voiceID}, speed: ${finalSpeed}, text: ${text.substring(0, 100)}...`);
|
|
@@ -2592,7 +2592,7 @@ server.registerTool('generate-scene-tts', {
|
|
|
2592
2592
|
console.log('TTS generated successfully, saving to materials...');
|
|
2593
2593
|
const { url, duration, ...opts } = res;
|
|
2594
2594
|
if (!skipConsistencyCheck && duration > 16) {
|
|
2595
|
-
return createErrorResponse('TTS duration exceeds 16 seconds, 建议调整文本长度、提升语速或拆分场景...,⚠️如简化文本内容或拆分文本,需要立即更新
|
|
2595
|
+
return createErrorResponse('TTS duration exceeds 16 seconds, 建议调整文本长度、提升语速或拆分场景...,⚠️如简化文本内容或拆分文本,需要立即更新 storyboard 以保持内容同步!如仍要生成,可设置 skipConsistencyCheck 为 true,跳过一致性检查。', 'generate-scene-tts');
|
|
2596
2596
|
}
|
|
2597
2597
|
if (!duration) {
|
|
2598
2598
|
return createErrorResponse('TTS duration not returned from AI service', 'generate-scene-tts');
|
|
@@ -2678,8 +2678,8 @@ server.registerTool('compile-and-run', {
|
|
|
2678
2678
|
checkStoryboardSubtitlesFlag = true;
|
|
2679
2679
|
return createErrorResponse(`请先对 draft_content 进行以下一致性检查:
|
|
2680
2680
|
|
|
2681
|
-
1. 检查字幕文字内容是否与
|
|
2682
|
-
2. 检查视频 resolution 设定是否与
|
|
2681
|
+
1. 检查字幕文字内容是否与 storyboard 中各个场景的 script 或 dialog 内容完全一致(⚠️ 允许字幕分段展示,只要最终文本保持一致就行)
|
|
2682
|
+
2. 检查视频 resolution 设定是否与 storyboard 的 orientation 设置一致,默认 720p 情况下视频尺寸应为横屏 1280x720,竖屏 720x1280,若视频为 1080p 则尺寸应分别为横屏 1920x1080 和竖屏 1080x1920,切勿设反
|
|
2683
2683
|
3. 除非用户明确表示不要背景音乐,否则应检查是否有生成并配置了 BGM,若无,则生成 BGM 并将其加入素材和轨道配置
|
|
2684
2684
|
|
|
2685
2685
|
以上检查任何一项有问题,先修复 draft_content 使其符合要求后再进行合成`, 'compile-and-run');
|
|
@@ -2837,47 +2837,26 @@ server.registerTool('compile-and-run', {
|
|
|
2837
2837
|
return createErrorResponse(error, 'compile-and-run');
|
|
2838
2838
|
}
|
|
2839
2839
|
});
|
|
2840
|
-
server.registerTool('get-
|
|
2841
|
-
title: 'Get
|
|
2842
|
-
description: 'Get the complete
|
|
2843
|
-
inputSchema: {
|
|
2844
|
-
|
|
2840
|
+
server.registerTool('get-schema', {
|
|
2841
|
+
title: 'Get Storyboard Schema or Draft Content Schema',
|
|
2842
|
+
description: 'Get the complete Storyboard or Draft Content JSON Schema definition. Use this schema to validate storyboard.json or draft_content.json files.',
|
|
2843
|
+
inputSchema: {
|
|
2844
|
+
type: zod_1.z.enum(['storyboard', 'draft_content']),
|
|
2845
|
+
},
|
|
2846
|
+
}, async ({ type }) => {
|
|
2845
2847
|
try {
|
|
2846
|
-
const schemaPath = (0, node_path_1.resolve)(__dirname,
|
|
2848
|
+
const schemaPath = (0, node_path_1.resolve)(__dirname, `./prompts/${type}-schema.json`);
|
|
2847
2849
|
const schemaContent = await (0, promises_1.readFile)(schemaPath, 'utf-8');
|
|
2848
2850
|
const schema = JSON.parse(schemaContent);
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
type: 'text',
|
|
2853
|
-
text: JSON.stringify({
|
|
2854
|
-
success: true,
|
|
2855
|
-
schema,
|
|
2856
|
-
important_guidelines: `⚠️ 生成文件时请严格遵守输出规范,字幕文本内容必须与 story_board.json 中的 script(或dialog) 字段的文本内容完全一致。
|
|
2851
|
+
let important_guidelines = '';
|
|
2852
|
+
if (type === 'draft_content') {
|
|
2853
|
+
important_guidelines = `⚠️ 生成文件时请严格遵守输出规范,字幕文本内容必须与 storyboard.json 中的 script(或dialog) 字段的文本内容完全一致。
|
|
2857
2854
|
|
|
2858
2855
|
** 字幕优化 **
|
|
2859
|
-
* 在保证字幕文本内容与
|
|
2856
|
+
* 在保证字幕文本内容与 storyboard.json 中的 script(或dialog) 字段的文本内容完全一致的前提下,可根据 tts 返回的 \`captions.utterances\` 字段对字幕的显示进行优化,将过长的字幕分段显示,在 draft_content.json 中使用分段字幕,captions 的内容在 media_logs.json 中可查询到。
|
|
2860
2857
|
* 如用户未特殊指定,字幕样式(字体及大小)务必遵守输出规范
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
}),
|
|
2864
|
-
},
|
|
2865
|
-
],
|
|
2866
|
-
};
|
|
2867
|
-
}
|
|
2868
|
-
catch (error) {
|
|
2869
|
-
return createErrorResponse(error, 'get-video-project-schema');
|
|
2870
|
-
}
|
|
2871
|
-
});
|
|
2872
|
-
server.registerTool('get-storyboard-schema', {
|
|
2873
|
-
title: 'Get Storyboard Schema',
|
|
2874
|
-
description: 'Get the complete Storyboard JSON Schema definition.',
|
|
2875
|
-
inputSchema: {},
|
|
2876
|
-
}, async () => {
|
|
2877
|
-
try {
|
|
2878
|
-
const schemaPath = (0, node_path_1.resolve)(__dirname, '../../utils/storyboard-schema.json');
|
|
2879
|
-
const schemaContent = await (0, promises_1.readFile)(schemaPath, 'utf-8');
|
|
2880
|
-
const schema = JSON.parse(schemaContent);
|
|
2858
|
+
`;
|
|
2859
|
+
}
|
|
2881
2860
|
return {
|
|
2882
2861
|
content: [
|
|
2883
2862
|
{
|
|
@@ -2885,6 +2864,7 @@ server.registerTool('get-storyboard-schema', {
|
|
|
2885
2864
|
text: JSON.stringify({
|
|
2886
2865
|
success: true,
|
|
2887
2866
|
schema,
|
|
2867
|
+
important_guidelines,
|
|
2888
2868
|
timestamp: new Date().toISOString(),
|
|
2889
2869
|
}),
|
|
2890
2870
|
},
|
|
@@ -2892,7 +2872,7 @@ server.registerTool('get-storyboard-schema', {
|
|
|
2892
2872
|
};
|
|
2893
2873
|
}
|
|
2894
2874
|
catch (error) {
|
|
2895
|
-
return createErrorResponse(error, 'get-
|
|
2875
|
+
return createErrorResponse(error, 'get-schema');
|
|
2896
2876
|
}
|
|
2897
2877
|
});
|
|
2898
2878
|
server.registerTool('do-storyboard-optimization', {
|
|
@@ -2912,7 +2892,7 @@ server.registerTool('do-storyboard-optimization', {
|
|
|
2912
2892
|
text: JSON.stringify({
|
|
2913
2893
|
content: {
|
|
2914
2894
|
guideline: storyboardOptimizationGuidelines,
|
|
2915
|
-
action: '你应当根据guideline优化
|
|
2895
|
+
action: '你应当根据guideline优化storyboard.json',
|
|
2916
2896
|
},
|
|
2917
2897
|
}),
|
|
2918
2898
|
},
|
|
@@ -3296,180 +3276,206 @@ server.registerTool('media-analyzer', {
|
|
|
3296
3276
|
return createErrorResponse(error, 'media-analyzer');
|
|
3297
3277
|
}
|
|
3298
3278
|
});
|
|
3299
|
-
server.registerTool(
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
}
|
|
3279
|
+
// server.registerTool(
|
|
3280
|
+
// 'image-aligner',
|
|
3281
|
+
// {
|
|
3282
|
+
// title: 'Image Aligner',
|
|
3283
|
+
// description:
|
|
3284
|
+
// 'Analyze image quality and alignment with prompt using AI Image Quality Inspector.',
|
|
3285
|
+
// inputSchema: {
|
|
3286
|
+
// imageFileName: z
|
|
3287
|
+
// .string()
|
|
3288
|
+
// .describe('The image file name in materials directory to analyze.'),
|
|
3289
|
+
// sceneIndex: z.number().min(1).describe('场景索引,从1开始的下标'),
|
|
3290
|
+
// storyBoardFile: z
|
|
3291
|
+
// .string()
|
|
3292
|
+
// .optional()
|
|
3293
|
+
// .default('storyboard.json')
|
|
3294
|
+
// .describe('故事板文件路径'),
|
|
3295
|
+
// imagePrompt: z
|
|
3296
|
+
// .string()
|
|
3297
|
+
// .optional()
|
|
3298
|
+
// .describe('可选的图片提示词,如果提供则覆盖storyboard中的提示词'),
|
|
3299
|
+
// customPrompt: z
|
|
3300
|
+
// .string()
|
|
3301
|
+
// .optional()
|
|
3302
|
+
// .describe('可选的额外用户要求,用于补充图片质量评估的特定需求'),
|
|
3303
|
+
// },
|
|
3304
|
+
// },
|
|
3305
|
+
// async ({
|
|
3306
|
+
// imageFileName,
|
|
3307
|
+
// sceneIndex,
|
|
3308
|
+
// storyBoardFile = 'storyboard.json',
|
|
3309
|
+
// imagePrompt,
|
|
3310
|
+
// customPrompt,
|
|
3311
|
+
// }) => {
|
|
3312
|
+
// try {
|
|
3313
|
+
// const currentSession = await validateSession('image-aligner');
|
|
3314
|
+
// // 验证图片文件
|
|
3315
|
+
// validateImageFile(imageFileName);
|
|
3316
|
+
// // 获取图片 URL
|
|
3317
|
+
// const imageUrl = getMaterialUri(currentSession, imageFileName);
|
|
3318
|
+
// // 确定要使用的提示词
|
|
3319
|
+
// let finalPrompt = imagePrompt;
|
|
3320
|
+
// // 如果没有提供imagePrompt,则从storyboard中获取
|
|
3321
|
+
// if (!imagePrompt) {
|
|
3322
|
+
// try {
|
|
3323
|
+
// const storyBoardPath = resolve(
|
|
3324
|
+
// process.env.ZEROCUT_PROJECT_CWD || process.cwd(),
|
|
3325
|
+
// projectLocalDir,
|
|
3326
|
+
// storyBoardFile
|
|
3327
|
+
// );
|
|
3328
|
+
// if (existsSync(storyBoardPath)) {
|
|
3329
|
+
// const storyBoardContent = await readFile(storyBoardPath, 'utf8');
|
|
3330
|
+
// const storyBoard = JSON.parse(storyBoardContent);
|
|
3331
|
+
// if (storyBoard.scenes && Array.isArray(storyBoard.scenes)) {
|
|
3332
|
+
// const scene = storyBoard.scenes[sceneIndex - 1]; // sceneIndex 从1开始,数组从0开始
|
|
3333
|
+
// if (scene) {
|
|
3334
|
+
// // 根据文件名判断优先级:若end_frame存在且imageFileName包含"_end"则优先取end_frame,否则取start_frame
|
|
3335
|
+
// if (scene.end_frame && imageFileName.includes('_end')) {
|
|
3336
|
+
// finalPrompt = scene.end_frame;
|
|
3337
|
+
// } else {
|
|
3338
|
+
// finalPrompt = scene.start_frame || scene.end_frame;
|
|
3339
|
+
// }
|
|
3340
|
+
// if (!finalPrompt) {
|
|
3341
|
+
// return createErrorResponse(
|
|
3342
|
+
// `场景 ${sceneIndex} 中未找到 start_frame 或 end_frame 提示词`,
|
|
3343
|
+
// 'image-aligner'
|
|
3344
|
+
// );
|
|
3345
|
+
// }
|
|
3346
|
+
// } else {
|
|
3347
|
+
// return createErrorResponse(
|
|
3348
|
+
// `在 ${storyBoardFile} 中未找到场景索引 ${sceneIndex}`,
|
|
3349
|
+
// 'image-aligner'
|
|
3350
|
+
// );
|
|
3351
|
+
// }
|
|
3352
|
+
// } else {
|
|
3353
|
+
// return createErrorResponse(
|
|
3354
|
+
// `${storyBoardFile} 文件格式不正确,缺少 scenes 数组`,
|
|
3355
|
+
// 'image-aligner'
|
|
3356
|
+
// );
|
|
3357
|
+
// }
|
|
3358
|
+
// } else {
|
|
3359
|
+
// return createErrorResponse(
|
|
3360
|
+
// `故事板文件不存在: ${storyBoardPath}`,
|
|
3361
|
+
// 'image-aligner'
|
|
3362
|
+
// );
|
|
3363
|
+
// }
|
|
3364
|
+
// } catch (error) {
|
|
3365
|
+
// return createErrorResponse(
|
|
3366
|
+
// `读取或解析故事板文件失败: ${error}`,
|
|
3367
|
+
// 'image-aligner'
|
|
3368
|
+
// );
|
|
3369
|
+
// }
|
|
3370
|
+
// }
|
|
3371
|
+
// // 如果仍然没有提示词,返回错误
|
|
3372
|
+
// if (!finalPrompt) {
|
|
3373
|
+
// return createErrorResponse(
|
|
3374
|
+
// '未提供 imagePrompt 且无法从故事板中获取提示词',
|
|
3375
|
+
// 'image-aligner'
|
|
3376
|
+
// );
|
|
3377
|
+
// }
|
|
3378
|
+
// // 读取图片质量检查指南
|
|
3379
|
+
// const alignerGuidelinePath = resolve(
|
|
3380
|
+
// __dirname,
|
|
3381
|
+
// './prompts/reasonings/image_aligner.md'
|
|
3382
|
+
// );
|
|
3383
|
+
// let alignerGuideline = '';
|
|
3384
|
+
// try {
|
|
3385
|
+
// alignerGuideline = await readFile(alignerGuidelinePath, 'utf8');
|
|
3386
|
+
// } catch (error) {
|
|
3387
|
+
// console.warn('无法读取图片质量检查指南:', error);
|
|
3388
|
+
// alignerGuideline =
|
|
3389
|
+
// '请对图片质量进行评估,包括构图、色彩、清晰度等方面。';
|
|
3390
|
+
// }
|
|
3391
|
+
// // 构建系统提示
|
|
3392
|
+
// const systemPrompt = `你是一个专业的AI图片质量检查员。请根据以下指南对图片进行评估:
|
|
3393
|
+
// ${alignerGuideline}
|
|
3394
|
+
// 请严格按照指南中的JSON格式返回评估结果。`;
|
|
3395
|
+
// // 构建用户提示
|
|
3396
|
+
// const userPrompt = `请对这张图片进行质量评估。
|
|
3397
|
+
// 原始提示词:${finalPrompt}${
|
|
3398
|
+
// customPrompt
|
|
3399
|
+
// ? `
|
|
3400
|
+
// 额外要求:${customPrompt}`
|
|
3401
|
+
// : ''
|
|
3402
|
+
// }
|
|
3403
|
+
// 请按照指南要求,返回包含评分、问题列表和优化建议的JSON格式结果。`;
|
|
3404
|
+
// // 调用AI模型进行图片质量评估
|
|
3405
|
+
// const ai = currentSession.ai;
|
|
3406
|
+
// const completion = await ai.getCompletions({
|
|
3407
|
+
// model: 'Doubao-Seed-1.6',
|
|
3408
|
+
// messages: [
|
|
3409
|
+
// {
|
|
3410
|
+
// role: 'system',
|
|
3411
|
+
// content: systemPrompt,
|
|
3412
|
+
// },
|
|
3413
|
+
// {
|
|
3414
|
+
// role: 'user',
|
|
3415
|
+
// content: [
|
|
3416
|
+
// {
|
|
3417
|
+
// type: 'image_url',
|
|
3418
|
+
// image_url: {
|
|
3419
|
+
// url: imageUrl,
|
|
3420
|
+
// },
|
|
3421
|
+
// },
|
|
3422
|
+
// {
|
|
3423
|
+
// type: 'text',
|
|
3424
|
+
// text: userPrompt,
|
|
3425
|
+
// },
|
|
3426
|
+
// ],
|
|
3427
|
+
// },
|
|
3428
|
+
// ],
|
|
3429
|
+
// });
|
|
3430
|
+
// const result = completion.choices[0]?.message?.content;
|
|
3431
|
+
// if (!result) {
|
|
3432
|
+
// throw new Error('No response from AI model');
|
|
3433
|
+
// }
|
|
3434
|
+
// // 解析AI响应
|
|
3435
|
+
// let alignmentResult;
|
|
3436
|
+
// try {
|
|
3437
|
+
// // 尝试从响应中提取JSON
|
|
3438
|
+
// const jsonMatch =
|
|
3439
|
+
// result.match(/```json\s*([\s\S]*?)\s*```/) ||
|
|
3440
|
+
// result.match(/\{[\s\S]*\}/);
|
|
3441
|
+
// if (jsonMatch) {
|
|
3442
|
+
// alignmentResult = JSON.parse(jsonMatch[1] || jsonMatch[0]);
|
|
3443
|
+
// } else {
|
|
3444
|
+
// // 如果没有找到JSON格式,尝试直接解析整个响应
|
|
3445
|
+
// alignmentResult = JSON.parse(result);
|
|
3446
|
+
// }
|
|
3447
|
+
// } catch (error) {
|
|
3448
|
+
// // 如果解析失败,返回原始响应
|
|
3449
|
+
// alignmentResult = {
|
|
3450
|
+
// error: 'JSON解析失败',
|
|
3451
|
+
// raw_response: result,
|
|
3452
|
+
// };
|
|
3453
|
+
// }
|
|
3454
|
+
// return {
|
|
3455
|
+
// content: [
|
|
3456
|
+
// {
|
|
3457
|
+
// type: 'text',
|
|
3458
|
+
// text: JSON.stringify({
|
|
3459
|
+
// success: true,
|
|
3460
|
+
// imageFileName,
|
|
3461
|
+
// sceneIndex,
|
|
3462
|
+
// storyBoardFile,
|
|
3463
|
+
// imagePrompt: finalPrompt,
|
|
3464
|
+
// customPrompt,
|
|
3465
|
+
// promptSource: imagePrompt ? 'manual_override' : 'storyboard',
|
|
3466
|
+
// analysis: alignmentResult,
|
|
3467
|
+
// imageUrl,
|
|
3468
|
+
// nextActionSuggest:
|
|
3469
|
+
// '可根据分析结果调整提示词,修改storyboard后,重新生成图片。',
|
|
3470
|
+
// }),
|
|
3471
|
+
// },
|
|
3472
|
+
// ],
|
|
3473
|
+
// };
|
|
3474
|
+
// } catch (error) {
|
|
3475
|
+
// return createErrorResponse(error, 'image-aligner');
|
|
3476
|
+
// }
|
|
3477
|
+
// }
|
|
3478
|
+
// );
|
|
3473
3479
|
server.registerTool('audio-video-sync', {
|
|
3474
3480
|
title: 'Audio Video Sync',
|
|
3475
3481
|
description: 'Generate audio-video-synced video by matching video with audio. 还可以对口型。',
|
|
@@ -3640,7 +3646,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3640
3646
|
.describe('Array of reference image objects with name, url and type. Can be empty for text-only generation.'),
|
|
3641
3647
|
duration: zod_1.z
|
|
3642
3648
|
.number()
|
|
3643
|
-
.min(
|
|
3649
|
+
.min(1)
|
|
3644
3650
|
.max(16)
|
|
3645
3651
|
.optional()
|
|
3646
3652
|
.default(5)
|
|
@@ -3671,7 +3677,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3671
3677
|
storyBoardFile: zod_1.z
|
|
3672
3678
|
.string()
|
|
3673
3679
|
.optional()
|
|
3674
|
-
.default('
|
|
3680
|
+
.default('storyboard.json')
|
|
3675
3681
|
.describe('故事板文件路径'),
|
|
3676
3682
|
skipConsistencyCheck: zod_1.z
|
|
3677
3683
|
.boolean()
|
|
@@ -3696,17 +3702,17 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3696
3702
|
// 检查 storyboard 标志
|
|
3697
3703
|
if (!checkStoryboardFlag && (0, node_fs_1.existsSync)(storyBoardPath)) {
|
|
3698
3704
|
checkStoryboardFlag = true;
|
|
3699
|
-
return createErrorResponse(`必须先审查生成的
|
|
3705
|
+
return createErrorResponse(`必须先审查生成的 storyboard.json 内容,按照如下步骤:
|
|
3700
3706
|
|
|
3701
3707
|
1. 确保每个场景中的stage_atmosphere内容按照规则被正确融合到video_prompt中,不得遗漏
|
|
3702
3708
|
2. 如有main_characters设定且包含了reference_image,或有reference_objects,需确保video_prompt描述已包含该场景相关main_characters和所有reference_objects中的物品或背景,并确保参考图具体内容已经在video_prompt中有明确描述,如果没有,可忽略。
|
|
3703
3709
|
3. 如有配音,先自我检查 media_logs 中的查音频时长,确保以匹配音频时长来生成视频
|
|
3704
3710
|
|
|
3705
|
-
检查完上述问题后先汇报,如果有需要,应当先修改
|
|
3711
|
+
检查完上述问题后先汇报,如果有需要,应当先修改 storyboard.json 内容,然后再调用 generate-video-by-ref 生成视频。注意修改 storyboard 内容时,仅修改相应字段的字符串值,不要破坏JSON格式!
|
|
3706
3712
|
|
|
3707
3713
|
再次调用 generate-video-by-ref 时,如需要参考图,要确保referenceImages使用正确(main_characters中的reference_image作为参考人物,reference_objects中的image作为参考物品或参考背景)`, 'generate-image');
|
|
3708
3714
|
}
|
|
3709
|
-
// 校验 prompt 与
|
|
3715
|
+
// 校验 prompt 与 storyboard.json 中场景设定的一致性(如果提供了 sceneIndex)
|
|
3710
3716
|
if (!skipConsistencyCheck && sceneIndex) {
|
|
3711
3717
|
try {
|
|
3712
3718
|
if ((0, node_fs_1.existsSync)(storyBoardPath)) {
|
|
@@ -3724,7 +3730,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3724
3730
|
if (scene) {
|
|
3725
3731
|
const videoPrompt = scene.video_prompt;
|
|
3726
3732
|
if (videoPrompt && prompt !== videoPrompt) {
|
|
3727
|
-
return createErrorResponse('视频提示词必须严格遵照
|
|
3733
|
+
return createErrorResponse('视频提示词必须严格遵照storyboard的设定,如果用户明确指出不需要遵守,请将skipConsistencyCheck设置为true后再次调用', 'generate-video-by-ref');
|
|
3728
3734
|
}
|
|
3729
3735
|
// 检查 scene.is_continuous 是否为 true
|
|
3730
3736
|
if (scene.is_continuous === true) {
|
|
@@ -3791,7 +3797,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3791
3797
|
}
|
|
3792
3798
|
}
|
|
3793
3799
|
else {
|
|
3794
|
-
console.warn(`Scene index ${sceneIndex} not found in
|
|
3800
|
+
console.warn(`Scene index ${sceneIndex} not found in storyboard.json`);
|
|
3795
3801
|
}
|
|
3796
3802
|
}
|
|
3797
3803
|
}
|
|
@@ -3801,7 +3807,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3801
3807
|
}
|
|
3802
3808
|
catch (error) {
|
|
3803
3809
|
console.error('Failed to validate prompt with story board:', error);
|
|
3804
|
-
// 如果读取或解析
|
|
3810
|
+
// 如果读取或解析 storyboard.json 失败,继续执行但记录警告
|
|
3805
3811
|
}
|
|
3806
3812
|
}
|
|
3807
3813
|
const validatedFileName = validateFileName(saveToFileName);
|
|
@@ -4026,6 +4032,132 @@ server.registerTool('extend-video-duration', {
|
|
|
4026
4032
|
return createErrorResponse(error, 'extend-video');
|
|
4027
4033
|
}
|
|
4028
4034
|
});
|
|
4035
|
+
server.registerTool('generate-video-by-template', {
|
|
4036
|
+
title: 'Generate Video by Template',
|
|
4037
|
+
description: 'Generate a video based on a template. The template must be a valid JSON string.',
|
|
4038
|
+
inputSchema: {
|
|
4039
|
+
purpose: zod_1.z
|
|
4040
|
+
.string()
|
|
4041
|
+
.describe('The prompt to generate the video. 自动根据意图匹配模板'),
|
|
4042
|
+
text_prompts: zod_1.z
|
|
4043
|
+
.array(zod_1.z.string().describe('Text prompt for the template to build video.'))
|
|
4044
|
+
.optional()
|
|
4045
|
+
.describe('Optional text prompts to use in the template.'),
|
|
4046
|
+
materials: zod_1.z
|
|
4047
|
+
.array(zod_1.z.string().describe('Material file name in materials directory.'))
|
|
4048
|
+
.optional()
|
|
4049
|
+
.describe('Optional materials to use in the template.'),
|
|
4050
|
+
saveToFileName: zod_1.z
|
|
4051
|
+
.string()
|
|
4052
|
+
.describe('The filename to save the generated video.'),
|
|
4053
|
+
},
|
|
4054
|
+
}, async ({ purpose, text_prompts, saveToFileName, materials }) => {
|
|
4055
|
+
try {
|
|
4056
|
+
const templates = {
|
|
4057
|
+
'7569583728302817331': '宠物唱歌',
|
|
4058
|
+
'7569605825011367976': '万圣节宠物弹吉他',
|
|
4059
|
+
};
|
|
4060
|
+
const currentSession = await validateSession('generate-video-by-template');
|
|
4061
|
+
const validatedFileName = validateFileName(saveToFileName);
|
|
4062
|
+
const ai = currentSession.ai;
|
|
4063
|
+
let completion = await ai.getCompletions({
|
|
4064
|
+
model: 'Doubao-Seed-1.6-flash',
|
|
4065
|
+
messages: [
|
|
4066
|
+
{
|
|
4067
|
+
role: 'system',
|
|
4068
|
+
content: `你根据用户需求,从以下模板中选择一个匹配的模板,返回模板ID:\n\n${JSON.stringify(templates)}\n\n**约束**:只输出模板ID,不需要其他解释。`,
|
|
4069
|
+
},
|
|
4070
|
+
{
|
|
4071
|
+
role: 'user',
|
|
4072
|
+
content: `用户意图:${purpose}`,
|
|
4073
|
+
},
|
|
4074
|
+
],
|
|
4075
|
+
});
|
|
4076
|
+
const templateId = completion.choices[0]?.message?.content.trim();
|
|
4077
|
+
if (!templateId) {
|
|
4078
|
+
throw new Error('Failed to get template ID');
|
|
4079
|
+
}
|
|
4080
|
+
const workflowInfo = await ai.getCozeWorkflowInfo(templateId);
|
|
4081
|
+
const materialUrls = materials?.map(material => getMaterialUri(currentSession, material));
|
|
4082
|
+
const schema = {
|
|
4083
|
+
name: 'workflow_parameters',
|
|
4084
|
+
schema: {
|
|
4085
|
+
type: 'object',
|
|
4086
|
+
properties: {
|
|
4087
|
+
parameters: {
|
|
4088
|
+
type: 'object',
|
|
4089
|
+
description: 'The parameters for the workflow.',
|
|
4090
|
+
},
|
|
4091
|
+
},
|
|
4092
|
+
required: ['parameters'],
|
|
4093
|
+
},
|
|
4094
|
+
};
|
|
4095
|
+
const prompt = `你根据模板工作流输入 schema 和 prompt、materials 生成一个 JSON 字符串,作为模板工作流的参数。
|
|
4096
|
+
|
|
4097
|
+
## **工作流输入 schema**:
|
|
4098
|
+
${JSON.stringify(workflowInfo.data.workflow_detail.description)}
|
|
4099
|
+
|
|
4100
|
+
## **prompt**:
|
|
4101
|
+
${text_prompts}
|
|
4102
|
+
|
|
4103
|
+
## **materials**:
|
|
4104
|
+
${JSON.stringify(materialUrls)}`;
|
|
4105
|
+
completion = await ai.getCompletions({
|
|
4106
|
+
model: 'Doubao-Seed-1.6-flash',
|
|
4107
|
+
messages: [
|
|
4108
|
+
{
|
|
4109
|
+
role: 'system',
|
|
4110
|
+
content: prompt,
|
|
4111
|
+
},
|
|
4112
|
+
{
|
|
4113
|
+
role: 'user',
|
|
4114
|
+
content: `生成模板调用的正确parameters参数`,
|
|
4115
|
+
},
|
|
4116
|
+
],
|
|
4117
|
+
response_format: {
|
|
4118
|
+
type: 'json_schema',
|
|
4119
|
+
json_schema: schema,
|
|
4120
|
+
},
|
|
4121
|
+
});
|
|
4122
|
+
const parameters = completion.choices[0]?.message?.content.trim();
|
|
4123
|
+
if (!parameters) {
|
|
4124
|
+
throw new Error('Failed to get parameters');
|
|
4125
|
+
}
|
|
4126
|
+
console.log(parameters);
|
|
4127
|
+
const result = await ai.runCozeWorkflow(templateId, JSON.parse(parameters).parameters);
|
|
4128
|
+
if (result.url) {
|
|
4129
|
+
// 保存到项目材料目录
|
|
4130
|
+
const uri = await saveMaterial(currentSession, result.url, validatedFileName);
|
|
4131
|
+
return {
|
|
4132
|
+
content: [
|
|
4133
|
+
{
|
|
4134
|
+
type: 'text',
|
|
4135
|
+
text: JSON.stringify({
|
|
4136
|
+
success: true,
|
|
4137
|
+
parameters,
|
|
4138
|
+
uri,
|
|
4139
|
+
}),
|
|
4140
|
+
},
|
|
4141
|
+
],
|
|
4142
|
+
};
|
|
4143
|
+
}
|
|
4144
|
+
return {
|
|
4145
|
+
content: [
|
|
4146
|
+
{
|
|
4147
|
+
type: 'text',
|
|
4148
|
+
text: JSON.stringify({
|
|
4149
|
+
success: false,
|
|
4150
|
+
result,
|
|
4151
|
+
parameters,
|
|
4152
|
+
}),
|
|
4153
|
+
},
|
|
4154
|
+
],
|
|
4155
|
+
};
|
|
4156
|
+
}
|
|
4157
|
+
catch (error) {
|
|
4158
|
+
return createErrorResponse(error, 'generate-video-by-template');
|
|
4159
|
+
}
|
|
4160
|
+
});
|
|
4029
4161
|
server.registerTool('run-ffmpeg-command', {
|
|
4030
4162
|
title: 'Run FFmpeg Command',
|
|
4031
4163
|
description: 'Run ffmpeg or ffprobe commands in the sandbox environment. Only ffmpeg and ffprobe commands are allowed. The default working directory is the materials directory.',
|
|
@@ -4269,182 +4401,6 @@ server.registerTool('build-capcat-draft', {
|
|
|
4269
4401
|
return createErrorResponse(error, 'build-capcat-draft');
|
|
4270
4402
|
}
|
|
4271
4403
|
});
|
|
4272
|
-
// server.registerTool(
|
|
4273
|
-
// 'custom-edit-draft',
|
|
4274
|
-
// {
|
|
4275
|
-
// title: 'Custom Edit Draft',
|
|
4276
|
-
// description:
|
|
4277
|
-
// 'Launch timeline editor server for custom draft editing. Starts a local server at specified port with draft file configuration.',
|
|
4278
|
-
// inputSchema: {
|
|
4279
|
-
// draftContentFileName: z
|
|
4280
|
-
// .string()
|
|
4281
|
-
// .optional()
|
|
4282
|
-
// .default('draft_content.json')
|
|
4283
|
-
// .describe('Draft content filename (default: draft_content.json)'),
|
|
4284
|
-
// port: z
|
|
4285
|
-
// .number()
|
|
4286
|
-
// .optional()
|
|
4287
|
-
// .default(6789)
|
|
4288
|
-
// .describe('Server port (default: 6789)'),
|
|
4289
|
-
// },
|
|
4290
|
-
// },
|
|
4291
|
-
// async ({ draftContentFileName = 'draft_content.json', port = 6789 }) => {
|
|
4292
|
-
// try {
|
|
4293
|
-
// // 使用项目本地目录
|
|
4294
|
-
// const workDir = resolve(
|
|
4295
|
-
// process.env.ZEROCUT_PROJECT_CWD || process.cwd(),
|
|
4296
|
-
// projectLocalDir
|
|
4297
|
-
// );
|
|
4298
|
-
// // 检查 draft_content.json 文件是否存在
|
|
4299
|
-
// const draftFilePath = resolve(workDir, draftContentFileName);
|
|
4300
|
-
// if (!existsSync(draftFilePath)) {
|
|
4301
|
-
// return createErrorResponse(
|
|
4302
|
-
// new Error(
|
|
4303
|
-
// `Draft content file not found: ${draftContentFileName}. Please generate draft content first using the generate-draft-content tool.`
|
|
4304
|
-
// ),
|
|
4305
|
-
// 'custom-edit-draft'
|
|
4306
|
-
// );
|
|
4307
|
-
// }
|
|
4308
|
-
// // 备份原始的 draft_content.json 文件
|
|
4309
|
-
// const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
4310
|
-
// const backupFileName = `${draftContentFileName}.backup.${timestamp}`;
|
|
4311
|
-
// const backupFilePath = resolve(workDir, backupFileName);
|
|
4312
|
-
// try {
|
|
4313
|
-
// const { copyFileSync } = await import('fs');
|
|
4314
|
-
// copyFileSync(draftFilePath, backupFilePath);
|
|
4315
|
-
// console.log(`Draft file backed up to: ${backupFileName}`);
|
|
4316
|
-
// } catch (backupError) {
|
|
4317
|
-
// console.warn(`Failed to backup draft file: ${backupError}`);
|
|
4318
|
-
// // 继续执行,不因备份失败而中断
|
|
4319
|
-
// }
|
|
4320
|
-
// // 构建启动命令参数
|
|
4321
|
-
// const timelineEditorPath = resolve(
|
|
4322
|
-
// __dirname,
|
|
4323
|
-
// '../../../dist/timeline-editor/index.js'
|
|
4324
|
-
// );
|
|
4325
|
-
// console.log(`Starting timeline editor server...`);
|
|
4326
|
-
// console.log(`Working directory: ${workDir}`);
|
|
4327
|
-
// console.log(`Draft file: ${draftContentFileName}`);
|
|
4328
|
-
// console.log(`Server port: ${port}`);
|
|
4329
|
-
// console.log(`Timeline editor path: ${timelineEditorPath}`);
|
|
4330
|
-
// // 检查端口是否被占用,如果被占用则停止占用的进程
|
|
4331
|
-
// if (await isPortInUse(port)) {
|
|
4332
|
-
// console.log(`Port ${port} is in use, killing existing processes...`);
|
|
4333
|
-
// await killPortProcess(port);
|
|
4334
|
-
// }
|
|
4335
|
-
// // 直接启动服务器进程
|
|
4336
|
-
// const platform = os.platform();
|
|
4337
|
-
// const nodeCommand = platform === 'win32' ? 'node.exe' : 'node';
|
|
4338
|
-
// const serverProcess = spawn(
|
|
4339
|
-
// nodeCommand,
|
|
4340
|
-
// [
|
|
4341
|
-
// timelineEditorPath,
|
|
4342
|
-
// '--draft-file',
|
|
4343
|
-
// draftContentFileName,
|
|
4344
|
-
// '--project-dir',
|
|
4345
|
-
// workDir,
|
|
4346
|
-
// '--port',
|
|
4347
|
-
// port.toString(),
|
|
4348
|
-
// ],
|
|
4349
|
-
// {
|
|
4350
|
-
// cwd: workDir,
|
|
4351
|
-
// stdio: 'pipe',
|
|
4352
|
-
// shell: platform === 'win32', // 在 Windows 下使用 shell
|
|
4353
|
-
// }
|
|
4354
|
-
// );
|
|
4355
|
-
// const serverUrl = `http://localhost:${port}`;
|
|
4356
|
-
// // 等待服务器启动 - 使用健康检查 API 轮询
|
|
4357
|
-
// await new Promise((resolve, reject) => {
|
|
4358
|
-
// const timeout = setTimeout(() => {
|
|
4359
|
-
// reject(new Error('Server startup timeout'));
|
|
4360
|
-
// }, 30000); // 增加超时时间到30秒
|
|
4361
|
-
// const checkHealth = async () => {
|
|
4362
|
-
// try {
|
|
4363
|
-
// const healthUrl = `${serverUrl}/health`;
|
|
4364
|
-
// const req = http.get(healthUrl, (res: any) => {
|
|
4365
|
-
// let data = '';
|
|
4366
|
-
// res.on('data', (chunk: any) => {
|
|
4367
|
-
// data += chunk;
|
|
4368
|
-
// });
|
|
4369
|
-
// res.on('end', () => {
|
|
4370
|
-
// try {
|
|
4371
|
-
// const healthData = JSON.parse(data);
|
|
4372
|
-
// if (healthData.status === 'ok') {
|
|
4373
|
-
// clearTimeout(timeout);
|
|
4374
|
-
// console.log('Server health check passed');
|
|
4375
|
-
// resolve(void 0);
|
|
4376
|
-
// } else {
|
|
4377
|
-
// // 继续轮询
|
|
4378
|
-
// setTimeout(checkHealth, 1000);
|
|
4379
|
-
// }
|
|
4380
|
-
// } catch (parseError) {
|
|
4381
|
-
// // 继续轮询
|
|
4382
|
-
// setTimeout(checkHealth, 1000);
|
|
4383
|
-
// }
|
|
4384
|
-
// });
|
|
4385
|
-
// });
|
|
4386
|
-
// req.on('error', () => {
|
|
4387
|
-
// // 服务器还未启动,继续轮询
|
|
4388
|
-
// setTimeout(checkHealth, 1000);
|
|
4389
|
-
// });
|
|
4390
|
-
// req.setTimeout(2000, () => {
|
|
4391
|
-
// req.destroy();
|
|
4392
|
-
// // 继续轮询
|
|
4393
|
-
// setTimeout(checkHealth, 1000);
|
|
4394
|
-
// });
|
|
4395
|
-
// } catch (error) {
|
|
4396
|
-
// // 继续轮询
|
|
4397
|
-
// setTimeout(checkHealth, 1000);
|
|
4398
|
-
// }
|
|
4399
|
-
// };
|
|
4400
|
-
// serverProcess.stdout?.on('data', (data: any) => {
|
|
4401
|
-
// console.log('Server output:', data.toString());
|
|
4402
|
-
// });
|
|
4403
|
-
// serverProcess.stderr?.on('data', (data: any) => {
|
|
4404
|
-
// console.error('Server error:', data.toString());
|
|
4405
|
-
// });
|
|
4406
|
-
// serverProcess.on('error', (error: any) => {
|
|
4407
|
-
// clearTimeout(timeout);
|
|
4408
|
-
// reject(error);
|
|
4409
|
-
// });
|
|
4410
|
-
// // 开始健康检查轮询
|
|
4411
|
-
// setTimeout(checkHealth, 2000); // 2秒后开始第一次检查
|
|
4412
|
-
// });
|
|
4413
|
-
// return {
|
|
4414
|
-
// content: [
|
|
4415
|
-
// {
|
|
4416
|
-
// type: 'text',
|
|
4417
|
-
// text: JSON.stringify(
|
|
4418
|
-
// {
|
|
4419
|
-
// success: true,
|
|
4420
|
-
// message: 'Timeline editor server started successfully',
|
|
4421
|
-
// serverUrl: serverUrl,
|
|
4422
|
-
// draftFile: draftContentFileName,
|
|
4423
|
-
// backupFile: backupFileName,
|
|
4424
|
-
// projectDirectory: workDir,
|
|
4425
|
-
// port: port,
|
|
4426
|
-
// pid: serverProcess.pid,
|
|
4427
|
-
// instructions: [
|
|
4428
|
-
// `Timeline editor is now running at: ${serverUrl}`,
|
|
4429
|
-
// 'Open the URL in your browser to edit the draft',
|
|
4430
|
-
// 'The server will automatically save changes to the draft file',
|
|
4431
|
-
// `Original draft file has been backed up as: ${backupFileName}`,
|
|
4432
|
-
// `Server process ID: ${serverProcess.pid}`,
|
|
4433
|
-
// 'The server will continue running in the background',
|
|
4434
|
-
// ],
|
|
4435
|
-
// },
|
|
4436
|
-
// null,
|
|
4437
|
-
// 2
|
|
4438
|
-
// ),
|
|
4439
|
-
// },
|
|
4440
|
-
// ],
|
|
4441
|
-
// };
|
|
4442
|
-
// } catch (error) {
|
|
4443
|
-
// console.error('Error starting timeline editor:', error);
|
|
4444
|
-
// return createErrorResponse(error, 'custom-edit-draft');
|
|
4445
|
-
// }
|
|
4446
|
-
// }
|
|
4447
|
-
// );
|
|
4448
4404
|
async function run() {
|
|
4449
4405
|
// Start receiving messages on stdin and sending messages on stdout
|
|
4450
4406
|
const transport = new stdio_js_1.StdioServerTransport();
|