feat(agent): 统一专业拆镜入口与真实镜头生成

This commit is contained in:
王志伟
2026-09-08 23:28:56 +08:00
parent 45de256deb
commit b33de36976
12 changed files with 215 additions and 22 deletions

View File

@@ -1,5 +1,5 @@
import { expect, test } from "bun:test";
import { buildAgentStoryboardOperations, validateAgentStoryboardCreation } from "../src/lib/canvas/canvas-agent-storyboard";
import { buildAgentStoryboardDraft, buildAgentStoryboardOperations, validateAgentStoryboardCreation } from "../src/lib/canvas/canvas-agent-storyboard";
import { inspectStoryboardReadiness } from "../src/lib/canvas/canvas-storyboard-context";
import { buildCanvasAgentContext } from "../src/lib/canvas/canvas-agent-context";
import { createShortDramaPipeline } from "../src/lib/canvas/canvas-short-drama";
@@ -15,6 +15,16 @@ function fixture() {
return { snapshot, script };
}
test("待拆镜入口保存原始剧本但不伪造已完成的镜头", () => {
const { snapshot } = fixture();
const result = applyCanvasAgentOps(snapshot, buildAgentStoryboardDraft({ title: "旧机来电", prompt: "手机突然响起", x: 500, y: 100 }));
const node = result.nodes.find((item) => item.title === "旧机来电")!;
expect(node.type).toBe("script");
expect(node.metadata?.composerContent).toBe("手机突然响起");
expect(node.metadata?.storyboard?.rows).toEqual([]);
expect(node.metadata?.workflowDescription).toContain("尚未生成");
});
test("Agent 上下文提前指出未选择画风及分镜空行", () => {
const { snapshot, script } = fixture();
const readiness = buildCanvasAgentContext(snapshot).storyboardReadiness;

View File

@@ -0,0 +1,37 @@
import { expect, test } from "bun:test";
import { submitStoryboardTask } from "../src/services/storyboard-submission";
import { creationRuns, type CreationSubmission } from "../src/services/api/creation-runs";
import type { CreateTaskInput } from "../src/services/api/task-center";
const request = { type: "agent_storyboard_rows", projectId: "canvas", prompt: "剧本" } as CreateTaskInput;
function harness(expiresAt = "2099-01-01") {
const calls: string[] = [];
const run = { id: "run", executionEpoch: 1, executionOwner: "owner" };
const submission = { id: "quote", quote: { expiresAt } } as CreationSubmission;
const api = {
...creationRuns,
create: async () => ({ run, submissions: [] }), claim: async () => run,
prepare: async () => { calls.push("prepare"); return submission; },
approve: async () => { calls.push("approve"); return { submissions: [{ ...submission, approvedAt: "now" }] }; },
execute: async () => { calls.push("execute"); return { id: "task" }; },
release: async () => { calls.push("release"); return { released: true }; },
} as typeof creationRuns;
return { api, calls };
}
test("取消专业拆镜费用确认不会批准或提交任务", async () => {
const h = harness();
expect(await submitStoryboardTask(request, { signal: new AbortController().signal, assertCurrent() {}, confirm: async () => false }, h.api)).toBeUndefined();
expect(h.calls).toEqual(["prepare", "release"]);
});
test("确认后提交原报价对应的专业任务", async () => {
const h = harness();
expect((await submitStoryboardTask(request, { signal: new AbortController().signal, assertCurrent() {}, confirm: async () => true }, h.api))?.id).toBe("task");
expect(h.calls).toEqual(["prepare", "approve", "execute", "release"]);
});
test("过期报价或确认期间编辑分镜均阻止提交", async () => {
for (const changed of [false, true]) {
const h = harness(changed ? "2099-01-01" : "2000-01-01");
await expect(submitStoryboardTask(request, { signal: new AbortController().signal, assertCurrent() { if (changed) throw new Error("分镜已编辑"); }, confirm: async () => true }, h.api)).rejects.toThrow();
expect(h.calls).toEqual(["prepare", "release"]);
}
});