diff --git a/src/server/index.ts b/src/server/index.ts index 5cc3347..c9ca00f 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1043,7 +1043,7 @@ app.post("/api/projects/:id/instruct", async (c) => { ctx.analyze, await instructHighlights(project.id), await askHistory(project.id), - { videos: await instructVideos(project.id), assemble: ctx.assemble, watchBudget: QUICK_WATCH_SECONDS }, + { videos: await instructVideos(project.id), assemble: ctx.assemble, clips: ctx.clips, watchBudget: QUICK_WATCH_SECONDS }, ); // A failed exchange stays on screen but out of what the model reads next. await userSays("failure" in out); diff --git a/src/server/instruct.ts b/src/server/instruct.ts index d3e340b..f0018b2 100644 --- a/src/server/instruct.ts +++ b/src/server/instruct.ts @@ -1058,7 +1058,7 @@ export async function instructEdit( /** Videos it may add, how to build a cut from several of them, and how much * footage this run may watch: past it, it stops with `background` and * changes nothing (omitted in a background job: no limit). */ - more: { videos?: InstructVideo[]; assemble?: Assembler; watchBudget?: number } = {}, + more: { videos?: InstructVideo[]; assemble?: Assembler; clips?: ClipDeps; watchBudget?: number } = {}, ): Promise { if (!cfg.openrouterKey) { return { @@ -1068,7 +1068,7 @@ export async function instructEdit( }, }; } - const ctx: InstructContext = { cfg, assetNames, sourceSeconds, analyze, highlights, assemble: more.assemble, watchBudget: more.watchBudget }; + const ctx: InstructContext = { cfg, assetNames, sourceSeconds, analyze, highlights, assemble: more.assemble, clips: more.clips, watchBudget: more.watchBudget }; let state = startInstruction(edl, instruction, assetNames, highlights, history, more.videos ?? []); for (;;) { const out = await instructStep(state, ctx); diff --git a/test/e2e/run.mjs b/test/e2e/run.mjs index 80e6282..4948fdc 100644 --- a/test/e2e/run.mjs +++ b/test/e2e/run.mjs @@ -1495,6 +1495,15 @@ console.log("14 ok: review comments off by default, on per link, pinned to an ex { title: "Why we started", start: "1:40.0", end: "2:30.0", hook_start: "2:10.0", hook_end: "2:14.0", why: "a story" }, { title: "The number", start: "6:00.0", end: "6:45.0", hook_start: "6:00.0", hook_end: "6:03.0", why: "a surprise" }, ] }; + // Asked the way a person asks in the editor (not background): offered, then proposed. + const pickClips = world.askReplies[0]; + world.askBodies = []; + world.askReplies = [pickClips]; + r = await call("POST", `/api/projects/${pi}/instruct`, { instruction: "We'd need 5/6 cut downs, with a strong hook" }); + assert.ok(world.askBodies[0].tools.some((t) => t.function.name === "make_clips"), "make_clips is offered in a quick answer"); + assert.equal(r.data.job?.status, "proposed", JSON.stringify(r.data)); + await call("POST", `/api/projects/${pi}/ask/jobs/${r.data.job.id}/cancel`); + world.askReplies = [pickClips, { content: "Made two clips from the interview." }]; r = await call("POST", `/api/projects/${pi}/instruct`, { instruction: "5 vertical clips with a strong hook for LinkedIn", background: true }); assert.equal(r.status, 202, JSON.stringify(r.data)); const cj = r.data.job.id;