cerevox 3.0.0-alpha.9 → 3.0.0-beta.2
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 -25
- package/dist/core/ai.d.ts.map +1 -1
- package/dist/core/ai.js +129 -17
- package/dist/core/ai.js.map +1 -1
- package/dist/mcp/servers/prompts/rules/anim.md +72 -0
- package/dist/mcp/servers/zerocut.d.ts.map +1 -1
- package/dist/mcp/servers/zerocut.js +227 -229
- package/dist/mcp/servers/zerocut.js.map +1 -1
- package/dist/utils/coze.d.ts +8 -0
- package/dist/utils/coze.d.ts.map +1 -1
- package/dist/utils/coze.js +20 -0
- package/dist/utils/coze.js.map +1 -1
- package/package.json +1 -1
- package/dist/mcp/servers/prompts/rules/music-video.md +0 -164
- package/dist/mcp/servers/prompts/rules/stage-play.md +0 -269
|
@@ -74,7 +74,7 @@ function createErrorResponse(error, operation, details) {
|
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
// Session 状态检查
|
|
77
|
-
async function validateSession(operation) {
|
|
77
|
+
async function validateSession(operation, checkRules = true) {
|
|
78
78
|
if (closeSessionTimerId) {
|
|
79
79
|
clearTimeout(closeSessionTimerId);
|
|
80
80
|
closeSessionTimerId = null;
|
|
@@ -84,7 +84,7 @@ async function validateSession(operation) {
|
|
|
84
84
|
throw new Error(`Session not initialized. Please call 'project-open' first before using ${operation}.`);
|
|
85
85
|
}
|
|
86
86
|
const projectRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `project_rules.md`);
|
|
87
|
-
if (!(0, node_fs_1.existsSync)(projectRulesFile)) {
|
|
87
|
+
if (checkRules && !(0, node_fs_1.existsSync)(projectRulesFile)) {
|
|
88
88
|
throw new Error(`Project rules file not found: ${projectRulesFile}. Please call 'retrieve-rules-context' first.`);
|
|
89
89
|
}
|
|
90
90
|
return session;
|
|
@@ -353,131 +353,6 @@ server.registerPrompt('zerocut-guideline', {
|
|
|
353
353
|
throw new Error(`Failed to load zerocut-guideline prompt: ${error}`);
|
|
354
354
|
}
|
|
355
355
|
});
|
|
356
|
-
server.registerTool('retrieve-rules-context', {
|
|
357
|
-
title: 'ZeroCut Basic Rules',
|
|
358
|
-
description: `ZeroCut-${constants_1.VERSION} Basic Rules`,
|
|
359
|
-
inputSchema: {
|
|
360
|
-
purpose: zod_1.z
|
|
361
|
-
.enum([
|
|
362
|
-
'general-video',
|
|
363
|
-
'music-video',
|
|
364
|
-
'stage-play',
|
|
365
|
-
'story-telling',
|
|
366
|
-
'creative-ad',
|
|
367
|
-
'expert',
|
|
368
|
-
'material-creation',
|
|
369
|
-
'freeform',
|
|
370
|
-
'custom',
|
|
371
|
-
])
|
|
372
|
-
.default('general-video')
|
|
373
|
-
.describe(`The purpose of the rules context to retrieve.
|
|
374
|
-
|
|
375
|
-
- general-video 创建通用视频
|
|
376
|
-
- music-video 创建音乐视频
|
|
377
|
-
- stage-play 创建舞台播放视频
|
|
378
|
-
- story-telling 创建故事讲述视频
|
|
379
|
-
- creative-ad 创建创意广告视频
|
|
380
|
-
- expert 以专家模式创建视频,必须用户主动要求才触发
|
|
381
|
-
- material-creation 素材创作模式,必须用户主动要求才触发
|
|
382
|
-
- freeform 自由创作模式,必须用户主动要求才触发
|
|
383
|
-
- custom 自定义模式`),
|
|
384
|
-
},
|
|
385
|
-
}, async ({ purpose }) => {
|
|
386
|
-
const projectRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `project_rules.md`);
|
|
387
|
-
const customRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `custom_rules.md`);
|
|
388
|
-
let promptContent = '';
|
|
389
|
-
if ((0, node_fs_1.existsSync)(projectRulesFile)) {
|
|
390
|
-
promptContent = await (0, promises_1.readFile)(projectRulesFile, 'utf-8');
|
|
391
|
-
if ((0, node_fs_1.existsSync)(customRulesFile)) {
|
|
392
|
-
promptContent +=
|
|
393
|
-
'\n\n---\n\n' + (await (0, promises_1.readFile)(customRulesFile, 'utf-8'));
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
// 当 projectRulesFile 不存在时,设置 checkStoryboardFlag 为 false
|
|
398
|
-
checkStoryboardFlag = false;
|
|
399
|
-
// 当 projectRulesFile 不存在时,设置 checkAudioVideoDurationFlag 为 false
|
|
400
|
-
checkAudioVideoDurationFlag = false;
|
|
401
|
-
// 当 projectRulesFile 不存在时,设置 checkStoryboardSubtitlesFlag 为 false
|
|
402
|
-
checkStoryboardSubtitlesFlag = false;
|
|
403
|
-
if (purpose !== 'general-video' &&
|
|
404
|
-
purpose !== 'music-video' &&
|
|
405
|
-
purpose !== 'stage-play' &&
|
|
406
|
-
purpose !== 'story-telling' &&
|
|
407
|
-
purpose !== 'creative-ad' &&
|
|
408
|
-
purpose !== 'expert' &&
|
|
409
|
-
purpose !== 'material-creation' &&
|
|
410
|
-
purpose !== 'freeform') {
|
|
411
|
-
return createErrorResponse(`Project rules file not found: ${projectRulesFile}`, 'retrieve-rules-context');
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
try {
|
|
415
|
-
if (!promptContent) {
|
|
416
|
-
const promptPath = (0, node_path_1.resolve)(__dirname, `./prompts/rules/${purpose}.md`);
|
|
417
|
-
promptContent = await (0, promises_1.readFile)(promptPath, 'utf-8');
|
|
418
|
-
// 确保目录存在
|
|
419
|
-
await (0, promises_1.mkdir)((0, node_path_1.dirname)(projectRulesFile), { recursive: true });
|
|
420
|
-
await (0, promises_1.writeFile)(projectRulesFile, promptContent);
|
|
421
|
-
}
|
|
422
|
-
// 如果 purpose 为 freeform,复制 skills 和 knowledge 目录
|
|
423
|
-
if (purpose === 'freeform') {
|
|
424
|
-
// 递归复制目录的通用函数
|
|
425
|
-
const copyDirectory = async (src, dest) => {
|
|
426
|
-
const entries = await (0, promises_1.readdir)(src, { withFileTypes: true });
|
|
427
|
-
for (const entry of entries) {
|
|
428
|
-
const srcPath = (0, node_path_1.resolve)(src, entry.name);
|
|
429
|
-
const destPath = (0, node_path_1.resolve)(dest, entry.name);
|
|
430
|
-
if (entry.isDirectory()) {
|
|
431
|
-
await (0, promises_1.mkdir)(destPath, { recursive: true });
|
|
432
|
-
await copyDirectory(srcPath, destPath);
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
const content = await (0, promises_1.readFile)(srcPath, 'utf-8');
|
|
436
|
-
await (0, promises_1.writeFile)(destPath, content);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
// 复制 skills 目录
|
|
441
|
-
const sourceSkillsDir = (0, node_path_1.resolve)(__dirname, './prompts/skills');
|
|
442
|
-
const targetSkillsDir = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'skills');
|
|
443
|
-
try {
|
|
444
|
-
await (0, promises_1.mkdir)(targetSkillsDir, { recursive: true });
|
|
445
|
-
await copyDirectory(sourceSkillsDir, targetSkillsDir);
|
|
446
|
-
console.log(`Skills directory copied to ${targetSkillsDir}`);
|
|
447
|
-
}
|
|
448
|
-
catch (skillsError) {
|
|
449
|
-
console.warn(`Failed to copy skills directory: ${skillsError}`);
|
|
450
|
-
}
|
|
451
|
-
// 复制 knowledge 目录
|
|
452
|
-
const sourceKnowledgeDir = (0, node_path_1.resolve)(__dirname, './prompts/knowledge');
|
|
453
|
-
const targetKnowledgeDir = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'knowledge');
|
|
454
|
-
try {
|
|
455
|
-
await (0, promises_1.mkdir)(targetKnowledgeDir, { recursive: true });
|
|
456
|
-
await copyDirectory(sourceKnowledgeDir, targetKnowledgeDir);
|
|
457
|
-
console.log(`Knowledge directory copied to ${targetKnowledgeDir}`);
|
|
458
|
-
}
|
|
459
|
-
catch (knowledgeError) {
|
|
460
|
-
console.warn(`Failed to copy knowledge directory: ${knowledgeError}`);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
return {
|
|
464
|
-
content: [
|
|
465
|
-
{
|
|
466
|
-
type: 'text',
|
|
467
|
-
text: JSON.stringify({
|
|
468
|
-
type: 'project_rules',
|
|
469
|
-
content: promptContent,
|
|
470
|
-
projectRulesFile,
|
|
471
|
-
}),
|
|
472
|
-
},
|
|
473
|
-
],
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
catch (error) {
|
|
477
|
-
console.error(`Failed to load rules context prompt for ${purpose}:`, error);
|
|
478
|
-
return createErrorResponse(`Failed to load rules context prompt for ${purpose}: ${error}`, 'retrieve-rules-context');
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
356
|
server.registerTool('project-open', {
|
|
482
357
|
title: 'Open Project',
|
|
483
358
|
description: 'Launch a new Cerevox session with a Chromium browser instance and open a new project context. Supports smart file filtering to optimize upload performance.',
|
|
@@ -673,6 +548,121 @@ server.registerTool('project-close', {
|
|
|
673
548
|
return createErrorResponse(error, 'project-close');
|
|
674
549
|
}
|
|
675
550
|
});
|
|
551
|
+
server.registerTool('retrieve-rules-context', {
|
|
552
|
+
title: 'ZeroCut Basic Rules',
|
|
553
|
+
description: `ZeroCut-${constants_1.VERSION} Basic Rules`,
|
|
554
|
+
inputSchema: {
|
|
555
|
+
request: zod_1.z.string().describe(`根据用户的具体的指令召回规则上下文,具体指令为通常为如下格式之一:
|
|
556
|
+
|
|
557
|
+
- 启用xxx模式
|
|
558
|
+
- 使用xxx助手
|
|
559
|
+
- 启用xxx角色
|
|
560
|
+
- 切换上下文为xxx
|
|
561
|
+
- 召回原规则上下文(配合type:memo使用)
|
|
562
|
+
`),
|
|
563
|
+
type: zod_1.z
|
|
564
|
+
.enum(['new', 'memo', 'switch'])
|
|
565
|
+
.describe('new: 新创建规则上下文\nmemo: 根据需要重新召回规则上下文(记忆)\nswitch: 根据用户指令,切换规则上下文'),
|
|
566
|
+
},
|
|
567
|
+
}, async ({ request, type }) => {
|
|
568
|
+
const currentSession = await validateSession('retrieve-rules-context', false);
|
|
569
|
+
const projectRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `project_rules.md`);
|
|
570
|
+
const customRulesFile = (0, node_path_1.resolve)(projectLocalDir, '.trae', 'rules', `custom_rules.md`);
|
|
571
|
+
let promptContent = '';
|
|
572
|
+
if ((0, node_fs_1.existsSync)(projectRulesFile)) {
|
|
573
|
+
promptContent = await (0, promises_1.readFile)(projectRulesFile, 'utf-8');
|
|
574
|
+
if ((0, node_fs_1.existsSync)(customRulesFile)) {
|
|
575
|
+
promptContent +=
|
|
576
|
+
'\n\n---\n\n' + (await (0, promises_1.readFile)(customRulesFile, 'utf-8'));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
// 当 projectRulesFile 不存在时,设置 checkStoryboardFlag 为 false
|
|
581
|
+
checkStoryboardFlag = false;
|
|
582
|
+
// 当 projectRulesFile 不存在时,设置 checkAudioVideoDurationFlag 为 false
|
|
583
|
+
checkAudioVideoDurationFlag = false;
|
|
584
|
+
// 当 projectRulesFile 不存在时,设置 checkStoryboardSubtitlesFlag 为 false
|
|
585
|
+
checkStoryboardSubtitlesFlag = false;
|
|
586
|
+
}
|
|
587
|
+
if (type === 'switch') {
|
|
588
|
+
(0, node_fs_1.rmSync)(projectRulesFile, { force: true });
|
|
589
|
+
(0, node_fs_1.rmSync)(customRulesFile, { force: true });
|
|
590
|
+
promptContent = '';
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
if (!promptContent) {
|
|
594
|
+
const ai = currentSession.ai;
|
|
595
|
+
const { data: rules } = await ai.listContextRules();
|
|
596
|
+
const rulesList = rules.map((rule) => ({
|
|
597
|
+
name: rule.name,
|
|
598
|
+
trigger: rule.trigger,
|
|
599
|
+
}));
|
|
600
|
+
const chooseRulePrompt = `
|
|
601
|
+
请分析用户具体指令,匹配可用规则中的trigger,选择最合适的规则。
|
|
602
|
+
|
|
603
|
+
## 可用规则:
|
|
604
|
+
${JSON.stringify(rulesList, null, 2)}
|
|
605
|
+
|
|
606
|
+
请返回一个 JSON 字符串,包含用户意图对应的规则名称。格式为:{"rule_name": "规则名称"}
|
|
607
|
+
`;
|
|
608
|
+
const schema = {
|
|
609
|
+
name: 'choose_rule',
|
|
610
|
+
schema: {
|
|
611
|
+
type: 'object',
|
|
612
|
+
properties: {
|
|
613
|
+
rule_name: {
|
|
614
|
+
type: 'string',
|
|
615
|
+
description: '用户意图对应的规则名称',
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
required: ['rule_name'],
|
|
619
|
+
},
|
|
620
|
+
};
|
|
621
|
+
const completion = await ai.getCompletions({
|
|
622
|
+
model: 'Doubao-Seed-1.6-flash',
|
|
623
|
+
messages: [
|
|
624
|
+
{
|
|
625
|
+
role: 'system',
|
|
626
|
+
content: chooseRulePrompt,
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
role: 'user',
|
|
630
|
+
content: request,
|
|
631
|
+
},
|
|
632
|
+
],
|
|
633
|
+
response_format: {
|
|
634
|
+
type: 'json_schema',
|
|
635
|
+
json_schema: schema,
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
const ruleName = JSON.parse(completion.choices[0].message.content).rule_name;
|
|
639
|
+
let rule = rules.find((rule) => rule.name === ruleName);
|
|
640
|
+
if (!rule) {
|
|
641
|
+
rule = rules.find((rule) => rule.name === '通用视频助手');
|
|
642
|
+
}
|
|
643
|
+
promptContent = rule.prompt;
|
|
644
|
+
// 确保目录存在
|
|
645
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(projectRulesFile), { recursive: true });
|
|
646
|
+
await (0, promises_1.writeFile)(projectRulesFile, promptContent);
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
content: [
|
|
650
|
+
{
|
|
651
|
+
type: 'text',
|
|
652
|
+
text: JSON.stringify({
|
|
653
|
+
type: 'project_rules',
|
|
654
|
+
content: promptContent,
|
|
655
|
+
projectRulesFile,
|
|
656
|
+
}),
|
|
657
|
+
},
|
|
658
|
+
],
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
catch (error) {
|
|
662
|
+
console.error(`Failed to load rules context prompt for ${prompt}:`, error);
|
|
663
|
+
return createErrorResponse(`Failed to load rules context prompt for ${prompt}: ${error}`, 'retrieve-rules-context');
|
|
664
|
+
}
|
|
665
|
+
});
|
|
676
666
|
// 列出项目下的所有文件
|
|
677
667
|
server.registerTool('list-project-files', {
|
|
678
668
|
title: 'List Project Files',
|
|
@@ -2810,34 +2800,6 @@ server.registerTool('get-schema', {
|
|
|
2810
2800
|
return createErrorResponse(error, 'get-schema');
|
|
2811
2801
|
}
|
|
2812
2802
|
});
|
|
2813
|
-
server.registerTool('do-storyboard-optimization', {
|
|
2814
|
-
title: 'Do Storyboard Optimization',
|
|
2815
|
-
description: 'Get storyboard optimization guidelines and action instructions.',
|
|
2816
|
-
inputSchema: {},
|
|
2817
|
-
}, async () => {
|
|
2818
|
-
try {
|
|
2819
|
-
// 调用 do-storyboard-optimization 工具时,设置 checkStoryboardFlag 为 true
|
|
2820
|
-
checkStoryboardFlag = true;
|
|
2821
|
-
const guidelinePath = (0, node_path_1.resolve)(__dirname, './prompts/actions/storyboard_optimization.md');
|
|
2822
|
-
const storyboardOptimizationGuidelines = await (0, promises_1.readFile)(guidelinePath, 'utf-8');
|
|
2823
|
-
return {
|
|
2824
|
-
content: [
|
|
2825
|
-
{
|
|
2826
|
-
type: 'text',
|
|
2827
|
-
text: JSON.stringify({
|
|
2828
|
-
content: {
|
|
2829
|
-
guideline: storyboardOptimizationGuidelines,
|
|
2830
|
-
action: '你应当根据guideline优化storyboard.json',
|
|
2831
|
-
},
|
|
2832
|
-
}),
|
|
2833
|
-
},
|
|
2834
|
-
],
|
|
2835
|
-
};
|
|
2836
|
-
}
|
|
2837
|
-
catch (error) {
|
|
2838
|
-
return createErrorResponse(error, 'do-storyboard-optimization');
|
|
2839
|
-
}
|
|
2840
|
-
});
|
|
2841
2803
|
server.registerTool('search-voices', {
|
|
2842
2804
|
title: 'Search Voices',
|
|
2843
2805
|
description: 'Search voices from doubao_voices_full based on scenes, emotions, languages, and gender.',
|
|
@@ -3416,7 +3378,11 @@ server.registerTool('audio-video-sync', {
|
|
|
3416
3378
|
description: 'Generate audio-video-synced video by matching video with audio. 还可以对口型。',
|
|
3417
3379
|
inputSchema: {
|
|
3418
3380
|
lipSync: zod_1.z.boolean().default(false),
|
|
3419
|
-
lipSyncType: zod_1.z
|
|
3381
|
+
lipSyncType: zod_1.z
|
|
3382
|
+
.enum(['pixv', 'vidu', 'basic', 'lite'])
|
|
3383
|
+
.optional()
|
|
3384
|
+
.default('pixv'),
|
|
3385
|
+
lipSyncPadAudio: zod_1.z.boolean().optional().default(true),
|
|
3420
3386
|
videoFileName: zod_1.z
|
|
3421
3387
|
.string()
|
|
3422
3388
|
.describe('The video file name in materials directory.'),
|
|
@@ -3435,7 +3401,7 @@ server.registerTool('audio-video-sync', {
|
|
|
3435
3401
|
.string()
|
|
3436
3402
|
.describe('The filename to save the audio-video-synced video.'),
|
|
3437
3403
|
},
|
|
3438
|
-
}, async ({ lipSync, lipSyncType, videoFileName, audioFileName, audioInMs, refPhotoFileName, saveToFileName, }, context) => {
|
|
3404
|
+
}, async ({ lipSync, lipSyncType, lipSyncPadAudio, videoFileName, audioFileName, audioInMs, refPhotoFileName, saveToFileName, }, context) => {
|
|
3439
3405
|
try {
|
|
3440
3406
|
// 验证session状态
|
|
3441
3407
|
const currentSession = await validateSession('audio-video-sync');
|
|
@@ -3507,6 +3473,7 @@ server.registerTool('audio-video-sync', {
|
|
|
3507
3473
|
audioUrl,
|
|
3508
3474
|
audioInMs,
|
|
3509
3475
|
ref_photo_url: refPhotoUrl,
|
|
3476
|
+
pad_audio: lipSyncPadAudio,
|
|
3510
3477
|
onProgress: async (metaData) => {
|
|
3511
3478
|
console.log('Lip sync progress:', metaData);
|
|
3512
3479
|
try {
|
|
@@ -3561,7 +3528,7 @@ server.registerTool('audio-video-sync', {
|
|
|
3561
3528
|
});
|
|
3562
3529
|
server.registerTool('generate-video-by-ref', {
|
|
3563
3530
|
title: 'Generate Video by Reference Images',
|
|
3564
|
-
description: 'Generate video using reference images. Supports sora2, veo3.1, veo3.1-pro (1 image max), lite and pro (4 images max), vidu (7 images max). Can work without reference images (0 images).',
|
|
3531
|
+
description: 'Generate video using reference images. Supports sora2, sora2-pro (1 image max), veo3.1, veo3.1-pro (1 image max), lite and pro (4 images max), vidu (7 images max). Can work without reference images (0 images).',
|
|
3565
3532
|
inputSchema: {
|
|
3566
3533
|
prompt: zod_1.z
|
|
3567
3534
|
.string()
|
|
@@ -3591,21 +3558,34 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3591
3558
|
.optional()
|
|
3592
3559
|
.default(5)
|
|
3593
3560
|
.describe('The duration of the video in seconds.'),
|
|
3594
|
-
|
|
3561
|
+
aspectRatio: zod_1.z
|
|
3562
|
+
.enum(['16:9', '9:16'])
|
|
3563
|
+
.describe('The aspect ratio of the video.'),
|
|
3564
|
+
resolution: zod_1.z
|
|
3565
|
+
.enum(['720p', '1080p'])
|
|
3566
|
+
.describe('The resolution of the video.'),
|
|
3595
3567
|
watermark: zod_1.z
|
|
3596
3568
|
.boolean()
|
|
3597
3569
|
.optional()
|
|
3598
3570
|
.default(false)
|
|
3599
3571
|
.describe('Whether to add watermark to the video.'),
|
|
3600
3572
|
type: zod_1.z
|
|
3601
|
-
.enum([
|
|
3573
|
+
.enum([
|
|
3574
|
+
'lite',
|
|
3575
|
+
'sora2',
|
|
3576
|
+
'sora2-pro',
|
|
3577
|
+
'veo3.1',
|
|
3578
|
+
'veo3.1-pro',
|
|
3579
|
+
'vidu',
|
|
3580
|
+
'pixv',
|
|
3581
|
+
])
|
|
3602
3582
|
.default('lite')
|
|
3603
|
-
.describe('The model type to use. sora2 allows max 1 reference image, lite allow max 4, vidu allow max 7.'),
|
|
3583
|
+
.describe('The model type to use. sora2 and veo3.1 allows max 1 reference image, lite allow max 4, vidu allow max 7.'),
|
|
3604
3584
|
mute: zod_1.z
|
|
3605
3585
|
.boolean()
|
|
3606
3586
|
.optional()
|
|
3607
3587
|
.default(false)
|
|
3608
|
-
.describe('Whether to mute the video (effective for sora2).'),
|
|
3588
|
+
.describe('Whether to mute the video (effective for sora2 and veo3.1).'),
|
|
3609
3589
|
saveToFileName: zod_1.z
|
|
3610
3590
|
.string()
|
|
3611
3591
|
.describe('The filename to save the generated video.'),
|
|
@@ -3634,7 +3614,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3634
3614
|
.default(false)
|
|
3635
3615
|
.describe('Whether to optimize the prompt.'),
|
|
3636
3616
|
},
|
|
3637
|
-
}, async ({ prompt, rewritePrompt, referenceImages, duration,
|
|
3617
|
+
}, async ({ prompt, rewritePrompt, referenceImages, duration, aspectRatio, resolution, watermark, type, mute, saveToFileName, sceneIndex, storyBoardFile, skipConsistencyCheck, optimizePrompt, }, context) => {
|
|
3638
3618
|
try {
|
|
3639
3619
|
// 验证session状态
|
|
3640
3620
|
const currentSession = await validateSession('generate-video-by-ref');
|
|
@@ -3676,6 +3656,9 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3676
3656
|
if (scene.is_continuous === true) {
|
|
3677
3657
|
return createErrorResponse('连续镜头应使用首尾帧,请修改连续镜头设置,或将本场景改为首尾帧方式实现', 'generate-video-by-ref');
|
|
3678
3658
|
}
|
|
3659
|
+
if (scene.video_type !== 'references') {
|
|
3660
|
+
return createErrorResponse(`场景 ${sceneIndex} 中的 video_type (${scene.video_type}) 未设置为 'references',不应当使用参考生视频,请使用图生视频 generate-video 方式生成`, 'generate-video-by-ref');
|
|
3661
|
+
}
|
|
3679
3662
|
// 检查 use_video_model 与 type 参数的一致性
|
|
3680
3663
|
if (scene.use_video_model &&
|
|
3681
3664
|
type &&
|
|
@@ -3724,15 +3707,15 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3724
3707
|
}
|
|
3725
3708
|
// 检查 storyBoard.orientation 与 size 参数的一致性
|
|
3726
3709
|
if (storyBoard.orientation) {
|
|
3727
|
-
const isLandscapeSize =
|
|
3728
|
-
const isPortraitSize =
|
|
3710
|
+
const isLandscapeSize = aspectRatio === '16:9';
|
|
3711
|
+
const isPortraitSize = aspectRatio === '9:16';
|
|
3729
3712
|
if (storyBoard.orientation === 'landscape' &&
|
|
3730
3713
|
!isLandscapeSize) {
|
|
3731
|
-
return createErrorResponse(`故事板设定为横屏模式(orientation: landscape)
|
|
3714
|
+
return createErrorResponse(`故事板设定为横屏模式(orientation: landscape),但视频为竖屏格式,请使用横屏尺寸 1280x720`, 'generate-video-by-ref');
|
|
3732
3715
|
}
|
|
3733
3716
|
if (storyBoard.orientation === 'portrait' &&
|
|
3734
3717
|
!isPortraitSize) {
|
|
3735
|
-
return createErrorResponse(`故事板设定为竖屏模式(orientation: portrait)
|
|
3718
|
+
return createErrorResponse(`故事板设定为竖屏模式(orientation: portrait),但视频为横屏格式,请使用竖屏尺寸 720x1280`, 'generate-video-by-ref');
|
|
3736
3719
|
}
|
|
3737
3720
|
}
|
|
3738
3721
|
}
|
|
@@ -3778,7 +3761,10 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3778
3761
|
}
|
|
3779
3762
|
}
|
|
3780
3763
|
// 验证参考图数量限制
|
|
3781
|
-
if ((type === 'sora2' ||
|
|
3764
|
+
if ((type === 'sora2' ||
|
|
3765
|
+
type === 'sora2-pro' ||
|
|
3766
|
+
type === 'veo3.1' ||
|
|
3767
|
+
type === 'veo3.1-pro') &&
|
|
3782
3768
|
referenceImages.length > 1) {
|
|
3783
3769
|
return createErrorResponse(`${type} model only supports maximum 1 reference image`, 'generate-video-by-ref');
|
|
3784
3770
|
}
|
|
@@ -3808,12 +3794,14 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3808
3794
|
if (promptPrefix) {
|
|
3809
3795
|
promptPrefix += '\n';
|
|
3810
3796
|
}
|
|
3797
|
+
const finalPrompt = `${promptPrefix}${prompt}`;
|
|
3811
3798
|
// 调用 referencesToVideo 函数
|
|
3812
3799
|
const result = await currentSession.ai.referencesToVideo({
|
|
3813
|
-
prompt:
|
|
3800
|
+
prompt: finalPrompt,
|
|
3814
3801
|
reference_images: referenceImageUrls, // 使用URL数组而不是base64数组
|
|
3815
3802
|
duration,
|
|
3816
|
-
|
|
3803
|
+
aspect_ratio: aspectRatio,
|
|
3804
|
+
resolution,
|
|
3817
3805
|
watermark,
|
|
3818
3806
|
type,
|
|
3819
3807
|
mute,
|
|
@@ -3825,9 +3813,23 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3825
3813
|
if (result.error) {
|
|
3826
3814
|
return createErrorResponse(result.error, 'generate-video-by-ref');
|
|
3827
3815
|
}
|
|
3828
|
-
if (!result.url) {
|
|
3816
|
+
if (!result.url && !result.taskUrl) {
|
|
3829
3817
|
return createErrorResponse('Video generation failed: no video URL returned', 'generate-video-by-ref');
|
|
3830
3818
|
}
|
|
3819
|
+
else if (result.taskUrl) {
|
|
3820
|
+
return {
|
|
3821
|
+
content: [
|
|
3822
|
+
{
|
|
3823
|
+
type: 'text',
|
|
3824
|
+
text: JSON.stringify({
|
|
3825
|
+
success: true,
|
|
3826
|
+
message: '该视频生成任务正在运行中,它是异步任务,且执行时间较长,你应立即调用工具 wait-for-task-finish 来等待任务结束,如该工具调用超时,你应立即再次重新调用直到任务结束。',
|
|
3827
|
+
taskUrl: result.taskUrl,
|
|
3828
|
+
}),
|
|
3829
|
+
},
|
|
3830
|
+
],
|
|
3831
|
+
};
|
|
3832
|
+
}
|
|
3831
3833
|
// 下载生成的视频
|
|
3832
3834
|
await saveMaterial(currentSession, result.url, validatedFileName);
|
|
3833
3835
|
// 更新媒体日志
|
|
@@ -3854,7 +3856,7 @@ server.registerTool('generate-video-by-ref', {
|
|
|
3854
3856
|
url: result.url,
|
|
3855
3857
|
last_frame_url: result.last_frame_url,
|
|
3856
3858
|
referenceImageUrls,
|
|
3857
|
-
prompt,
|
|
3859
|
+
prompt: finalPrompt,
|
|
3858
3860
|
}, null, 2),
|
|
3859
3861
|
},
|
|
3860
3862
|
],
|
|
@@ -3878,6 +3880,10 @@ server.registerTool('extend-video-duration', {
|
|
|
3878
3880
|
.max(7)
|
|
3879
3881
|
.default(3)
|
|
3880
3882
|
.describe('Duration to extend the video in seconds (1-7).'),
|
|
3883
|
+
resolution: zod_1.z
|
|
3884
|
+
.enum(['720p', '1080p'])
|
|
3885
|
+
.default('720p')
|
|
3886
|
+
.describe('The resolution of the video.'),
|
|
3881
3887
|
prompt: zod_1.z
|
|
3882
3888
|
.string()
|
|
3883
3889
|
.optional()
|
|
@@ -3894,7 +3900,7 @@ server.registerTool('extend-video-duration', {
|
|
|
3894
3900
|
.string()
|
|
3895
3901
|
.describe('The filename to save the extended video.'),
|
|
3896
3902
|
},
|
|
3897
|
-
}, async ({ videoFileName, duration, prompt, type = 'turbo', endFrame, saveToFileName, }, context) => {
|
|
3903
|
+
}, async ({ videoFileName, duration, resolution, prompt, type = 'turbo', endFrame, saveToFileName, }, context) => {
|
|
3898
3904
|
try {
|
|
3899
3905
|
await validateSession('extend-video');
|
|
3900
3906
|
validateFileName(videoFileName);
|
|
@@ -3923,6 +3929,7 @@ server.registerTool('extend-video-duration', {
|
|
|
3923
3929
|
video_url: videoUri,
|
|
3924
3930
|
prompt: finalPrompt,
|
|
3925
3931
|
duration,
|
|
3932
|
+
resolution,
|
|
3926
3933
|
end_frame: endFrameUri,
|
|
3927
3934
|
onProgress: async (metaData) => {
|
|
3928
3935
|
sendProgress(context, ++progress, undefined, `Extension progress: ${Math.round(progress * 100)}%`);
|
|
@@ -3976,54 +3983,31 @@ server.registerTool('extend-video-duration', {
|
|
|
3976
3983
|
return createErrorResponse(error, 'extend-video');
|
|
3977
3984
|
}
|
|
3978
3985
|
});
|
|
3979
|
-
server.registerTool('
|
|
3980
|
-
title: '
|
|
3981
|
-
description: '
|
|
3986
|
+
server.registerTool('use-template', {
|
|
3987
|
+
title: 'Use Template',
|
|
3988
|
+
description: 'Find a template that matches the user request, and use it to generate a new material.',
|
|
3982
3989
|
inputSchema: {
|
|
3983
3990
|
user_request: zod_1.z.string().describe('用户请求,根据意图自动匹配模板'),
|
|
3984
|
-
text_prompts: zod_1.z
|
|
3985
|
-
.array(zod_1.z.string().describe('Text prompt for the template to build video.'))
|
|
3986
|
-
.optional()
|
|
3987
|
-
.describe('Optional text prompts to use in the template.'),
|
|
3988
3991
|
materials: zod_1.z
|
|
3989
3992
|
.array(zod_1.z.string().describe('Material file name in materials directory.'))
|
|
3990
3993
|
.optional()
|
|
3991
3994
|
.describe('Optional materials to use in the template.'),
|
|
3992
3995
|
saveToFileName: zod_1.z
|
|
3993
3996
|
.string()
|
|
3994
|
-
.describe('The filename to save the generated
|
|
3997
|
+
.describe('The filename to save the generated material.'),
|
|
3995
3998
|
},
|
|
3996
|
-
}, async ({ user_request,
|
|
3999
|
+
}, async ({ user_request, saveToFileName, materials }) => {
|
|
3997
4000
|
try {
|
|
3998
|
-
const templates = [
|
|
3999
|
-
{
|
|
4000
|
-
id: '7569583728302817331',
|
|
4001
|
-
name: '宠物唱歌',
|
|
4002
|
-
description: '用一张宠物照片,生成一段宠物唱歌的视频。',
|
|
4003
|
-
trigger: '根据{A图片}生成一段{宠物A}唱歌的视频',
|
|
4004
|
-
},
|
|
4005
|
-
{
|
|
4006
|
-
id: '7569605825011367976',
|
|
4007
|
-
name: '万圣节宠物弹吉他',
|
|
4008
|
-
description: '用一张宠物照片,生成一段宠物弹吉他的视频。',
|
|
4009
|
-
trigger: '根据{A图片}生成一段{宠物A}弹吉他的视频',
|
|
4010
|
-
},
|
|
4011
|
-
{
|
|
4012
|
-
id: '7572443489834844223',
|
|
4013
|
-
name: '图生动作模仿视频',
|
|
4014
|
-
description: '用一张图片和动作视频,生成一段图片主体模仿该动作视频的新视频。',
|
|
4015
|
-
trigger: '生成{A图片}模仿{B视频}的视频',
|
|
4016
|
-
},
|
|
4017
|
-
{
|
|
4018
|
-
id: '7575160546555674670',
|
|
4019
|
-
name: '文生动作模仿视频',
|
|
4020
|
-
description: '用一段提示词和视频,生成一段模仿该视频的新视频。',
|
|
4021
|
-
trigger: '生成一段{提示词A}模仿{B视频}的视频',
|
|
4022
|
-
},
|
|
4023
|
-
];
|
|
4024
4001
|
const currentSession = await validateSession('generate-video-by-template');
|
|
4025
|
-
const validatedFileName = validateFileName(saveToFileName);
|
|
4026
4002
|
const ai = currentSession.ai;
|
|
4003
|
+
const data = await ai.listTemplates('all');
|
|
4004
|
+
const templates = data.map(item => ({
|
|
4005
|
+
id: item.workflow_id,
|
|
4006
|
+
name: item.details.name,
|
|
4007
|
+
description: item.details.description,
|
|
4008
|
+
trigger: item.details.trigger,
|
|
4009
|
+
}));
|
|
4010
|
+
const validatedFileName = validateFileName(saveToFileName);
|
|
4027
4011
|
let completion = await ai.getCompletions({
|
|
4028
4012
|
model: 'Doubao-Seed-1.6',
|
|
4029
4013
|
messages: [
|
|
@@ -4056,13 +4040,13 @@ server.registerTool('generate-video-by-template', {
|
|
|
4056
4040
|
required: ['parameters'],
|
|
4057
4041
|
},
|
|
4058
4042
|
};
|
|
4059
|
-
const prompt =
|
|
4043
|
+
const prompt = `你根据模板工作流描述中的 input_schema、用户需求 prompt 和 materials 生成一个 JSON 对象,作为模板工作流的调用参数。
|
|
4060
4044
|
|
|
4061
|
-
##
|
|
4062
|
-
${
|
|
4045
|
+
## **工作流描述**:
|
|
4046
|
+
${workflowInfo.data.workflow_detail.description}
|
|
4063
4047
|
|
|
4064
|
-
##
|
|
4065
|
-
${
|
|
4048
|
+
## **用户需求 prompt**:
|
|
4049
|
+
${user_request}
|
|
4066
4050
|
|
|
4067
4051
|
## **materials**:
|
|
4068
4052
|
${JSON.stringify(materialUrls)}`;
|
|
@@ -4088,20 +4072,6 @@ ${JSON.stringify(materialUrls)}`;
|
|
|
4088
4072
|
if (!parameters) {
|
|
4089
4073
|
throw new Error('Failed to get parameters');
|
|
4090
4074
|
}
|
|
4091
|
-
// console.log(parameters);
|
|
4092
|
-
// return {
|
|
4093
|
-
// content: [
|
|
4094
|
-
// {
|
|
4095
|
-
// type: 'text',
|
|
4096
|
-
// text: JSON.stringify({
|
|
4097
|
-
// success: true,
|
|
4098
|
-
// prompt,
|
|
4099
|
-
// templateId,
|
|
4100
|
-
// parameters,
|
|
4101
|
-
// }),
|
|
4102
|
-
// },
|
|
4103
|
-
// ],
|
|
4104
|
-
// };
|
|
4105
4075
|
const result = await ai.runCozeWorkflow(templateId, JSON.parse(parameters).parameters);
|
|
4106
4076
|
if (result.url) {
|
|
4107
4077
|
// 保存到项目材料目录
|
|
@@ -4133,7 +4103,7 @@ ${JSON.stringify(materialUrls)}`;
|
|
|
4133
4103
|
};
|
|
4134
4104
|
}
|
|
4135
4105
|
catch (error) {
|
|
4136
|
-
return createErrorResponse(error, '
|
|
4106
|
+
return createErrorResponse(error, 'use-template');
|
|
4137
4107
|
}
|
|
4138
4108
|
});
|
|
4139
4109
|
server.registerTool('run-ffmpeg-command', {
|
|
@@ -4230,6 +4200,34 @@ server.registerTool('run-ffmpeg-command', {
|
|
|
4230
4200
|
return createErrorResponse(error, 'run-ffmpeg-command');
|
|
4231
4201
|
}
|
|
4232
4202
|
});
|
|
4203
|
+
server.registerTool('do-storyboard-optimization', {
|
|
4204
|
+
title: 'Do Storyboard Optimization',
|
|
4205
|
+
description: 'Get storyboard optimization guidelines and action instructions.',
|
|
4206
|
+
inputSchema: {},
|
|
4207
|
+
}, async () => {
|
|
4208
|
+
try {
|
|
4209
|
+
// 调用 do-storyboard-optimization 工具时,设置 checkStoryboardFlag 为 true
|
|
4210
|
+
checkStoryboardFlag = true;
|
|
4211
|
+
const guidelinePath = (0, node_path_1.resolve)(__dirname, './prompts/actions/storyboard_optimization.md');
|
|
4212
|
+
const storyboardOptimizationGuidelines = await (0, promises_1.readFile)(guidelinePath, 'utf-8');
|
|
4213
|
+
return {
|
|
4214
|
+
content: [
|
|
4215
|
+
{
|
|
4216
|
+
type: 'text',
|
|
4217
|
+
text: JSON.stringify({
|
|
4218
|
+
content: {
|
|
4219
|
+
guideline: storyboardOptimizationGuidelines,
|
|
4220
|
+
action: '你应当根据guideline优化storyboard.json',
|
|
4221
|
+
},
|
|
4222
|
+
}),
|
|
4223
|
+
},
|
|
4224
|
+
],
|
|
4225
|
+
};
|
|
4226
|
+
}
|
|
4227
|
+
catch (error) {
|
|
4228
|
+
return createErrorResponse(error, 'do-storyboard-optimization');
|
|
4229
|
+
}
|
|
4230
|
+
});
|
|
4233
4231
|
server.registerTool('search-context', {
|
|
4234
4232
|
title: 'Search Context',
|
|
4235
4233
|
description: 'Search the context.',
|