@vanillaskyai/video 0.5.0 → 0.5.2
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 +11 -0
- package/README.md +0 -2
- package/dist/{chunk-3ROGEU5A.js → chunk-D6I2UQ5W.js} +229 -172
- package/dist/{compose-video-QJHKTOQ7.js → compose-video-KMQEDOI7.js} +1 -1
- package/dist/server.js +1 -1
- package/dist/test.js +1 -1
- package/examples/nextjs-quickstart/package.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ VanillaSky follows semantic versioning. This changelog begins with the 0.1 beta.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.5.2
|
|
8
|
+
|
|
9
|
+
- Refactors composition state transitions into a deterministic, directly
|
|
10
|
+
tested session engine without changing the public API, event stream,
|
|
11
|
+
checksums, or runtime behavior.
|
|
12
|
+
|
|
13
|
+
## 0.5.1
|
|
14
|
+
|
|
15
|
+
- Removes the redundant README version badge. Package registries and release
|
|
16
|
+
pages remain the authoritative source for the current version.
|
|
17
|
+
|
|
7
18
|
## 0.5.0
|
|
8
19
|
|
|
9
20
|
### Breaking changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# Give your AI a video output
|
|
2
2
|
|
|
3
|
-

|
|
4
|
-
|
|
5
3
|
**VanillaSky is the open-source video response layer.** Turn text, structured
|
|
6
4
|
data, and live application context into personalized video responses that start
|
|
7
5
|
playing while your LLM composes them.
|
|
@@ -349,6 +349,193 @@ function createSceneQualityWarnings(scene) {
|
|
|
349
349
|
}];
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
// src/server/composition-session.ts
|
|
353
|
+
var warningActions = (warnings) => warnings.map((warning) => ({ type: "response.warning", warning }));
|
|
354
|
+
function createCompositionSession() {
|
|
355
|
+
return {
|
|
356
|
+
acceptedSceneCount: 0,
|
|
357
|
+
rejectedSceneCount: 0,
|
|
358
|
+
planCompleted: false,
|
|
359
|
+
runtimeDurationLimited: false,
|
|
360
|
+
deferredForCloser: [],
|
|
361
|
+
closerCommitted: false,
|
|
362
|
+
plannerReportedLength: false,
|
|
363
|
+
finishReason: "stop"
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function pace(scene, video, options, closerReserveSec) {
|
|
367
|
+
return paceScene(scene, {
|
|
368
|
+
previousScenes: video.scenes,
|
|
369
|
+
audio: video.audio,
|
|
370
|
+
maxDurationSec: options.maxDurationSec,
|
|
371
|
+
closerReserveSec,
|
|
372
|
+
getTemplatePacing: options.getTemplatePacing
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function commitScene(session, result, includeQualityWarnings) {
|
|
376
|
+
return {
|
|
377
|
+
session: {
|
|
378
|
+
...session,
|
|
379
|
+
acceptedSceneCount: session.acceptedSceneCount + 1
|
|
380
|
+
},
|
|
381
|
+
actions: [
|
|
382
|
+
...warningActions(result.warnings),
|
|
383
|
+
...includeQualityWarnings ? warningActions(createSceneQualityWarnings(result.scene)) : [],
|
|
384
|
+
{ type: "scene.add", scene: result.scene }
|
|
385
|
+
]
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function completePlan(session, reportedFinishReason, requireCloser) {
|
|
389
|
+
const missingCloser = requireCloser && !session.closerCommitted;
|
|
390
|
+
const finishReason = session.runtimeDurationLimited ? "length" : reportedFinishReason != null && reportedFinishReason !== "stop" ? reportedFinishReason : missingCloser ? "other" : "stop";
|
|
391
|
+
return {
|
|
392
|
+
session: {
|
|
393
|
+
...session,
|
|
394
|
+
planCompleted: true,
|
|
395
|
+
plannerReportedLength: reportedFinishReason === "length",
|
|
396
|
+
finishReason
|
|
397
|
+
},
|
|
398
|
+
actions: missingCloser ? [{ type: "response.warning", warning: createMissingCloserWarning() }] : []
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function advanceComposition(session, part, video, options) {
|
|
402
|
+
if (session.planCompleted) {
|
|
403
|
+
throw new Error(part.type === "plan.complete" ? "The planner emitted plan.complete more than once" : "The planner emitted content after plan.complete");
|
|
404
|
+
}
|
|
405
|
+
if (part.type === "plan.complete") {
|
|
406
|
+
if (!session.pendingCloser) {
|
|
407
|
+
let next2 = { ...session, deferredForCloser: [] };
|
|
408
|
+
let currentVideo = video;
|
|
409
|
+
const actions2 = [];
|
|
410
|
+
for (const deferred of session.deferredForCloser) {
|
|
411
|
+
const recovered = pace(deferred, currentVideo, options, 0);
|
|
412
|
+
if (!recovered.scene) {
|
|
413
|
+
next2 = {
|
|
414
|
+
...next2,
|
|
415
|
+
rejectedSceneCount: next2.rejectedSceneCount + 1,
|
|
416
|
+
runtimeDurationLimited: true
|
|
417
|
+
};
|
|
418
|
+
actions2.push(...warningActions(recovered.warnings));
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const committed2 = commitScene(next2, { ...recovered, scene: recovered.scene }, true);
|
|
422
|
+
next2 = committed2.session;
|
|
423
|
+
actions2.push(...committed2.actions);
|
|
424
|
+
currentVideo = { ...currentVideo, scenes: [...currentVideo.scenes, recovered.scene] };
|
|
425
|
+
}
|
|
426
|
+
const completed2 = completePlan(next2, part.finishReason, options.requireCloser);
|
|
427
|
+
return {
|
|
428
|
+
session: completed2.session,
|
|
429
|
+
actions: [...actions2, ...completed2.actions]
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
let next = {
|
|
433
|
+
...session,
|
|
434
|
+
rejectedSceneCount: session.rejectedSceneCount + session.deferredForCloser.length,
|
|
435
|
+
runtimeDurationLimited: session.runtimeDurationLimited || session.deferredForCloser.length > 0,
|
|
436
|
+
deferredForCloser: []
|
|
437
|
+
};
|
|
438
|
+
const actions = session.deferredForCloser.map((scene) => ({
|
|
439
|
+
type: "response.warning",
|
|
440
|
+
warning: createOmittedForCloserWarning(scene.id)
|
|
441
|
+
}));
|
|
442
|
+
const closer = pace(session.pendingCloser, video, options, 0);
|
|
443
|
+
if (!closer.scene) {
|
|
444
|
+
next = {
|
|
445
|
+
...next,
|
|
446
|
+
rejectedSceneCount: next.rejectedSceneCount + 1,
|
|
447
|
+
runtimeDurationLimited: true
|
|
448
|
+
};
|
|
449
|
+
actions.push(...warningActions(closer.warnings));
|
|
450
|
+
const completed2 = completePlan(next, part.finishReason, options.requireCloser);
|
|
451
|
+
return {
|
|
452
|
+
session: completed2.session,
|
|
453
|
+
actions: [...actions, ...completed2.actions]
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
const committed = commitScene(next, { ...closer, scene: closer.scene }, false);
|
|
457
|
+
next = { ...committed.session, closerCommitted: true };
|
|
458
|
+
const completed = completePlan(next, part.finishReason, options.requireCloser);
|
|
459
|
+
return {
|
|
460
|
+
session: completed.session,
|
|
461
|
+
actions: [...actions, ...committed.actions, ...completed.actions]
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const metadata = options.getTemplatePacing?.(part.scene.templateId);
|
|
465
|
+
const isAsk = metadata?.jobs?.includes("ask") === true;
|
|
466
|
+
const isPayoff = metadata?.jobs?.includes("payoff") === true;
|
|
467
|
+
if (part.placement === "closer" && metadata && !isAsk && !isPayoff) {
|
|
468
|
+
throw new Error(`Scene template ${part.scene.templateId} cannot be used as a closer`);
|
|
469
|
+
}
|
|
470
|
+
if (part.placement === "closer" || isAsk) {
|
|
471
|
+
if (session.pendingCloser) {
|
|
472
|
+
return {
|
|
473
|
+
session: {
|
|
474
|
+
...session,
|
|
475
|
+
rejectedSceneCount: session.rejectedSceneCount + 1
|
|
476
|
+
},
|
|
477
|
+
actions: [{
|
|
478
|
+
type: "response.warning",
|
|
479
|
+
warning: createDuplicateCloserWarning()
|
|
480
|
+
}]
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
return { session: { ...session, pendingCloser: part.scene }, actions: [] };
|
|
484
|
+
}
|
|
485
|
+
if (session.deferredForCloser.length > 0) {
|
|
486
|
+
return {
|
|
487
|
+
session: { ...session, deferredForCloser: [...session.deferredForCloser, part.scene] },
|
|
488
|
+
actions: []
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
const paced = pace(
|
|
492
|
+
part.scene,
|
|
493
|
+
video,
|
|
494
|
+
options,
|
|
495
|
+
session.pendingCloser ? pendingCloserReserve(session.pendingCloser, options.getTemplatePacing) : options.closerReserveSec
|
|
496
|
+
);
|
|
497
|
+
if (!paced.scene) {
|
|
498
|
+
if (paced.warnings.some(({ code }) => code === "scene_omitted_for_closer")) {
|
|
499
|
+
return {
|
|
500
|
+
session: { ...session, deferredForCloser: [...session.deferredForCloser, part.scene] },
|
|
501
|
+
actions: []
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
session: {
|
|
506
|
+
...session,
|
|
507
|
+
rejectedSceneCount: session.rejectedSceneCount + 1,
|
|
508
|
+
runtimeDurationLimited: true
|
|
509
|
+
},
|
|
510
|
+
actions: warningActions(paced.warnings)
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return commitScene(session, { ...paced, scene: paced.scene }, true);
|
|
514
|
+
}
|
|
515
|
+
function rejectCompositionScene(session) {
|
|
516
|
+
return {
|
|
517
|
+
...session,
|
|
518
|
+
rejectedSceneCount: session.rejectedSceneCount + 1
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function recoverComposition(session, video, options) {
|
|
522
|
+
if (!session.pendingCloser || session.closerCommitted || video.scenes.length === 0) {
|
|
523
|
+
return { session, actions: [] };
|
|
524
|
+
}
|
|
525
|
+
const closer = pace(session.pendingCloser, video, options, 0);
|
|
526
|
+
if (!closer.scene) {
|
|
527
|
+
return {
|
|
528
|
+
session,
|
|
529
|
+
actions: warningActions(closer.warnings)
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
const committed = commitScene(session, { ...closer, scene: closer.scene }, false);
|
|
533
|
+
return {
|
|
534
|
+
session: { ...committed.session, closerCommitted: true },
|
|
535
|
+
actions: committed.actions
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
352
539
|
// src/server/composition-runtime.ts
|
|
353
540
|
function invokeIsolated(callback, value) {
|
|
354
541
|
if (!callback) return;
|
|
@@ -422,8 +609,6 @@ function createVideo(rawInput, options) {
|
|
|
422
609
|
rejectResult = reject;
|
|
423
610
|
});
|
|
424
611
|
const startedAt = monotonicNow();
|
|
425
|
-
let acceptedSceneCount = 0;
|
|
426
|
-
let rejectedSceneCount = 0;
|
|
427
612
|
let timeToFirstSceneMs;
|
|
428
613
|
const { sink: lifecycle, settle: settleProviderLifecycle } = createProviderLifecycle();
|
|
429
614
|
const reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
@@ -434,6 +619,13 @@ function createVideo(rawInput, options) {
|
|
|
434
619
|
};
|
|
435
620
|
const eventSource = (async function* () {
|
|
436
621
|
let state = createVideoState();
|
|
622
|
+
let composition = createCompositionSession();
|
|
623
|
+
const compositionOptions = {
|
|
624
|
+
maxDurationSec: input.maxDurationSec ?? 30,
|
|
625
|
+
closerReserveSec,
|
|
626
|
+
requireCloser: options.requireCloser ?? false,
|
|
627
|
+
getTemplatePacing: options.getTemplatePacing
|
|
628
|
+
};
|
|
437
629
|
const emit = (event) => {
|
|
438
630
|
state = applyVideoEvent(state, event);
|
|
439
631
|
if (event.type === "response.warning") {
|
|
@@ -442,21 +634,23 @@ function createVideo(rawInput, options) {
|
|
|
442
634
|
options.onEvent?.(event);
|
|
443
635
|
return event;
|
|
444
636
|
};
|
|
637
|
+
const emitCompositionAction = (action) => {
|
|
638
|
+
if (action.type === "response.warning") {
|
|
639
|
+
return emit(events.create("response.warning", { warning: action.warning }));
|
|
640
|
+
}
|
|
641
|
+
const event = emit(events.create("scene.add", {
|
|
642
|
+
scene: action.scene,
|
|
643
|
+
position: state.config?.scenes.length ?? 0
|
|
644
|
+
}));
|
|
645
|
+
timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
|
|
646
|
+
return event;
|
|
647
|
+
};
|
|
445
648
|
const finish = (finalState) => {
|
|
446
649
|
if (!settled) {
|
|
447
650
|
settled = true;
|
|
448
651
|
resolveResult(finalState);
|
|
449
652
|
}
|
|
450
653
|
};
|
|
451
|
-
let planCompleted = false;
|
|
452
|
-
let generatedSceneCount = 0;
|
|
453
|
-
let runtimeDurationLimited = false;
|
|
454
|
-
let pendingCloser;
|
|
455
|
-
const deferredForCloser = [];
|
|
456
|
-
let plannerReportedLength = false;
|
|
457
|
-
let closerCommitted = false;
|
|
458
|
-
let missingCloser = false;
|
|
459
|
-
let finishReason = "stop";
|
|
460
654
|
try {
|
|
461
655
|
yield emit(events.create("response.start", {
|
|
462
656
|
requestId,
|
|
@@ -484,8 +678,8 @@ function createVideo(rawInput, options) {
|
|
|
484
678
|
const part = resolveSuppliedMediaPlanPart(parseVideoPlanPart(untrustedPart), input);
|
|
485
679
|
attemptedScene = part.type === "scene.add";
|
|
486
680
|
if (controller.signal.aborted) throw controller.signal.reason ?? new Error(abortReason);
|
|
487
|
-
if (planCompleted
|
|
488
|
-
throw new Error("The planner emitted content after plan.complete");
|
|
681
|
+
if (composition.planCompleted) {
|
|
682
|
+
throw new Error(part.type === "plan.complete" ? "The planner emitted plan.complete more than once" : "The planner emitted content after plan.complete");
|
|
489
683
|
}
|
|
490
684
|
if (part.type === "scene.add") {
|
|
491
685
|
if (options.capabilities?.templates != null && !options.capabilities.templates.includes(part.scene.templateId)) {
|
|
@@ -495,138 +689,16 @@ function createVideo(rawInput, options) {
|
|
|
495
689
|
input,
|
|
496
690
|
previousScenes: state.config?.scenes ?? []
|
|
497
691
|
});
|
|
498
|
-
const templatePacing = options.getTemplatePacing?.(part.scene.templateId);
|
|
499
|
-
const isAskCandidate = templatePacing?.jobs?.includes("ask") === true;
|
|
500
|
-
const isPayoffCandidate = templatePacing?.jobs?.includes("payoff") === true;
|
|
501
|
-
if (part.placement === "closer" && templatePacing && !isAskCandidate && !isPayoffCandidate) {
|
|
502
|
-
throw new Error(`Scene template ${part.scene.templateId} cannot be used as a closer`);
|
|
503
|
-
}
|
|
504
|
-
const isCloserCandidate = part.placement === "closer" || isAskCandidate;
|
|
505
|
-
if (isCloserCandidate) {
|
|
506
|
-
if (pendingCloser) {
|
|
507
|
-
rejectedSceneCount += 1;
|
|
508
|
-
yield emit(events.create("response.warning", {
|
|
509
|
-
warning: createDuplicateCloserWarning()
|
|
510
|
-
}));
|
|
511
|
-
} else {
|
|
512
|
-
pendingCloser = part.scene;
|
|
513
|
-
}
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
if (deferredForCloser.length) {
|
|
517
|
-
deferredForCloser.push(part.scene);
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
|
-
const paced = paceScene(part.scene, {
|
|
521
|
-
previousScenes: state.config?.scenes ?? [],
|
|
522
|
-
audio: state.config?.audio,
|
|
523
|
-
maxDurationSec: input.maxDurationSec ?? 30,
|
|
524
|
-
closerReserveSec: pendingCloser ? pendingCloserReserve(pendingCloser, options.getTemplatePacing) : closerReserveSec,
|
|
525
|
-
getTemplatePacing: options.getTemplatePacing
|
|
526
|
-
});
|
|
527
|
-
if (!paced.scene) {
|
|
528
|
-
if (paced.warnings.some(({ code }) => code === "scene_omitted_for_closer")) {
|
|
529
|
-
deferredForCloser.push(part.scene);
|
|
530
|
-
continue;
|
|
531
|
-
}
|
|
532
|
-
for (const warning of paced.warnings) {
|
|
533
|
-
yield emit(events.create("response.warning", { warning }));
|
|
534
|
-
}
|
|
535
|
-
runtimeDurationLimited = true;
|
|
536
|
-
rejectedSceneCount += 1;
|
|
537
|
-
continue;
|
|
538
|
-
}
|
|
539
|
-
const scene = paced.scene;
|
|
540
|
-
for (const warning of paced.warnings) {
|
|
541
|
-
yield emit(events.create("response.warning", { warning }));
|
|
542
|
-
}
|
|
543
|
-
for (const warning of createSceneQualityWarnings(scene)) {
|
|
544
|
-
yield emit(events.create("response.warning", { warning }));
|
|
545
|
-
}
|
|
546
|
-
yield emit(events.create("scene.add", {
|
|
547
|
-
scene,
|
|
548
|
-
position: state.config?.scenes.length ?? 0
|
|
549
|
-
}));
|
|
550
|
-
generatedSceneCount += 1;
|
|
551
|
-
acceptedSceneCount += 1;
|
|
552
|
-
timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
|
|
553
|
-
} else if (part.type === "plan.complete") {
|
|
554
|
-
if (planCompleted) throw new Error("The planner emitted plan.complete more than once");
|
|
555
|
-
if (pendingCloser) {
|
|
556
|
-
if (deferredForCloser.length) runtimeDurationLimited = true;
|
|
557
|
-
for (const deferred of deferredForCloser.splice(0)) {
|
|
558
|
-
rejectedSceneCount += 1;
|
|
559
|
-
yield emit(events.create("response.warning", {
|
|
560
|
-
warning: createOmittedForCloserWarning(deferred.id)
|
|
561
|
-
}));
|
|
562
|
-
}
|
|
563
|
-
const pacedCloser = paceScene(pendingCloser, {
|
|
564
|
-
previousScenes: state.config?.scenes ?? [],
|
|
565
|
-
audio: state.config?.audio,
|
|
566
|
-
maxDurationSec: input.maxDurationSec ?? 30,
|
|
567
|
-
closerReserveSec: 0,
|
|
568
|
-
getTemplatePacing: options.getTemplatePacing
|
|
569
|
-
});
|
|
570
|
-
for (const warning of pacedCloser.warnings) {
|
|
571
|
-
yield emit(events.create("response.warning", { warning }));
|
|
572
|
-
}
|
|
573
|
-
if (pacedCloser.scene) {
|
|
574
|
-
yield emit(events.create("scene.add", {
|
|
575
|
-
scene: pacedCloser.scene,
|
|
576
|
-
position: state.config?.scenes.length ?? 0
|
|
577
|
-
}));
|
|
578
|
-
generatedSceneCount += 1;
|
|
579
|
-
acceptedSceneCount += 1;
|
|
580
|
-
timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
|
|
581
|
-
closerCommitted = true;
|
|
582
|
-
} else {
|
|
583
|
-
runtimeDurationLimited = true;
|
|
584
|
-
rejectedSceneCount += 1;
|
|
585
|
-
}
|
|
586
|
-
} else if (deferredForCloser.length) {
|
|
587
|
-
for (const deferred of deferredForCloser.splice(0)) {
|
|
588
|
-
const recovered = paceScene(deferred, {
|
|
589
|
-
previousScenes: state.config?.scenes ?? [],
|
|
590
|
-
audio: state.config?.audio,
|
|
591
|
-
maxDurationSec: input.maxDurationSec ?? 30,
|
|
592
|
-
closerReserveSec: 0,
|
|
593
|
-
getTemplatePacing: options.getTemplatePacing
|
|
594
|
-
});
|
|
595
|
-
for (const warning of recovered.warnings) {
|
|
596
|
-
yield emit(events.create("response.warning", { warning }));
|
|
597
|
-
}
|
|
598
|
-
if (!recovered.scene) {
|
|
599
|
-
runtimeDurationLimited = true;
|
|
600
|
-
rejectedSceneCount += 1;
|
|
601
|
-
continue;
|
|
602
|
-
}
|
|
603
|
-
for (const warning of createSceneQualityWarnings(recovered.scene)) {
|
|
604
|
-
yield emit(events.create("response.warning", { warning }));
|
|
605
|
-
}
|
|
606
|
-
yield emit(events.create("scene.add", {
|
|
607
|
-
scene: recovered.scene,
|
|
608
|
-
position: state.config?.scenes.length ?? 0
|
|
609
|
-
}));
|
|
610
|
-
generatedSceneCount += 1;
|
|
611
|
-
acceptedSceneCount += 1;
|
|
612
|
-
timeToFirstSceneMs ??= Math.max(0, monotonicNow() - startedAt);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
if (options.requireCloser && !closerCommitted) {
|
|
616
|
-
missingCloser = true;
|
|
617
|
-
yield emit(events.create("response.warning", {
|
|
618
|
-
warning: createMissingCloserWarning()
|
|
619
|
-
}));
|
|
620
|
-
}
|
|
621
|
-
planCompleted = true;
|
|
622
|
-
const reportedFinishReason = part.finishReason ?? "stop";
|
|
623
|
-
finishReason = runtimeDurationLimited ? "length" : reportedFinishReason !== "stop" ? reportedFinishReason : missingCloser ? "other" : "stop";
|
|
624
|
-
plannerReportedLength = part.finishReason === "length";
|
|
625
|
-
continue;
|
|
626
692
|
}
|
|
693
|
+
if (!state.config) throw new Error("response.start did not initialize composition state");
|
|
694
|
+
const transition = advanceComposition(composition, part, state.config, compositionOptions);
|
|
695
|
+
for (const action of transition.actions) yield emitCompositionAction(action);
|
|
696
|
+
composition = transition.session;
|
|
627
697
|
} catch (cause) {
|
|
628
698
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
629
|
-
if (attemptedScene || untrustedPart != null && typeof untrustedPart === "object" && untrustedPart.type === "scene.add")
|
|
699
|
+
if (attemptedScene || untrustedPart != null && typeof untrustedPart === "object" && untrustedPart.type === "scene.add") {
|
|
700
|
+
composition = rejectCompositionScene(composition);
|
|
701
|
+
}
|
|
630
702
|
reportError(error);
|
|
631
703
|
if ((options.invalidPartBehavior ?? "fail") === "fail") throw error;
|
|
632
704
|
yield emit(events.create("response.error", {
|
|
@@ -646,35 +718,35 @@ function createVideo(rawInput, options) {
|
|
|
646
718
|
for (const warning of provider.warnings) {
|
|
647
719
|
yield emit(events.create("response.warning", { warning }));
|
|
648
720
|
}
|
|
649
|
-
if (!planCompleted) throw new Error("The planner stream ended before plan.complete");
|
|
650
|
-
if (plannerReportedLength &&
|
|
721
|
+
if (!composition.planCompleted) throw new Error("The planner stream ended before plan.complete");
|
|
722
|
+
if (composition.plannerReportedLength && composition.acceptedSceneCount === 0) {
|
|
651
723
|
throw new Error("The planner was truncated before adding a generated scene");
|
|
652
724
|
}
|
|
653
|
-
if (plannerReportedLength) {
|
|
725
|
+
if (composition.plannerReportedLength) {
|
|
654
726
|
yield emit(events.create("response.warning", {
|
|
655
727
|
warning: createIncompletePlanWarning()
|
|
656
728
|
}));
|
|
657
729
|
}
|
|
658
|
-
if (
|
|
730
|
+
if (composition.acceptedSceneCount === 0 && requiresGeneratedScene) {
|
|
659
731
|
throw new Error("The planner completed without adding a scene");
|
|
660
732
|
}
|
|
661
733
|
const snapshot = parseVideo(state.config);
|
|
662
734
|
const completeEvent = emit(events.create("response.complete", {
|
|
663
|
-
finishReason,
|
|
735
|
+
finishReason: composition.finishReason,
|
|
664
736
|
snapshot,
|
|
665
737
|
checksum: checksumVideo(snapshot)
|
|
666
738
|
}));
|
|
667
739
|
finish(state);
|
|
668
740
|
const summary = {
|
|
669
|
-
finishReason,
|
|
741
|
+
finishReason: composition.finishReason,
|
|
670
742
|
...provider.usage ? { usage: provider.usage } : {},
|
|
671
743
|
...provider.providerMetadata !== void 0 ? { providerMetadata: provider.providerMetadata } : {},
|
|
672
744
|
...provider.requestedModelId ? { requestedModelId: provider.requestedModelId } : {},
|
|
673
745
|
...provider.resolvedModelId ? { resolvedModelId: provider.resolvedModelId } : {},
|
|
674
746
|
...timeToFirstSceneMs != null ? { timeToFirstSceneMs } : {},
|
|
675
747
|
totalDurationMs: Math.max(0, monotonicNow() - startedAt),
|
|
676
|
-
acceptedSceneCount,
|
|
677
|
-
rejectedSceneCount,
|
|
748
|
+
acceptedSceneCount: composition.acceptedSceneCount,
|
|
749
|
+
rejectedSceneCount: composition.rejectedSceneCount,
|
|
678
750
|
videoDurationSec: snapshot.scenes.at(-1)?.timing.endTime ?? 0,
|
|
679
751
|
warnings: state.warnings.map(cloneWarning)
|
|
680
752
|
};
|
|
@@ -691,30 +763,16 @@ function createVideo(rawInput, options) {
|
|
|
691
763
|
return;
|
|
692
764
|
}
|
|
693
765
|
try {
|
|
694
|
-
if (
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
maxDurationSec: input.maxDurationSec ?? 30,
|
|
699
|
-
closerReserveSec: 0,
|
|
700
|
-
getTemplatePacing: options.getTemplatePacing
|
|
701
|
-
});
|
|
702
|
-
for (const warning of pacedCloser.warnings) {
|
|
703
|
-
yield emit(events.create("response.warning", { warning }));
|
|
704
|
-
}
|
|
705
|
-
if (pacedCloser.scene) {
|
|
706
|
-
yield emit(events.create("scene.add", {
|
|
707
|
-
scene: pacedCloser.scene,
|
|
708
|
-
position: state.config.scenes.length
|
|
709
|
-
}));
|
|
710
|
-
generatedSceneCount += 1;
|
|
711
|
-
acceptedSceneCount += 1;
|
|
712
|
-
closerCommitted = true;
|
|
713
|
-
}
|
|
766
|
+
if (state.config) {
|
|
767
|
+
const recovery = recoverComposition(composition, state.config, compositionOptions);
|
|
768
|
+
for (const action of recovery.actions) yield emitCompositionAction(action);
|
|
769
|
+
composition = recovery.session;
|
|
714
770
|
}
|
|
715
771
|
const provider = await settleProviderLifecycle();
|
|
716
772
|
for (const warning of provider.warnings) {
|
|
717
|
-
if (!state.warnings.some(
|
|
773
|
+
if (!state.warnings.some(
|
|
774
|
+
(existing) => existing.code === warning.code && existing.message === warning.message
|
|
775
|
+
)) {
|
|
718
776
|
yield emit(events.create("response.warning", { warning }));
|
|
719
777
|
}
|
|
720
778
|
}
|
|
@@ -752,7 +810,6 @@ function createVideo(rawInput, options) {
|
|
|
752
810
|
return {
|
|
753
811
|
request,
|
|
754
812
|
initialConfig,
|
|
755
|
-
initialStyle: initialConfig.style,
|
|
756
813
|
stream,
|
|
757
814
|
result,
|
|
758
815
|
abort(reason = "user cancelled") {
|
package/dist/server.js
CHANGED
package/dist/test.js
CHANGED
|
@@ -233,7 +233,7 @@ async function* simulateVideoStream(parts, options = {}) {
|
|
|
233
233
|
if (timeoutMs != null && (!Number.isFinite(timeoutMs) || timeoutMs < 0)) {
|
|
234
234
|
throw new Error("Simulation timeoutMs must be a non-negative finite number");
|
|
235
235
|
}
|
|
236
|
-
const { createVideo } = await import("./compose-video-
|
|
236
|
+
const { createVideo } = await import("./compose-video-KMQEDOI7.js");
|
|
237
237
|
const { createTextDeltaVideoPlanner } = await import("./text-stream-J6EDDJP4.js");
|
|
238
238
|
const { BUILTIN_SERVER_TEMPLATE_KIT } = await import("./builtin-server-YHEZ2JRF.js");
|
|
239
239
|
const { createTemplateSceneValidator } = await import("./validate-T7GBU2YF.js");
|