fix(storyboard): 章节分镜任务补输出契约并穿透 text 包装解析镜头行

- 章节分镜任务 prompt 显式追加 {title, rows} JSON 输出契约:该链路不走服务端分镜模板(无 metadata.promptTemplateOperation),缺契约时模型按自然语言习惯返回 Markdown 表格,导致“分镜任务没有返回镜头行”
- 文本任务 result_json 外层统一为 {mode, reasoning, text} 包装,storyboardRowsFromTask 现穿透 text 内层解析分镜表,并兜底剥离代码块与首尾大括号截取;顶层直接落 {title, rows} 的结构保持兼容
- 分镜任务 input 显式携带 mode: text(后端仅对 video_ 前缀任务兜底)
- 新增 canvas-storyboard-rows-parse.test.ts 覆盖六种解析场景
This commit is contained in:
李祥生
2026-09-15 17:23:38 +08:00
parent 8db7cf0300
commit 446c263426
3 changed files with 108 additions and 3 deletions

View File

@@ -1,6 +1,6 @@
import { runBackendCanvasGenerationTask } from "@/lib/canvas/canvas-project-generation";
import { PromptTemplateOperation, promptTemplateTaskPlaceholder } from "@/lib/prompts";
import { storyboardRowsFromTask } from "@/lib/canvas/canvas-project-domain";
import { storyboardRowsFromTask, storyboardRowsOutputContract } from "@/lib/canvas/canvas-project-domain";
import { parseChapterAssetBreakdown, type ChapterAssetBreakdown } from "@/lib/canvas/chapter-asset-breakdown";
import { parseCharacterBreakdown } from "@/lib/canvas/canvas-character-reference";
import { backendProviderConfig, parseBackendGenerationResult } from "@/services/api/generation-task";
@@ -90,14 +90,21 @@ export async function generateChapterStoryboard(input: ChapterStoryboardGenerati
skills: input.skills,
selectedSkillIds: input.selectedSkillIds,
});
// 技能上下文只描述工作流,不会约束输出结构;必须显式带上输出契约,否则模型返回 Markdown 表格。
const prompt = [
skillExecution.prompt,
storyboardRowsOutputContract("每个镜头必须能独立用于生成首帧图片和镜头视频。"),
].join("\n\n");
const task = await createGenerationTask({
projectId: input.projectId,
type: "canvas_text",
operation: "storyboard",
prompt: skillExecution.prompt,
prompt,
model,
...(logicalModelIDForConfig(config) ? { logicalModelId: logicalModelIDForConfig(config) } : {}),
input: {
// 后端只在 video_ 前缀的任务上兜底 mode,文本任务必须显式带上。
mode: "text",
canvasAssets: input.assets,
requirements: "输出可直接写入分镜制作并继续生成分镜图、动作预演和镜头视频的分镜表。",
projectStyle: input.projectStyle,