@vanillaskyai/video 0.4.1 → 0.5.1
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/CHANGELOG.md +88 -0
- package/PUBLIC-API.md +12 -5
- package/README.md +13 -18
- package/dist/{builtin-server-BRTZN4Q7.js → builtin-server-YHEZ2JRF.js} +2 -2
- package/dist/check-runtime.js +1 -1
- package/dist/{chunk-BKF3A357.js → chunk-34O5BY6X.js} +1 -1
- package/dist/{chunk-LHFADNWJ.js → chunk-3ROGEU5A.js} +179 -342
- package/dist/{chunk-XAMUOSX7.js → chunk-3RV4YKB3.js} +0 -1
- package/dist/{chunk-GHJEAP4O.js → chunk-66MNRUCR.js} +1 -1
- package/dist/{chunk-EE6PK6XC.js → chunk-AOWSXU2K.js} +1 -1
- package/dist/{chunk-GR4I3MN2.js → chunk-EFL34TXF.js} +3 -3
- package/dist/{chunk-I5YWVQ5R.js → chunk-FVTMYS6U.js} +1 -1
- package/dist/{chunk-STIFILQG.js → chunk-FZHMQFG3.js} +46 -38
- package/dist/{chunk-JW47XCRL.js → chunk-G66Z5CWR.js} +30 -72
- package/dist/{chunk-RBQN3VSY.js → chunk-HVCPEAQF.js} +83 -1265
- package/dist/{chunk-N3KUA7VP.js → chunk-K6YVQODK.js} +4 -1
- package/dist/{chunk-WHPZED7X.js → chunk-PNS52FL4.js} +2 -5
- package/dist/{chunk-S5ZU5WUU.js → chunk-ZQQQAAKP.js} +2 -2
- package/dist/cli.js +20 -14
- package/dist/compose-video-QJHKTOQ7.js +13 -0
- package/dist/{events-D_1F6gw5.d.ts → events-BBP30j3c.d.ts} +3 -14
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{kit-DLeCr3d0.d.ts → kit-DEG3fcaL.d.ts} +1 -1
- package/dist/react.d.ts +6 -5
- package/dist/react.js +999 -348
- package/dist/server.d.ts +3 -3
- package/dist/server.js +18 -79
- package/dist/{system-prompt-NMYHLAMJ.js → system-prompt-4I6Z5HK3.js} +1 -1
- package/dist/template-catalog.js +1 -1
- package/dist/templates.d.ts +3 -3
- package/dist/templates.js +87 -4
- package/dist/test.d.ts +3 -27
- package/dist/test.js +3 -3
- package/dist/{text-stream-UPGUD2TD.js → text-stream-J6EDDJP4.js} +3 -3
- package/dist/{types-Cg0Onj7l.d.ts → types-BV9IqExh.d.ts} +1 -16
- package/docs/agent-integration.md +4 -3
- package/docs/concepts.md +4 -4
- package/docs/custom-templates.md +2 -2
- package/docs/getting-started.md +13 -18
- package/docs/integrate-nextjs.md +12 -8
- package/docs/persistence.md +1 -1
- package/docs/prompt-and-input.md +2 -2
- package/docs/reference/protocol.md +9 -12
- package/docs/reference/provider-adapters.md +5 -5
- package/docs/testing.md +1 -1
- package/examples/nextjs-quickstart/.env.example +2 -2
- package/examples/nextjs-quickstart/README.md +4 -1
- package/examples/nextjs-quickstart/package.json +4 -4
- package/examples/nextjs-quickstart/src/app/api/video/route.ts +4 -2
- package/package.json +8 -8
- package/dist/chunk-CR7FE7BW.js +0 -109
- package/dist/compose-video-6UQ33DOO.js +0 -13
|
@@ -4,24 +4,25 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
applyVideoEvent,
|
|
6
6
|
checksumVideo,
|
|
7
|
+
createReplayableStream,
|
|
7
8
|
createVideoState
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-FZHMQFG3.js";
|
|
9
10
|
import {
|
|
10
11
|
parseVideoPlanPart,
|
|
11
12
|
safePublicDiagnostic
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-G66Z5CWR.js";
|
|
13
14
|
import {
|
|
14
15
|
MAX_RETAINED_INSTRUCTIONS_LENGTH,
|
|
15
16
|
MAX_RETAINED_MEDIA_URLS,
|
|
16
17
|
MAX_RETAINED_MEDIA_URL_LENGTH,
|
|
17
18
|
MAX_RETAINED_SOURCE_LENGTH,
|
|
18
19
|
parseVideo
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-66MNRUCR.js";
|
|
20
21
|
import {
|
|
21
22
|
VIDEO_PROTOCOL_VERSION,
|
|
22
23
|
VIDEO_SCHEMA_VERSION,
|
|
23
24
|
createVideoRequest
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-AOWSXU2K.js";
|
|
25
26
|
import {
|
|
26
27
|
getCloserReserve,
|
|
27
28
|
getReadableSceneDuration,
|
|
@@ -84,12 +85,6 @@ function resolveSuppliedMediaPlanPart(part, input) {
|
|
|
84
85
|
if (part.type === "scene.add") {
|
|
85
86
|
return { ...part, scene: { ...part.scene, variables: resolveReferences(part.scene.variables, references) } };
|
|
86
87
|
}
|
|
87
|
-
if (part.type === "scene.patch" && part.patch.variables) {
|
|
88
|
-
return { ...part, patch: { ...part.patch, variables: resolveReferences(part.patch.variables, references) } };
|
|
89
|
-
}
|
|
90
|
-
if (part.type === "asset.patch") {
|
|
91
|
-
return { ...part, variables: resolveReferences(part.variables, references) };
|
|
92
|
-
}
|
|
93
88
|
return part;
|
|
94
89
|
}
|
|
95
90
|
function buildVideoUserPrompt(input, openingDurationSec = 0) {
|
|
@@ -100,7 +95,7 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
|
|
|
100
95
|
typeof input.opening === "string" && input.opening.trim() ? `The host has already added the opening scene, which consumes ${openingDurationSec} seconds. Continue after it and do not repeat or rewrite it.` : "Add the first grounded scene as soon as it is complete.",
|
|
101
96
|
"The first generated body scene must be fully playable without external media. Use a content-fit text, data, comparison, list, or device-free template with no media URL or keyword.",
|
|
102
97
|
"Never use media, ctaMedia, or reaction as the first generated body template, even with mediaType=gradient. Choose a non-media template first.",
|
|
103
|
-
"Add that scene before resolving any stock or supplied asset.
|
|
98
|
+
"Add that scene before resolving any stock or supplied asset. Resolve media as part of each complete later body scene.",
|
|
104
99
|
"Use only claims supported by the factual basis permitted by the trusted system prompt.",
|
|
105
100
|
"Select the most decision-relevant grounded takeaways that fit the duration; represent each selected takeaway once before completing the response.",
|
|
106
101
|
"For a long source, summarize instead of attempting to represent every fact, unless the creative instructions explicitly request complete fact coverage that fits the duration.",
|
|
@@ -134,49 +129,51 @@ function buildVideoUserPrompt(input, openingDurationSec = 0) {
|
|
|
134
129
|
].join("\n");
|
|
135
130
|
}
|
|
136
131
|
|
|
137
|
-
// src/server/
|
|
132
|
+
// src/server/composition-input.ts
|
|
138
133
|
var DEFAULT_OPENING_TEXT = "Creating your video...";
|
|
139
|
-
function
|
|
134
|
+
function normalizeVideoInput(input) {
|
|
140
135
|
return {
|
|
141
136
|
...input,
|
|
142
137
|
opening: input.opening === false ? false : input.opening?.trim() || DEFAULT_OPENING_TEXT
|
|
143
138
|
};
|
|
144
139
|
}
|
|
145
140
|
function resolveStreamCapabilities(capabilities, hasRuntimeOpening) {
|
|
146
|
-
if (!hasRuntimeOpening) return capabilities;
|
|
147
|
-
if (capabilities?.templates == null) return capabilities;
|
|
141
|
+
if (!hasRuntimeOpening || capabilities?.templates == null) return capabilities;
|
|
148
142
|
return {
|
|
149
143
|
...capabilities,
|
|
150
144
|
templates: [.../* @__PURE__ */ new Set(["media", ...capabilities.templates])]
|
|
151
145
|
};
|
|
152
146
|
}
|
|
153
|
-
function
|
|
154
|
-
if (!
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} catch {
|
|
147
|
+
function validateVideoInput(input) {
|
|
148
|
+
if (!input.input.trim()) throw new Error("Video response input is required");
|
|
149
|
+
if (input.knowledgeMode != null && input.knowledgeMode !== "input-only" && input.knowledgeMode !== "general") {
|
|
150
|
+
throw new Error("Video response knowledge mode must be input-only or general");
|
|
158
151
|
}
|
|
152
|
+
if (input.maxDurationSec != null && (!Number.isFinite(input.maxDurationSec) || input.maxDurationSec < 5 || input.maxDurationSec > 120)) {
|
|
153
|
+
throw new Error("Video response maximum duration must be between 5 and 120 seconds");
|
|
154
|
+
}
|
|
155
|
+
if (input.opening !== false && input.opening != null && !input.opening.trim()) {
|
|
156
|
+
throw new Error("Video response opening must be a non-empty string");
|
|
157
|
+
}
|
|
158
|
+
if (input.audio && !input.audio.src.trim()) {
|
|
159
|
+
throw new Error("Video response audio src must be a non-empty string");
|
|
160
|
+
}
|
|
161
|
+
if (input.audio && input.audio.src.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
|
|
162
|
+
throw new Error(`Video response audio src must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
|
|
163
|
+
}
|
|
164
|
+
for (const [index, media] of (input.suppliedMedia ?? []).entries()) {
|
|
165
|
+
if (media.url.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
|
|
166
|
+
throw new Error(`Video response supplied media ${index} URL must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
|
|
167
|
+
}
|
|
168
|
+
if (media.posterUrl && media.posterUrl.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
|
|
169
|
+
throw new Error(`Video response supplied media ${index} poster URL must be at most ${MAX_RETAINED_MEDIA_URL_LENGTH} characters`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
resolveVideoBrand(input.brand);
|
|
159
173
|
}
|
|
160
|
-
function
|
|
161
|
-
return {
|
|
162
|
-
code: warning.code,
|
|
163
|
-
category: warning.category,
|
|
164
|
-
message: warning.message,
|
|
165
|
-
...warning.sceneId != null ? { sceneId: warning.sceneId } : {},
|
|
166
|
-
recoverable: warning.recoverable
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
function now() {
|
|
170
|
-
return globalThis.performance?.now?.() ?? Date.now();
|
|
171
|
-
}
|
|
172
|
-
function safeAbortReason(value) {
|
|
173
|
-
if (value instanceof DOMException && value.name === "TimeoutError") return "Request timed out";
|
|
174
|
-
if (value instanceof Error && value.name === "TimeoutError") return "Request timed out";
|
|
175
|
-
return safePublicDiagnostic(value instanceof Error ? value.message : value, "Request aborted");
|
|
176
|
-
}
|
|
177
|
-
function buildInitialConfig(input, audio, snapshotRetention, closerReserveSec, getTemplatePacing) {
|
|
174
|
+
function buildInitialComposition(input, audio, snapshotRetention, closerReserveSec, getTemplatePacing) {
|
|
178
175
|
const openingText = typeof input.opening === "string" ? input.opening.trim() : void 0;
|
|
179
|
-
const
|
|
176
|
+
const openingScenes = openingText ? [{
|
|
180
177
|
id: "supplied-opening",
|
|
181
178
|
templateId: "media",
|
|
182
179
|
variables: {
|
|
@@ -187,7 +184,7 @@ function buildInitialConfig(input, audio, snapshotRetention, closerReserveSec, g
|
|
|
187
184
|
}] : [];
|
|
188
185
|
const scenes = [];
|
|
189
186
|
const warnings = [];
|
|
190
|
-
for (const scene of
|
|
187
|
+
for (const scene of openingScenes) {
|
|
191
188
|
const maxDurationSec = input.maxDurationSec ?? 30;
|
|
192
189
|
const readableMinimum = getReadableSceneDuration(scene, getTemplatePacing?.(scene.templateId));
|
|
193
190
|
if (maxDurationSec < readableMinimum) {
|
|
@@ -216,59 +213,83 @@ function buildInitialConfig(input, audio, snapshotRetention, closerReserveSec, g
|
|
|
216
213
|
if (snapshotRetention?.suppliedMediaUrls && input.suppliedMedia?.length) {
|
|
217
214
|
meta.uploadedMediaUrls = input.suppliedMedia.slice(0, MAX_RETAINED_MEDIA_URLS).map((item) => item.url);
|
|
218
215
|
}
|
|
219
|
-
return {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
216
|
+
return {
|
|
217
|
+
config: {
|
|
218
|
+
schemaVersion: VIDEO_SCHEMA_VERSION,
|
|
219
|
+
orientation: input.orientation ?? "portrait",
|
|
220
|
+
scenes,
|
|
221
|
+
...audio ? { audio } : {},
|
|
222
|
+
style: {
|
|
223
|
+
brand: resolveVideoBrand(input.brand),
|
|
224
|
+
density: input.style?.density ?? "normal",
|
|
225
|
+
motion: input.style?.motion ?? "normal",
|
|
226
|
+
defaultBackgroundEffect: input.style?.backgroundEffect ?? "slow-zoom-in",
|
|
227
|
+
defaultTextArchetype: input.style?.textArchetype ?? "subtle"
|
|
228
|
+
},
|
|
229
|
+
meta
|
|
230
230
|
},
|
|
231
|
-
|
|
232
|
-
}
|
|
231
|
+
warnings
|
|
232
|
+
};
|
|
233
233
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
234
|
+
|
|
235
|
+
// src/server/composition-setup.ts
|
|
236
|
+
function prepareComposition(rawInput, options, requestId) {
|
|
237
|
+
validateVideoInput(rawInput);
|
|
238
|
+
const requiresGeneratedScene = rawInput.opening == null || rawInput.opening === false;
|
|
239
|
+
const input = normalizeVideoInput(rawInput);
|
|
240
|
+
const request = createVideoRequest(input, {
|
|
241
|
+
requestId,
|
|
242
|
+
capabilities: options.capabilities
|
|
243
|
+
});
|
|
244
|
+
const initialAudio = input.audio === false ? void 0 : input.audio ? {
|
|
245
|
+
trackId: "soundtrack",
|
|
246
|
+
audioUrl: input.audio.src,
|
|
247
|
+
duration: input.maxDurationSec ?? 30,
|
|
248
|
+
beatDetection: { sensitivity: 0.5 },
|
|
249
|
+
beatMarkers: [],
|
|
250
|
+
volume: 1,
|
|
251
|
+
fadeOutMs: 3e3
|
|
252
|
+
} : options.selectAudio?.(input);
|
|
253
|
+
const closerReserveSec = getCloserReserve(
|
|
254
|
+
options.capabilities?.templates,
|
|
255
|
+
options.getTemplatePacing
|
|
256
|
+
);
|
|
257
|
+
const initial = buildInitialComposition(
|
|
258
|
+
input,
|
|
259
|
+
initialAudio,
|
|
260
|
+
options.snapshotRetention,
|
|
261
|
+
closerReserveSec,
|
|
262
|
+
options.getTemplatePacing
|
|
263
|
+
);
|
|
264
|
+
const initialConfig = initial.config;
|
|
265
|
+
for (let position = 0; position < initialConfig.scenes.length; position += 1) {
|
|
266
|
+
options.validateScene?.(initialConfig.scenes[position], {
|
|
267
|
+
input,
|
|
268
|
+
previousScenes: initialConfig.scenes.slice(0, position)
|
|
269
|
+
});
|
|
258
270
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
271
|
+
return {
|
|
272
|
+
requiresGeneratedScene,
|
|
273
|
+
input,
|
|
274
|
+
request,
|
|
275
|
+
initial,
|
|
276
|
+
initialConfig,
|
|
277
|
+
userPrompt: buildVideoUserPrompt(
|
|
278
|
+
input,
|
|
279
|
+
initialConfig.scenes.at(-1)?.timing.endTime ?? 0
|
|
280
|
+
),
|
|
281
|
+
closerReserveSec
|
|
282
|
+
};
|
|
264
283
|
}
|
|
265
|
-
|
|
284
|
+
|
|
285
|
+
// src/server/composition-warnings.ts
|
|
286
|
+
function cloneWarning(warning) {
|
|
266
287
|
return {
|
|
267
|
-
code:
|
|
268
|
-
category:
|
|
269
|
-
message:
|
|
270
|
-
sceneId,
|
|
271
|
-
recoverable:
|
|
288
|
+
code: warning.code,
|
|
289
|
+
category: warning.category,
|
|
290
|
+
message: warning.message,
|
|
291
|
+
...warning.sceneId != null ? { sceneId: warning.sceneId } : {},
|
|
292
|
+
recoverable: warning.recoverable
|
|
272
293
|
};
|
|
273
294
|
}
|
|
274
295
|
function createOmittedForCloserWarning(sceneId) {
|
|
@@ -311,9 +332,6 @@ function pendingCloserReserve(scene, getTemplatePacing) {
|
|
|
311
332
|
const requested = explicitRange ?? scene.timing.fixedDuration ?? metadata?.preferredDuration ?? readableMinimum;
|
|
312
333
|
return Math.max(readableMinimum, requested);
|
|
313
334
|
}
|
|
314
|
-
function sceneSlotDuration(scene) {
|
|
315
|
-
return Math.max(0, (scene.timing.endTime ?? 0) - (scene.timing.startTime ?? 0));
|
|
316
|
-
}
|
|
317
335
|
function createSceneQualityWarnings(scene) {
|
|
318
336
|
if (scene.templateId !== "barChart" || !Array.isArray(scene.variables.bars)) return [];
|
|
319
337
|
const values = scene.variables.bars.flatMap((bar) => {
|
|
@@ -330,47 +348,63 @@ function createSceneQualityWarnings(scene) {
|
|
|
330
348
|
recoverable: true
|
|
331
349
|
}];
|
|
332
350
|
}
|
|
351
|
+
|
|
352
|
+
// src/server/composition-runtime.ts
|
|
353
|
+
function invokeIsolated(callback, value) {
|
|
354
|
+
if (!callback) return;
|
|
355
|
+
try {
|
|
356
|
+
void Promise.resolve(callback(value)).catch(() => void 0);
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function monotonicNow() {
|
|
361
|
+
return globalThis.performance?.now?.() ?? Date.now();
|
|
362
|
+
}
|
|
363
|
+
function safeAbortReason(value) {
|
|
364
|
+
if (value instanceof DOMException && value.name === "TimeoutError") return "Request timed out";
|
|
365
|
+
if (value instanceof Error && value.name === "TimeoutError") return "Request timed out";
|
|
366
|
+
return safePublicDiagnostic(value instanceof Error ? value.message : value, "Request aborted");
|
|
367
|
+
}
|
|
368
|
+
function createId(prefix) {
|
|
369
|
+
const randomId = globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
370
|
+
return `${prefix}-${randomId}`;
|
|
371
|
+
}
|
|
372
|
+
function createProviderLifecycle() {
|
|
373
|
+
const results = [];
|
|
374
|
+
const sink = {
|
|
375
|
+
registerProviderResult(result) {
|
|
376
|
+
results.push(result);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const settle = async () => {
|
|
380
|
+
const resolved = await Promise.all(results);
|
|
381
|
+
const warnings = resolved.flatMap((result) => result.warnings);
|
|
382
|
+
const uniqueWarnings = [...new Map(warnings.map((warning) => [
|
|
383
|
+
`${warning.code}\0${warning.category}\0${warning.message}\0${warning.sceneId ?? ""}`,
|
|
384
|
+
warning
|
|
385
|
+
])).values()];
|
|
386
|
+
return resolved.reduce((summary, result) => ({
|
|
387
|
+
...summary,
|
|
388
|
+
...result,
|
|
389
|
+
warnings: uniqueWarnings
|
|
390
|
+
}), { warnings: uniqueWarnings });
|
|
391
|
+
};
|
|
392
|
+
return { sink, settle };
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/server/compose-video.ts
|
|
333
396
|
function createVideo(rawInput, options) {
|
|
334
|
-
validateInput(rawInput);
|
|
335
|
-
const requiresGeneratedScene = rawInput.opening == null || rawInput.opening === false;
|
|
336
|
-
const input = resolveVideoInput(rawInput);
|
|
337
397
|
const requestId = options.requestId ?? createId("request");
|
|
338
398
|
const runId = options.runId ?? createId("run");
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
capabilities: options.capabilities
|
|
342
|
-
});
|
|
343
|
-
const initialAudio = input.audio === false ? void 0 : input.audio ? {
|
|
344
|
-
trackId: "soundtrack",
|
|
345
|
-
audioUrl: input.audio.src,
|
|
346
|
-
duration: input.maxDurationSec ?? 30,
|
|
347
|
-
beatDetection: { sensitivity: 0.5 },
|
|
348
|
-
beatMarkers: [],
|
|
349
|
-
volume: 1,
|
|
350
|
-
fadeOutMs: 3e3
|
|
351
|
-
} : options.selectAudio?.(input);
|
|
352
|
-
const closerReserveSec = getCloserReserve(
|
|
353
|
-
options.capabilities?.templates,
|
|
354
|
-
options.getTemplatePacing
|
|
355
|
-
);
|
|
356
|
-
const initial = buildInitialConfig(
|
|
399
|
+
const {
|
|
400
|
+
requiresGeneratedScene,
|
|
357
401
|
input,
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
for (let position = 0; position < initialConfig.scenes.length; position += 1) {
|
|
365
|
-
options.validateScene?.(initialConfig.scenes[position], {
|
|
366
|
-
input,
|
|
367
|
-
previousScenes: initialConfig.scenes.slice(0, position)
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
const userPrompt = buildVideoUserPrompt(
|
|
371
|
-
input,
|
|
372
|
-
initialConfig.scenes.at(-1)?.timing.endTime ?? 0
|
|
373
|
-
);
|
|
402
|
+
request,
|
|
403
|
+
initial,
|
|
404
|
+
initialConfig,
|
|
405
|
+
userPrompt,
|
|
406
|
+
closerReserveSec
|
|
407
|
+
} = prepareComposition(rawInput, options, requestId);
|
|
374
408
|
const events = createVideoEventFactory({ runId });
|
|
375
409
|
const controller = new AbortController();
|
|
376
410
|
let abortReason = "Request aborted";
|
|
@@ -387,35 +421,17 @@ function createVideo(rawInput, options) {
|
|
|
387
421
|
resolveResult = resolve;
|
|
388
422
|
rejectResult = reject;
|
|
389
423
|
});
|
|
390
|
-
const startedAt =
|
|
424
|
+
const startedAt = monotonicNow();
|
|
391
425
|
let acceptedSceneCount = 0;
|
|
392
426
|
let rejectedSceneCount = 0;
|
|
393
427
|
let timeToFirstSceneMs;
|
|
394
|
-
const
|
|
395
|
-
const lifecycle = {
|
|
396
|
-
registerProviderResult(providerResult) {
|
|
397
|
-
providerResults.push(providerResult);
|
|
398
|
-
}
|
|
399
|
-
};
|
|
428
|
+
const { sink: lifecycle, settle: settleProviderLifecycle } = createProviderLifecycle();
|
|
400
429
|
const reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
401
430
|
const reportError = (error) => {
|
|
402
431
|
if (reportedErrors.has(error)) return;
|
|
403
432
|
reportedErrors.add(error);
|
|
404
433
|
invokeIsolated(options.onError, error);
|
|
405
434
|
};
|
|
406
|
-
const settleProviderLifecycle = async () => {
|
|
407
|
-
const results = await Promise.all(providerResults);
|
|
408
|
-
const warnings = results.flatMap((result2) => result2.warnings);
|
|
409
|
-
const uniqueWarnings = [...new Map(warnings.map((warning) => [
|
|
410
|
-
`${warning.code}\0${warning.category}\0${warning.message}\0${warning.sceneId ?? ""}`,
|
|
411
|
-
warning
|
|
412
|
-
])).values()];
|
|
413
|
-
return results.reduce((summary, result2) => ({
|
|
414
|
-
...summary,
|
|
415
|
-
...result2,
|
|
416
|
-
warnings: uniqueWarnings
|
|
417
|
-
}), { warnings: uniqueWarnings });
|
|
418
|
-
};
|
|
419
435
|
const eventSource = (async function* () {
|
|
420
436
|
let state = createVideoState();
|
|
421
437
|
const emit = (event) => {
|
|
@@ -456,11 +472,10 @@ function createVideo(rawInput, options) {
|
|
|
456
472
|
for (let position = 0; position < initialConfig.scenes.length; position += 1) {
|
|
457
473
|
yield emit(events.create("scene.add", {
|
|
458
474
|
scene: initialConfig.scenes[position],
|
|
459
|
-
position
|
|
460
|
-
revision: 0
|
|
475
|
+
position
|
|
461
476
|
}));
|
|
462
477
|
}
|
|
463
|
-
const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-
|
|
478
|
+
const systemPrompt = options.systemPrompt ?? (await import("./system-prompt-4I6Z5HK3.js")).createVideoSystemPrompt(input.knowledgeMode);
|
|
464
479
|
const context = { request, systemPrompt, userPrompt, initialConfig, signal: controller.signal };
|
|
465
480
|
attachGenerationLifecycleSink(context, lifecycle);
|
|
466
481
|
for await (const untrustedPart of options.generate(context)) {
|
|
@@ -530,154 +545,11 @@ function createVideo(rawInput, options) {
|
|
|
530
545
|
}
|
|
531
546
|
yield emit(events.create("scene.add", {
|
|
532
547
|
scene,
|
|
533
|
-
position: state.config?.scenes.length ?? 0
|
|
534
|
-
revision: 0
|
|
548
|
+
position: state.config?.scenes.length ?? 0
|
|
535
549
|
}));
|
|
536
550
|
generatedSceneCount += 1;
|
|
537
551
|
acceptedSceneCount += 1;
|
|
538
|
-
timeToFirstSceneMs ??= Math.max(0,
|
|
539
|
-
} else if (part.type === "scene.patch") {
|
|
540
|
-
if (pendingCloser?.id === part.sceneId) {
|
|
541
|
-
const nextPendingCloser = {
|
|
542
|
-
...pendingCloser,
|
|
543
|
-
...part.patch,
|
|
544
|
-
variables: part.patch.variables ? { ...pendingCloser.variables, ...part.patch.variables } : pendingCloser.variables,
|
|
545
|
-
timing: part.patch.timing ? { ...pendingCloser.timing, ...part.patch.timing } : pendingCloser.timing
|
|
546
|
-
};
|
|
547
|
-
options.validateScene?.(nextPendingCloser, {
|
|
548
|
-
input,
|
|
549
|
-
previousScenes: state.config?.scenes ?? []
|
|
550
|
-
});
|
|
551
|
-
pendingCloser = nextPendingCloser;
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
const scenes = state.config?.scenes ?? [];
|
|
555
|
-
const sceneIndex = scenes.findIndex((scene) => scene.id === part.sceneId);
|
|
556
|
-
if (sceneIndex < 0) throw new Error(`Scene ${part.sceneId} does not exist`);
|
|
557
|
-
if (part.patch.timing && sceneIndex < scenes.length - 1) {
|
|
558
|
-
yield emit(events.create("response.warning", {
|
|
559
|
-
warning: createPatchReadabilityWarning(part.sceneId)
|
|
560
|
-
}));
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
const currentScene = scenes[sceneIndex];
|
|
564
|
-
if (part.patch.timing?.startTime != null && Math.abs(part.patch.timing.startTime - (currentScene.timing.startTime ?? 0)) > 1e-6) {
|
|
565
|
-
yield emit(events.create("response.warning", {
|
|
566
|
-
warning: createPatchReadabilityWarning(part.sceneId)
|
|
567
|
-
}));
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
let timing;
|
|
571
|
-
if (part.patch.timing) {
|
|
572
|
-
const requestedTiming = { ...currentScene.timing, ...part.patch.timing };
|
|
573
|
-
if (part.patch.timing.startTime != null && part.patch.timing.endTime == null) {
|
|
574
|
-
delete requestedTiming.endTime;
|
|
575
|
-
}
|
|
576
|
-
if (part.patch.timing.fixedDuration != null && part.patch.timing.endTime == null) {
|
|
577
|
-
delete requestedTiming.endTime;
|
|
578
|
-
}
|
|
579
|
-
const pacedPatch = paceScene({
|
|
580
|
-
...currentScene,
|
|
581
|
-
...part.patch,
|
|
582
|
-
variables: part.patch.variables ? { ...currentScene.variables, ...part.patch.variables } : currentScene.variables,
|
|
583
|
-
timing: requestedTiming
|
|
584
|
-
}, {
|
|
585
|
-
previousScenes: scenes.slice(0, sceneIndex),
|
|
586
|
-
audio: state.config?.audio,
|
|
587
|
-
maxDurationSec: input.maxDurationSec ?? 30,
|
|
588
|
-
closerReserveSec: pendingCloser ? pendingCloserReserve(pendingCloser, options.getTemplatePacing) : closerReserveSec,
|
|
589
|
-
getTemplatePacing: options.getTemplatePacing
|
|
590
|
-
});
|
|
591
|
-
if (!pacedPatch.scene) {
|
|
592
|
-
yield emit(events.create("response.warning", {
|
|
593
|
-
warning: createPatchReadabilityWarning(part.sceneId)
|
|
594
|
-
}));
|
|
595
|
-
continue;
|
|
596
|
-
}
|
|
597
|
-
for (const warning of pacedPatch.warnings) {
|
|
598
|
-
yield emit(events.create("response.warning", { warning }));
|
|
599
|
-
}
|
|
600
|
-
timing = pacedPatch.scene.timing;
|
|
601
|
-
}
|
|
602
|
-
const patch = {
|
|
603
|
-
...part.patch,
|
|
604
|
-
...timing ? { timing } : {}
|
|
605
|
-
};
|
|
606
|
-
const nextScene = {
|
|
607
|
-
...currentScene,
|
|
608
|
-
...patch,
|
|
609
|
-
variables: patch.variables ? { ...currentScene.variables, ...patch.variables } : currentScene.variables,
|
|
610
|
-
timing: patch.timing ? { ...currentScene.timing, ...patch.timing } : currentScene.timing
|
|
611
|
-
};
|
|
612
|
-
options.validateScene?.(nextScene, { input, previousScenes: scenes });
|
|
613
|
-
if (getReadableSceneDuration(
|
|
614
|
-
nextScene,
|
|
615
|
-
options.getTemplatePacing?.(nextScene.templateId)
|
|
616
|
-
) > sceneSlotDuration(nextScene) + 1e-6) {
|
|
617
|
-
yield emit(events.create("response.warning", {
|
|
618
|
-
warning: createPatchReadabilityWarning(part.sceneId)
|
|
619
|
-
}));
|
|
620
|
-
continue;
|
|
621
|
-
}
|
|
622
|
-
yield emit(events.create("scene.patch", {
|
|
623
|
-
sceneId: part.sceneId,
|
|
624
|
-
revision: (state.sceneRevisions[part.sceneId] ?? -1) + 1,
|
|
625
|
-
patch
|
|
626
|
-
}));
|
|
627
|
-
} else if (part.type === "asset.patch") {
|
|
628
|
-
if (pendingCloser?.id === part.sceneId) {
|
|
629
|
-
const nextPendingCloser = {
|
|
630
|
-
...pendingCloser,
|
|
631
|
-
variables: { ...pendingCloser.variables, ...part.variables }
|
|
632
|
-
};
|
|
633
|
-
options.validateScene?.(nextPendingCloser, {
|
|
634
|
-
input,
|
|
635
|
-
previousScenes: state.config?.scenes ?? []
|
|
636
|
-
});
|
|
637
|
-
pendingCloser = nextPendingCloser;
|
|
638
|
-
continue;
|
|
639
|
-
}
|
|
640
|
-
const scenes = state.config?.scenes ?? [];
|
|
641
|
-
const scene = scenes.find((item) => item.id === part.sceneId);
|
|
642
|
-
if (!scene) throw new Error(`Scene ${part.sceneId} does not exist`);
|
|
643
|
-
const nextScene = {
|
|
644
|
-
...scene,
|
|
645
|
-
variables: { ...scene.variables, ...part.variables }
|
|
646
|
-
};
|
|
647
|
-
options.validateScene?.(nextScene, { input, previousScenes: scenes });
|
|
648
|
-
if (getReadableSceneDuration(
|
|
649
|
-
nextScene,
|
|
650
|
-
options.getTemplatePacing?.(nextScene.templateId)
|
|
651
|
-
) > sceneSlotDuration(nextScene) + 1e-6) {
|
|
652
|
-
yield emit(events.create("response.warning", {
|
|
653
|
-
warning: createPatchReadabilityWarning(part.sceneId)
|
|
654
|
-
}));
|
|
655
|
-
continue;
|
|
656
|
-
}
|
|
657
|
-
yield emit(events.create("asset.patch", {
|
|
658
|
-
sceneId: part.sceneId,
|
|
659
|
-
revision: (state.sceneRevisions[part.sceneId] ?? -1) + 1,
|
|
660
|
-
variables: part.variables
|
|
661
|
-
}));
|
|
662
|
-
} else if (part.type === "plan.error") {
|
|
663
|
-
const internalError = new Error(part.error.message);
|
|
664
|
-
reportError(internalError);
|
|
665
|
-
const terminal = !(part.error.recoverable ?? false);
|
|
666
|
-
const errorEvent = emit(events.create("response.error", {
|
|
667
|
-
error: {
|
|
668
|
-
code: "generation_failed",
|
|
669
|
-
message: "Video response generation failed",
|
|
670
|
-
recoverable: part.error.recoverable ?? false
|
|
671
|
-
},
|
|
672
|
-
terminal,
|
|
673
|
-
...terminal && state.config ? { snapshot: state.config } : {}
|
|
674
|
-
}));
|
|
675
|
-
if (terminal) {
|
|
676
|
-
finish(state);
|
|
677
|
-
yield errorEvent;
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
yield errorEvent;
|
|
552
|
+
timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
|
|
681
553
|
} else if (part.type === "plan.complete") {
|
|
682
554
|
if (planCompleted) throw new Error("The planner emitted plan.complete more than once");
|
|
683
555
|
if (pendingCloser) {
|
|
@@ -701,12 +573,11 @@ function createVideo(rawInput, options) {
|
|
|
701
573
|
if (pacedCloser.scene) {
|
|
702
574
|
yield emit(events.create("scene.add", {
|
|
703
575
|
scene: pacedCloser.scene,
|
|
704
|
-
position: state.config?.scenes.length ?? 0
|
|
705
|
-
revision: 0
|
|
576
|
+
position: state.config?.scenes.length ?? 0
|
|
706
577
|
}));
|
|
707
578
|
generatedSceneCount += 1;
|
|
708
579
|
acceptedSceneCount += 1;
|
|
709
|
-
timeToFirstSceneMs ??= Math.max(0,
|
|
580
|
+
timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
|
|
710
581
|
closerCommitted = true;
|
|
711
582
|
} else {
|
|
712
583
|
runtimeDurationLimited = true;
|
|
@@ -734,12 +605,11 @@ function createVideo(rawInput, options) {
|
|
|
734
605
|
}
|
|
735
606
|
yield emit(events.create("scene.add", {
|
|
736
607
|
scene: recovered.scene,
|
|
737
|
-
position: state.config?.scenes.length ?? 0
|
|
738
|
-
revision: 0
|
|
608
|
+
position: state.config?.scenes.length ?? 0
|
|
739
609
|
}));
|
|
740
610
|
generatedSceneCount += 1;
|
|
741
611
|
acceptedSceneCount += 1;
|
|
742
|
-
timeToFirstSceneMs ??= Math.max(0,
|
|
612
|
+
timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
|
|
743
613
|
}
|
|
744
614
|
}
|
|
745
615
|
if (options.requireCloser && !closerCommitted) {
|
|
@@ -802,7 +672,7 @@ function createVideo(rawInput, options) {
|
|
|
802
672
|
...provider.requestedModelId ? { requestedModelId: provider.requestedModelId } : {},
|
|
803
673
|
...provider.resolvedModelId ? { resolvedModelId: provider.resolvedModelId } : {},
|
|
804
674
|
...timeToFirstSceneMs != null ? { timeToFirstSceneMs } : {},
|
|
805
|
-
totalDurationMs: Math.max(0,
|
|
675
|
+
totalDurationMs: Math.max(0, monotonicNow() - startedAt),
|
|
806
676
|
acceptedSceneCount,
|
|
807
677
|
rejectedSceneCount,
|
|
808
678
|
videoDurationSec: snapshot.scenes.at(-1)?.timing.endTime ?? 0,
|
|
@@ -835,8 +705,7 @@ function createVideo(rawInput, options) {
|
|
|
835
705
|
if (pacedCloser.scene) {
|
|
836
706
|
yield emit(events.create("scene.add", {
|
|
837
707
|
scene: pacedCloser.scene,
|
|
838
|
-
position: state.config.scenes.length
|
|
839
|
-
revision: 0
|
|
708
|
+
position: state.config.scenes.length
|
|
840
709
|
}));
|
|
841
710
|
generatedSceneCount += 1;
|
|
842
711
|
acceptedSceneCount += 1;
|
|
@@ -874,44 +743,12 @@ function createVideo(rawInput, options) {
|
|
|
874
743
|
options.signal?.removeEventListener("abort", forwardAbort);
|
|
875
744
|
}
|
|
876
745
|
})();
|
|
877
|
-
const
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
const notify = () => {
|
|
882
|
-
for (const waiter of waiters) waiter();
|
|
883
|
-
waiters.clear();
|
|
884
|
-
};
|
|
885
|
-
void (async () => {
|
|
886
|
-
try {
|
|
887
|
-
for await (const event of eventSource) {
|
|
888
|
-
buffered.push(event);
|
|
889
|
-
notify();
|
|
890
|
-
}
|
|
891
|
-
} catch (cause) {
|
|
892
|
-
streamError = cause;
|
|
893
|
-
if (!settled) {
|
|
894
|
-
settled = true;
|
|
895
|
-
rejectResult(cause);
|
|
896
|
-
}
|
|
897
|
-
} finally {
|
|
898
|
-
streamDone = true;
|
|
899
|
-
notify();
|
|
746
|
+
const stream = createReplayableStream(eventSource, (cause) => {
|
|
747
|
+
if (!settled) {
|
|
748
|
+
settled = true;
|
|
749
|
+
rejectResult(cause);
|
|
900
750
|
}
|
|
901
|
-
})
|
|
902
|
-
const stream = {
|
|
903
|
-
async *[Symbol.asyncIterator]() {
|
|
904
|
-
let index = 0;
|
|
905
|
-
while (true) {
|
|
906
|
-
while (index < buffered.length) yield buffered[index++];
|
|
907
|
-
if (streamDone) {
|
|
908
|
-
if (streamError) throw streamError;
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
await new Promise((resolve) => waiters.add(resolve));
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
};
|
|
751
|
+
});
|
|
915
752
|
return {
|
|
916
753
|
request,
|
|
917
754
|
initialConfig,
|