feat: refine UI, improve UX, optimize the analysis pipeline, and add character standing positions
This commit is contained in:
@@ -274,4 +274,97 @@ describe('script-to-storyboard orchestrator retry', () => {
|
||||
expect(result.summary.clipCount).toBe(3)
|
||||
expect(maxActivePhase1).toBe(1)
|
||||
})
|
||||
|
||||
it('pipelines clips so one clip can enter phase2 before another clip finishes phase1', async () => {
|
||||
let releaseClip1Phase1: (() => void) | null = null
|
||||
const clip1Phase1Gate = new Promise<void>((resolve) => {
|
||||
releaseClip1Phase1 = resolve
|
||||
})
|
||||
let clip2Phase2Started = false
|
||||
let clip1Phase1ResolvedAfterClip2Phase2 = false
|
||||
|
||||
const runStep = vi.fn(async (meta, _prompt, action: string) => {
|
||||
const stepId = String(meta.stepId)
|
||||
|
||||
if (action === 'storyboard_phase1_plan' && stepId === 'clip_clip-1_phase1') {
|
||||
await clip1Phase1Gate
|
||||
clip1Phase1ResolvedAfterClip2Phase2 = clip2Phase2Started
|
||||
return {
|
||||
text: JSON.stringify([{ panel_number: 1, description: '镜头1', location: '场景A', source_text: '原文1', characters: [] }]),
|
||||
reasoning: '',
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'storyboard_phase1_plan' && stepId === 'clip_clip-2_phase1') {
|
||||
return {
|
||||
text: JSON.stringify([{ panel_number: 1, description: '镜头2', location: '场景A', source_text: '原文2', characters: [] }]),
|
||||
reasoning: '',
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'storyboard_phase2_cinematography' && stepId === 'clip_clip-2_phase2_cinematography') {
|
||||
clip2Phase2Started = true
|
||||
releaseClip1Phase1?.()
|
||||
return {
|
||||
text: JSON.stringify([{ panel_number: 1, composition: '居中', lighting: '顶光', color_palette: '冷色', atmosphere: '紧张', technical_notes: 'note' }]),
|
||||
reasoning: '',
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'storyboard_phase2_acting') {
|
||||
return { text: JSON.stringify([{ panel_number: 1, characters: [] }]), reasoning: '' }
|
||||
}
|
||||
|
||||
if (action === 'storyboard_phase2_cinematography') {
|
||||
return {
|
||||
text: JSON.stringify([{ panel_number: 1, composition: '居中', lighting: '顶光', color_palette: '冷色', atmosphere: '紧张', technical_notes: 'note' }]),
|
||||
reasoning: '',
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'storyboard_phase3_detail') {
|
||||
return {
|
||||
text: JSON.stringify([{ panel_number: 1, description: '细化镜头', location: '场景A', source_text: '原文', characters: [] }]),
|
||||
reasoning: '',
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`unexpected action: ${action}:${stepId}`)
|
||||
})
|
||||
|
||||
const result = await runScriptToStoryboardOrchestrator({
|
||||
concurrency: 2,
|
||||
clips: [
|
||||
{
|
||||
id: 'clip-1',
|
||||
content: '文本1',
|
||||
characters: JSON.stringify([{ name: '角色A' }]),
|
||||
location: '场景A',
|
||||
screenplay: null,
|
||||
},
|
||||
{
|
||||
id: 'clip-2',
|
||||
content: '文本2',
|
||||
characters: JSON.stringify([{ name: '角色A' }]),
|
||||
location: '场景A',
|
||||
screenplay: null,
|
||||
},
|
||||
],
|
||||
novelPromotionData: {
|
||||
characters: [{ name: '角色A', appearances: [] }],
|
||||
locations: [{ name: '场景A', images: [] }],
|
||||
},
|
||||
promptTemplates: {
|
||||
phase1PlanTemplate: '{clip_content} {clip_json} {characters_lib_name} {locations_lib_name} {characters_introduction} {characters_appearance_list} {characters_full_description}',
|
||||
phase2CinematographyTemplate: '{panels_json} {panel_count} {locations_description} {characters_info}',
|
||||
phase2ActingTemplate: '{panels_json} {panel_count} {characters_info}',
|
||||
phase3DetailTemplate: '{panels_json} {characters_age_gender} {locations_description}',
|
||||
},
|
||||
runStep,
|
||||
})
|
||||
|
||||
expect(result.summary.clipCount).toBe(2)
|
||||
expect(clip2Phase2Started).toBe(true)
|
||||
expect(clip1Phase1ResolvedAfterClip2Phase2).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user