@vanillaskyai/video 0.4.0 → 0.5.0
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 +89 -0
- package/PUBLIC-API.md +12 -5
- package/README.md +14 -17
- 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 +7 -6
- package/dist/react.js +1000 -349
- 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/live-channels.md +5 -2
- package/docs/media-and-audio.md +7 -0
- 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
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as VideoGenerationContext, o as VideoPlanner, p as VideoRequest, a as VideoCapabilities, j as VideoInput, d as VideoAudio, q as VideoSceneValidator, r as VideoTemplatePacing, s as VideoSnapshotRetention, t as VideoResumeCursor, u as VideoSceneValidationContext, e as VideoScene } from './types-
|
|
2
|
-
import {
|
|
3
|
-
export { c as VideoWarningCategory } from './events-
|
|
1
|
+
import { n as VideoGenerationContext, o as VideoPlanner, p as VideoRequest, a as VideoCapabilities, j as VideoInput, d as VideoAudio, q as VideoSceneValidator, r as VideoTemplatePacing, s as VideoSnapshotRetention, t as VideoResumeCursor, u as VideoSceneValidationContext, e as VideoScene } from './types-BV9IqExh.js';
|
|
2
|
+
import { a as VideoFinishReason, V as VideoWarning, b as VideoEvent } from './events-BBP30j3c.js';
|
|
3
|
+
export { c as VideoWarningCategory } from './events-BBP30j3c.js';
|
|
4
4
|
import { S as SceneTemplateMetadata } from './catalog-types-BIhSpOWK.js';
|
|
5
5
|
|
|
6
6
|
interface VideoProviderUsage {
|
package/dist/server.js
CHANGED
|
@@ -1,41 +1,46 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createTextDeltaVideoPlanner
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-34O5BY6X.js";
|
|
4
4
|
import {
|
|
5
5
|
createVideo
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-3ROGEU5A.js";
|
|
7
7
|
import "./chunk-E7CL7UPB.js";
|
|
8
8
|
import {
|
|
9
9
|
BUILTIN_SERVER_TEMPLATE_KIT,
|
|
10
10
|
createServerTemplateRegistry,
|
|
11
11
|
overlayServerTemplateRegistry
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FVTMYS6U.js";
|
|
13
13
|
import {
|
|
14
14
|
createTemplateSceneValidator
|
|
15
15
|
} from "./chunk-LMG3GHLL.js";
|
|
16
16
|
import {
|
|
17
17
|
encodeVideoSseEvent,
|
|
18
18
|
videoSseHeaders
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
19
|
+
} from "./chunk-ZQQQAAKP.js";
|
|
20
|
+
import "./chunk-FZHMQFG3.js";
|
|
21
21
|
import {
|
|
22
|
+
allowedKeys,
|
|
23
|
+
jsonValue,
|
|
24
|
+
nonNegativeInteger,
|
|
22
25
|
parseVideoEvent,
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
record,
|
|
27
|
+
safePublicDiagnostic,
|
|
28
|
+
string
|
|
29
|
+
} from "./chunk-G66Z5CWR.js";
|
|
25
30
|
import {
|
|
26
31
|
MAX_RETAINED_MEDIA_URL_LENGTH
|
|
27
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-66MNRUCR.js";
|
|
28
33
|
import {
|
|
29
34
|
VIDEO_PROTOCOL_VERSION
|
|
30
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-AOWSXU2K.js";
|
|
31
36
|
import {
|
|
32
37
|
createTemplateSystemPrompt,
|
|
33
38
|
getStandardMediaResolverContract
|
|
34
|
-
} from "./chunk-
|
|
35
|
-
import "./chunk-
|
|
39
|
+
} from "./chunk-EFL34TXF.js";
|
|
40
|
+
import "./chunk-PNS52FL4.js";
|
|
36
41
|
import "./chunk-3O7OMMMF.js";
|
|
42
|
+
import "./chunk-3RV4YKB3.js";
|
|
37
43
|
import "./chunk-352ZZCET.js";
|
|
38
|
-
import "./chunk-XAMUOSX7.js";
|
|
39
44
|
import "./chunk-2E6T633S.js";
|
|
40
45
|
import {
|
|
41
46
|
validateBrandInput
|
|
@@ -43,23 +48,6 @@ import {
|
|
|
43
48
|
import "./chunk-73NTSFFI.js";
|
|
44
49
|
|
|
45
50
|
// src/server/request-validation.ts
|
|
46
|
-
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
47
|
-
function record(value, path) {
|
|
48
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
49
|
-
throw new Error(`${path} must be an object`);
|
|
50
|
-
}
|
|
51
|
-
return value;
|
|
52
|
-
}
|
|
53
|
-
function allowedKeys(value, allowed, path) {
|
|
54
|
-
const permitted = new Set(allowed);
|
|
55
|
-
for (const key of Object.keys(value)) {
|
|
56
|
-
if (!permitted.has(key)) throw new Error(`${path} contains unsupported field ${key}`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
function string(value, path) {
|
|
60
|
-
if (typeof value !== "string" || !value.trim()) throw new Error(`${path} must be a non-empty string`);
|
|
61
|
-
return value;
|
|
62
|
-
}
|
|
63
51
|
function boundedString(value, path) {
|
|
64
52
|
const result = string(value, path);
|
|
65
53
|
if (result.length > MAX_RETAINED_MEDIA_URL_LENGTH) {
|
|
@@ -67,12 +55,6 @@ function boundedString(value, path) {
|
|
|
67
55
|
}
|
|
68
56
|
return result;
|
|
69
57
|
}
|
|
70
|
-
function nonNegativeInteger(value, path) {
|
|
71
|
-
if (!Number.isInteger(value) || Number(value) < 0) {
|
|
72
|
-
throw new Error(`${path} must be a non-negative integer`);
|
|
73
|
-
}
|
|
74
|
-
return Number(value);
|
|
75
|
-
}
|
|
76
58
|
function numberBetween(value, minimum, maximum, path) {
|
|
77
59
|
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum || value > maximum) {
|
|
78
60
|
throw new Error(`${path} must be a number between ${minimum} and ${maximum}`);
|
|
@@ -85,24 +67,6 @@ function enumValue(value, allowed, description, path) {
|
|
|
85
67
|
}
|
|
86
68
|
return value;
|
|
87
69
|
}
|
|
88
|
-
function jsonValue(value, path, seen = /* @__PURE__ */ new Set()) {
|
|
89
|
-
if (value == null || typeof value === "string" || typeof value === "boolean") return;
|
|
90
|
-
if (typeof value === "number") {
|
|
91
|
-
if (!Number.isFinite(value)) throw new Error(`${path} must be finite`);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (typeof value !== "object" || seen.has(value)) throw new Error(`${path} must be JSON-serializable`);
|
|
95
|
-
seen.add(value);
|
|
96
|
-
if (Array.isArray(value)) {
|
|
97
|
-
value.forEach((item, index) => jsonValue(item, `${path}[${index}]`, seen));
|
|
98
|
-
} else {
|
|
99
|
-
for (const [key, child] of Object.entries(value)) {
|
|
100
|
-
if (DANGEROUS_KEYS.has(key)) throw new Error(`${path} contains unsupported field ${key}`);
|
|
101
|
-
jsonValue(child, `${path}.${key}`, seen);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
seen.delete(value);
|
|
105
|
-
}
|
|
106
70
|
function capabilities(value, path) {
|
|
107
71
|
const result = record(value, path);
|
|
108
72
|
allowedKeys(result, ["templates", "extensions"], path);
|
|
@@ -553,12 +517,6 @@ function sanitizeUnknownTemplatePart(part) {
|
|
|
553
517
|
if (part.type === "scene.add") {
|
|
554
518
|
return { ...part, scene: { ...part.scene, variables: withoutKeyword(part.scene.variables) } };
|
|
555
519
|
}
|
|
556
|
-
if (part.type === "scene.patch" && part.patch.variables) {
|
|
557
|
-
return { ...part, patch: { ...part.patch, variables: withoutKeyword(part.patch.variables) } };
|
|
558
|
-
}
|
|
559
|
-
if (part.type === "asset.patch") {
|
|
560
|
-
return { ...part, variables: withoutKeyword(part.variables) };
|
|
561
|
-
}
|
|
562
520
|
return part;
|
|
563
521
|
}
|
|
564
522
|
async function resolvePartVariables(options) {
|
|
@@ -579,33 +537,14 @@ async function resolvePartVariables(options) {
|
|
|
579
537
|
});
|
|
580
538
|
return { ...options.part, scene: { ...options.part.scene, variables } };
|
|
581
539
|
}
|
|
582
|
-
if (options.part.type === "scene.patch" && options.part.patch.variables) {
|
|
583
|
-
const variables = await resolveVariables({
|
|
584
|
-
...shared,
|
|
585
|
-
variables: options.part.patch.variables
|
|
586
|
-
});
|
|
587
|
-
return { ...options.part, patch: { ...options.part.patch, variables } };
|
|
588
|
-
}
|
|
589
|
-
if (options.part.type === "asset.patch") {
|
|
590
|
-
const variables = await resolveVariables({
|
|
591
|
-
...shared,
|
|
592
|
-
variables: options.part.variables
|
|
593
|
-
});
|
|
594
|
-
return { ...options.part, variables };
|
|
595
|
-
}
|
|
596
540
|
return options.part;
|
|
597
541
|
}
|
|
598
542
|
function createMediaResolvingPlanner(options) {
|
|
599
543
|
return async function* resolveMediaPlan(context) {
|
|
600
|
-
const sceneTemplates = /* @__PURE__ */ new Map();
|
|
601
544
|
for await (const part of options.planner(context)) {
|
|
602
|
-
const templateId = part.type === "scene.add" ? part.scene.templateId : part.type === "scene.patch" || part.type === "asset.patch" ? sceneTemplates.get(part.sceneId) : void 0;
|
|
603
|
-
if (part.type === "scene.add") {
|
|
604
|
-
sceneTemplates.set(part.scene.id, part.scene.templateId);
|
|
605
|
-
}
|
|
606
545
|
yield await resolvePartVariables({
|
|
607
546
|
part,
|
|
608
|
-
templateId,
|
|
547
|
+
templateId: part.type === "scene.add" ? part.scene.templateId : void 0,
|
|
609
548
|
input: context.request.input,
|
|
610
549
|
signal: context.signal,
|
|
611
550
|
templates: options.templates,
|
package/dist/template-catalog.js
CHANGED
package/dist/templates.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { I as InferTemplateJsonSchema, S as SceneTemplateProps, a as SceneTemplate } from './kit-
|
|
2
|
-
export { T as TemplateRegistry, c as createTemplateRegistry } from './kit-
|
|
1
|
+
import { I as InferTemplateJsonSchema, S as SceneTemplateProps, a as SceneTemplate } from './kit-DEG3fcaL.js';
|
|
2
|
+
export { T as TemplateRegistry, c as createTemplateRegistry } from './kit-DEG3fcaL.js';
|
|
3
3
|
import { ComponentType } from 'react';
|
|
4
4
|
import { d as TemplateJsonSchema, S as SceneTemplateMetadata } from './catalog-types-BIhSpOWK.js';
|
|
5
5
|
export { T as TemplateFamily, c as TemplateTimingMetadata, e as TemplateTransitionTiming } from './catalog-types-BIhSpOWK.js';
|
|
6
|
-
import './types-
|
|
6
|
+
import './types-BV9IqExh.js';
|
|
7
7
|
|
|
8
8
|
interface TemplateExample<Variables extends Record<string, unknown> = Record<string, unknown>> {
|
|
9
9
|
name: string;
|
package/dist/templates.js
CHANGED
|
@@ -1,8 +1,91 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
assertTemplateTransitionMetadata
|
|
3
|
+
} from "./chunk-2E6T633S.js";
|
|
4
|
+
|
|
5
|
+
// src/visual-system/catalog/kit.ts
|
|
6
|
+
function toMetadata(template) {
|
|
7
|
+
return {
|
|
8
|
+
id: template.id,
|
|
9
|
+
label: template.label,
|
|
10
|
+
description: template.description,
|
|
11
|
+
category: template.category,
|
|
12
|
+
family: template.family,
|
|
13
|
+
usesGlobalTextEffect: template.usesGlobalTextEffect,
|
|
14
|
+
usesGlobalTransition: template.usesGlobalTransition,
|
|
15
|
+
transitionTiming: template.transitionTiming,
|
|
16
|
+
usesGlobalBackgroundEffect: template.usesGlobalBackgroundEffect,
|
|
17
|
+
textCanvas: template.textCanvas,
|
|
18
|
+
jobs: template.jobs,
|
|
19
|
+
register: template.register,
|
|
20
|
+
useWhen: template.useWhen,
|
|
21
|
+
avoidWhen: template.avoidWhen,
|
|
22
|
+
schema: template.schema,
|
|
23
|
+
minDuration: template.minDuration,
|
|
24
|
+
preferredDuration: template.preferredDuration,
|
|
25
|
+
timing: template.timing
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function isTemplateJsonSchema(value) {
|
|
29
|
+
if (typeof value !== "object" || value == null) return false;
|
|
30
|
+
const schema = value;
|
|
31
|
+
return schema.type === "object" && typeof schema.properties === "object" && schema.properties != null;
|
|
32
|
+
}
|
|
33
|
+
function defineTemplate(definition) {
|
|
34
|
+
if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(definition.id)) {
|
|
35
|
+
throw new Error(`Invalid template id: ${definition.id}`);
|
|
36
|
+
}
|
|
37
|
+
const suppliedSchema = "schema" in definition ? definition.schema : void 0;
|
|
38
|
+
const standard = suppliedSchema && "~standard" in suppliedSchema ? suppliedSchema["~standard"] : void 0;
|
|
39
|
+
const convertedSchema = standard?.jsonSchema ? standard.jsonSchema.input({ target: "draft-07" }) : suppliedSchema && !("~standard" in suppliedSchema) ? suppliedSchema : void 0;
|
|
40
|
+
if (standard && !standard.jsonSchema) {
|
|
41
|
+
throw new Error("Template schemas must expose Standard JSON Schema so the server registry can be generated safely");
|
|
42
|
+
}
|
|
43
|
+
if (!convertedSchema || !isTemplateJsonSchema(convertedSchema)) {
|
|
44
|
+
throw new Error("Template schema must describe a JSON object");
|
|
45
|
+
}
|
|
46
|
+
const schema = { ...convertedSchema, additionalProperties: convertedSchema.additionalProperties ?? false };
|
|
47
|
+
const transitionMetadata = {
|
|
48
|
+
id: definition.id,
|
|
49
|
+
usesGlobalTransition: definition.usesGlobalTransition ?? false,
|
|
50
|
+
transitionTiming: definition.transitionTiming
|
|
51
|
+
};
|
|
52
|
+
assertTemplateTransitionMetadata(transitionMetadata);
|
|
53
|
+
const { schema: _runtimeSchema, duration, ...definitionWithoutSchema } = definition;
|
|
54
|
+
return Object.freeze({
|
|
55
|
+
label: definition.id,
|
|
56
|
+
description: "",
|
|
57
|
+
usesGlobalTextEffect: false,
|
|
58
|
+
usesGlobalTransition: false,
|
|
59
|
+
usesGlobalBackgroundEffect: false,
|
|
60
|
+
...definitionWithoutSchema,
|
|
61
|
+
schema,
|
|
62
|
+
...duration == null ? {} : { preferredDuration: duration }
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function createRenderTemplateRegistry(options) {
|
|
66
|
+
const byId = /* @__PURE__ */ new Map();
|
|
67
|
+
for (const template of options.templates) {
|
|
68
|
+
assertTemplateTransitionMetadata(template);
|
|
69
|
+
if (byId.has(template.id)) throw new Error(`Duplicate template id: ${template.id}`);
|
|
70
|
+
byId.set(template.id, template);
|
|
71
|
+
}
|
|
72
|
+
const templates = Object.freeze([...options.templates]);
|
|
73
|
+
if (options.metadata && options.metadata.map(({ id }) => id).join("\0") !== templates.map(({ id }) => id).join("\0")) {
|
|
74
|
+
throw new Error("Template metadata does not match render templates");
|
|
75
|
+
}
|
|
76
|
+
const metadata = new Map((options.metadata ?? templates.map(toMetadata)).map((template) => [template.id, template]));
|
|
77
|
+
const capabilities = { templates: templates.map(({ id }) => id) };
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
templates,
|
|
80
|
+
capabilities,
|
|
81
|
+
getTemplate: (id) => byId.get(id),
|
|
82
|
+
getTemplateMetadata: (id) => metadata.get(id),
|
|
83
|
+
listTemplateMetadata: () => [...metadata.values()]
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function createTemplateRegistry(options) {
|
|
87
|
+
return createRenderTemplateRegistry({ templates: options.definitions });
|
|
88
|
+
}
|
|
6
89
|
|
|
7
90
|
// src/templates.ts
|
|
8
91
|
function defineTemplate2(definition) {
|
package/dist/test.d.ts
CHANGED
|
@@ -1,28 +1,13 @@
|
|
|
1
|
-
import { e as VideoScene, b as VideoOrientation, V as VideoStyle, c as Video, d as VideoAudio, j as VideoInput } from './types-
|
|
2
|
-
import {
|
|
1
|
+
import { e as VideoScene, b as VideoOrientation, V as VideoStyle, c as Video, d as VideoAudio, j as VideoInput } from './types-BV9IqExh.js';
|
|
2
|
+
import { V as VideoWarning, a as VideoFinishReason } from './events-BBP30j3c.js';
|
|
3
3
|
|
|
4
4
|
type MockVideoStreamPart = {
|
|
5
5
|
type: "scene.add";
|
|
6
6
|
placement?: "closer";
|
|
7
7
|
scene: VideoScene;
|
|
8
|
-
} | {
|
|
9
|
-
type: "scene.patch";
|
|
10
|
-
sceneId: string;
|
|
11
|
-
patch: Partial<Omit<VideoScene, "id" | "templateId">>;
|
|
12
|
-
} | {
|
|
13
|
-
type: "asset.patch";
|
|
14
|
-
sceneId: string;
|
|
15
|
-
variables: Record<string, unknown>;
|
|
16
8
|
} | {
|
|
17
9
|
type: "plan.complete";
|
|
18
10
|
finishReason?: "stop" | "length" | "content-filter" | "other";
|
|
19
|
-
} | {
|
|
20
|
-
type: "plan.error";
|
|
21
|
-
error: {
|
|
22
|
-
message: string;
|
|
23
|
-
code?: string;
|
|
24
|
-
recoverable?: boolean;
|
|
25
|
-
};
|
|
26
11
|
} | {
|
|
27
12
|
type: "mock.delay";
|
|
28
13
|
durationMs: number;
|
|
@@ -50,7 +35,7 @@ interface MockProviderTextStream {
|
|
|
50
35
|
finishReason: Promise<"stop" | "length" | "content-filter">;
|
|
51
36
|
}
|
|
52
37
|
type SimulatedEventEnvelope<TType extends string, TData> = {
|
|
53
|
-
protocolVersion: "0.
|
|
38
|
+
protocolVersion: "0.5";
|
|
54
39
|
runId: string;
|
|
55
40
|
sequence: number;
|
|
56
41
|
eventId: string;
|
|
@@ -73,15 +58,6 @@ type SimulatedVideoEvent = SimulatedEventEnvelope<"response.start", {
|
|
|
73
58
|
}> | SimulatedEventEnvelope<"scene.add", {
|
|
74
59
|
scene: VideoScene;
|
|
75
60
|
position: number;
|
|
76
|
-
revision: number;
|
|
77
|
-
}> | SimulatedEventEnvelope<"scene.patch", {
|
|
78
|
-
sceneId: string;
|
|
79
|
-
revision: number;
|
|
80
|
-
patch: Partial<Omit<VideoScene, "id" | "templateId">>;
|
|
81
|
-
}> | SimulatedEventEnvelope<"asset.patch", {
|
|
82
|
-
sceneId: string;
|
|
83
|
-
revision: number;
|
|
84
|
-
variables: Record<string, unknown>;
|
|
85
61
|
}> | SimulatedEventEnvelope<"response.warning", {
|
|
86
62
|
warning: VideoWarning;
|
|
87
63
|
}> | SimulatedEventEnvelope<"response.complete", {
|
package/dist/test.js
CHANGED
|
@@ -233,9 +233,9 @@ 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-
|
|
237
|
-
const { createTextDeltaVideoPlanner } = await import("./text-stream-
|
|
238
|
-
const { BUILTIN_SERVER_TEMPLATE_KIT } = await import("./builtin-server-
|
|
236
|
+
const { createVideo } = await import("./compose-video-QJHKTOQ7.js");
|
|
237
|
+
const { createTextDeltaVideoPlanner } = await import("./text-stream-J6EDDJP4.js");
|
|
238
|
+
const { BUILTIN_SERVER_TEMPLATE_KIT } = await import("./builtin-server-YHEZ2JRF.js");
|
|
239
239
|
const { createTemplateSceneValidator } = await import("./validate-T7GBU2YF.js");
|
|
240
240
|
const controller = new AbortController();
|
|
241
241
|
const forwardAbort = () => controller.abort(options.signal?.reason);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createTextDeltaVideoPlanner
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-34O5BY6X.js";
|
|
4
4
|
import "./chunk-E7CL7UPB.js";
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-G66Z5CWR.js";
|
|
6
|
+
import "./chunk-AOWSXU2K.js";
|
|
7
7
|
import "./chunk-XGRA2MUV.js";
|
|
8
8
|
export {
|
|
9
9
|
createTextDeltaVideoPlanner
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const VIDEO_PROTOCOL_VERSION: "0.
|
|
1
|
+
declare const VIDEO_PROTOCOL_VERSION: "0.5";
|
|
2
2
|
declare const VIDEO_SCHEMA_VERSION: "0.1";
|
|
3
3
|
type VideoOrientation = "portrait" | "landscape";
|
|
4
4
|
type VideoKnowledgeMode = "input-only" | "general";
|
|
@@ -164,24 +164,9 @@ type VideoPlanPart = {
|
|
|
164
164
|
type: "scene.add";
|
|
165
165
|
placement?: "closer";
|
|
166
166
|
scene: VideoScene;
|
|
167
|
-
} | {
|
|
168
|
-
type: "scene.patch";
|
|
169
|
-
sceneId: string;
|
|
170
|
-
patch: Partial<Omit<VideoScene, "id" | "templateId">>;
|
|
171
|
-
} | {
|
|
172
|
-
type: "asset.patch";
|
|
173
|
-
sceneId: string;
|
|
174
|
-
variables: Record<string, unknown>;
|
|
175
167
|
} | {
|
|
176
168
|
type: "plan.complete";
|
|
177
169
|
finishReason?: "stop" | "length" | "content-filter" | "other";
|
|
178
|
-
} | {
|
|
179
|
-
type: "plan.error";
|
|
180
|
-
error: {
|
|
181
|
-
message: string;
|
|
182
|
-
code?: string;
|
|
183
|
-
recoverable?: boolean;
|
|
184
|
-
};
|
|
185
170
|
};
|
|
186
171
|
type VideoPlanner = (context: VideoGenerationContext) => AsyncIterable<VideoPlanPart>;
|
|
187
172
|
interface VideoSceneValidationContext {
|
|
@@ -11,9 +11,10 @@ npx skills add VanillaSkyAi/video@vanillasky
|
|
|
11
11
|
|
|
12
12
|
## Build the first response
|
|
13
13
|
|
|
14
|
-
1. Install `@vanillaskyai/video
|
|
15
|
-
2.
|
|
16
|
-
|
|
14
|
+
1. Install `@vanillaskyai/video`.
|
|
15
|
+
2. Choose and configure a current model separately; put its provider key in an
|
|
16
|
+
ignored `.env.local` file.
|
|
17
|
+
3. Connect that app-owned model with `createVideoHandler` in an authenticated
|
|
17
18
|
server route.
|
|
18
19
|
4. Pass grounded text or structured data as `input`.
|
|
19
20
|
5. Call `useVideo()` and render `VideoPlayer` in React.
|
package/docs/concepts.md
CHANGED
|
@@ -50,12 +50,12 @@ The planner cannot legitimately select a template outside the active kit.
|
|
|
50
50
|
|
|
51
51
|
## Planner
|
|
52
52
|
|
|
53
|
-
The server-side planner turns the input into
|
|
54
|
-
|
|
53
|
+
The server-side planner turns the input into complete `scene.add` parts followed
|
|
54
|
+
by `plan.complete`. A planner may be
|
|
55
55
|
recorded, deterministic, backed by OpenAI or Anthropic, or implemented with
|
|
56
56
|
another streaming text model.
|
|
57
57
|
|
|
58
|
-
The planner does not create public event IDs, checksums,
|
|
58
|
+
The planner does not create public event IDs, checksums, or final
|
|
59
59
|
snapshots. The runtime owns those guarantees.
|
|
60
60
|
|
|
61
61
|
`createVideoHandler` asks the planner to emit one early
|
|
@@ -113,5 +113,5 @@ complete storage contract.
|
|
|
113
113
|
checksum. Persist the snapshot when you need replay, resume, export, analytics,
|
|
114
114
|
or an editing handoff. The event log can also be persisted and revalidated.
|
|
115
115
|
|
|
116
|
-
Read the complete [Protocol 0.
|
|
116
|
+
Read the complete [Protocol 0.5](reference/protocol.md) and the separate
|
|
117
117
|
[persisted Video 0.1 contract](persistence.md).
|
package/docs/custom-templates.md
CHANGED
|
@@ -213,9 +213,9 @@ selection, and validator. Import it in the route that connects your model:
|
|
|
213
213
|
|
|
214
214
|
```ts
|
|
215
215
|
// src/video-route.ts
|
|
216
|
-
import { openai } from "@ai-sdk/openai";
|
|
217
216
|
import { streamText } from "ai";
|
|
218
217
|
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
218
|
+
import { videoModel } from "./video-model";
|
|
219
219
|
import { templates } from "../vanillasky/server";
|
|
220
220
|
|
|
221
221
|
export const handleVideo = createVideoHandler({
|
|
@@ -226,7 +226,7 @@ export const handleVideo = createVideoHandler({
|
|
|
226
226
|
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
227
227
|
},
|
|
228
228
|
streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
|
|
229
|
-
model:
|
|
229
|
+
model: videoModel,
|
|
230
230
|
system: systemPrompt,
|
|
231
231
|
prompt: userPrompt,
|
|
232
232
|
abortSignal: signal,
|
package/docs/getting-started.md
CHANGED
|
@@ -5,26 +5,22 @@
|
|
|
5
5
|
Install VanillaSky:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @vanillaskyai/video
|
|
8
|
+
npm install @vanillaskyai/video
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
VanillaSky does not select a provider or model during installation. Configure
|
|
12
|
+
that separately on the server. The route below assumes your application exports
|
|
13
|
+
an AI SDK `LanguageModel` as `videoModel`; if it does not, follow
|
|
14
|
+
[Provider integration](provider-integration.md) first.
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
OPENAI_MODEL=gpt-4.1
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Create one authenticated server route with `createVideoHandler`. Connect your
|
|
19
|
-
provider through `streamText`; keep the model, credentials, authentication, rate
|
|
16
|
+
Create one authenticated server route with `createVideoHandler`. Connect the
|
|
17
|
+
app-owned model through `streamText`; keep credentials, authentication, rate
|
|
20
18
|
limits, and media policy on the server:
|
|
21
19
|
|
|
22
20
|
```ts
|
|
23
|
-
import { openai } from "@ai-sdk/openai";
|
|
24
21
|
import { streamText } from "ai";
|
|
25
22
|
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
26
|
-
|
|
27
|
-
const model = openai(process.env.OPENAI_MODEL ?? "gpt-4.1");
|
|
23
|
+
import { videoModel } from "@/lib/video-model";
|
|
28
24
|
|
|
29
25
|
const handle = createVideoHandler({
|
|
30
26
|
// Local development only. Replace with your session check before deploying.
|
|
@@ -34,7 +30,7 @@ const handle = createVideoHandler({
|
|
|
34
30
|
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
35
31
|
},
|
|
36
32
|
streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
|
|
37
|
-
model,
|
|
33
|
+
model: videoModel,
|
|
38
34
|
system: systemPrompt,
|
|
39
35
|
prompt: userPrompt,
|
|
40
36
|
abortSignal: signal,
|
|
@@ -49,12 +45,11 @@ The local bypass is intentionally fail-closed: the packaged development command
|
|
|
49
45
|
sets its marker only for `next dev`, and it accepts only localhost. Every
|
|
50
46
|
production request is denied. Replace it with your real session validation
|
|
51
47
|
before deploying. For literal files and commands,
|
|
52
|
-
use the tested
|
|
53
|
-
[`examples/nextjs-quickstart` directory](https://github.com/VanillaSkyAi/video/tree/v0.4.0/examples/nextjs-quickstart).
|
|
48
|
+
use the tested [`examples/nextjs-quickstart` directory](../examples/nextjs-quickstart).
|
|
54
49
|
|
|
55
|
-
`
|
|
56
|
-
or custom implementation. The application can choose a cheaper
|
|
57
|
-
per request without changing VanillaSky.
|
|
50
|
+
`videoModel` can come from any AI SDK provider, registry, gateway, compatible
|
|
51
|
+
API, or custom implementation. The application can choose a cheaper, faster,
|
|
52
|
+
or higher-quality model per request without changing VanillaSky.
|
|
58
53
|
|
|
59
54
|
Call that route from React and render the player:
|
|
60
55
|
|
package/docs/integrate-nextjs.md
CHANGED
|
@@ -2,27 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Next.js integration
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
After installing VanillaSky, choose the provider and model for your server. This
|
|
6
|
+
complete example uses Anthropic and asks you to select a current Claude Sonnet
|
|
7
|
+
model explicitly instead of baking a model ID into the SDK docs:
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
npm install @vanillaskyai/video
|
|
10
|
+
npm install @vanillaskyai/video ai @ai-sdk/anthropic
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
Create an ignored `.env.local`:
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
ANTHROPIC_API_KEY=your-key
|
|
17
|
+
ANTHROPIC_MODEL=your-current-sonnet-model
|
|
16
18
|
```
|
|
17
19
|
|
|
18
20
|
Create `app/api/video/route.ts`:
|
|
19
21
|
|
|
20
22
|
```ts
|
|
21
|
-
import {
|
|
23
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
22
24
|
import { streamText } from "ai";
|
|
23
25
|
import { createVideoHandler } from "@vanillaskyai/video/server";
|
|
24
26
|
|
|
25
|
-
const
|
|
27
|
+
const modelId = process.env.ANTHROPIC_MODEL;
|
|
28
|
+
if (!modelId) throw new Error("Set ANTHROPIC_MODEL in the server environment");
|
|
29
|
+
const model = anthropic(modelId);
|
|
26
30
|
|
|
27
31
|
const handle = createVideoHandler({
|
|
28
32
|
// Local development only. Replace with your session check before deploying.
|
|
@@ -74,9 +78,9 @@ requests; replace it with your application's session validation before
|
|
|
74
78
|
deploying.
|
|
75
79
|
|
|
76
80
|
The copy-and-run app is in the
|
|
77
|
-
[`examples/nextjs-quickstart` directory](
|
|
81
|
+
[`examples/nextjs-quickstart` directory](../examples/nextjs-quickstart).
|
|
78
82
|
|
|
79
|
-
For another LLM, replace `
|
|
83
|
+
For another LLM, replace `anthropic(...)` with the matching AI SDK model. The route
|
|
80
84
|
shape and React code stay the same. See [Provider integration](provider-integration.md)
|
|
81
85
|
for adapters, authentication, diagnostics, and production controls. Add media,
|
|
82
86
|
audio, persistence, or custom templates only after the default path works.
|
package/docs/live-channels.md
CHANGED
|
@@ -33,7 +33,9 @@ The job owns the data and the mapping. The player owns nothing but playback.
|
|
|
33
33
|
## Play without stopping
|
|
34
34
|
|
|
35
35
|
Pass `loop` alongside a saved video. The player restarts from the beginning
|
|
36
|
-
instead of showing its replay affordance
|
|
36
|
+
instead of showing its replay affordance and loops its soundtrack continuously.
|
|
37
|
+
A track shorter than the channel repeats as soon as it ends; when the visual
|
|
38
|
+
timeline wraps, the soundtrack restarts from the beginning with it.
|
|
37
39
|
|
|
38
40
|
<!-- verify:live-channel-example:start -->
|
|
39
41
|
```tsx
|
|
@@ -130,7 +132,8 @@ reading time will stretch its animation instead and give them none.
|
|
|
130
132
|
- **Never replace a good channel with a broken one.** Validate first, and on
|
|
131
133
|
failure leave the previous configuration in place.
|
|
132
134
|
- **Autoplay needs `playbackMode="muted-autoplay"`.** Browsers refuse unmuted
|
|
133
|
-
autoplay
|
|
135
|
+
autoplay. A channel can still carry a looping soundtrack: it starts muted and
|
|
136
|
+
becomes audible when a viewer uses the player's sound control.
|
|
134
137
|
- **Host your media.** Loops re-request assets over long sessions; serving them
|
|
135
138
|
from your own origin keeps a content policy simple and avoids depending on
|
|
136
139
|
someone else's CDN.
|
package/docs/media-and-audio.md
CHANGED
|
@@ -87,6 +87,13 @@ generated scene exists, and render it with `autoPlay` and `startMuted={false}`.
|
|
|
87
87
|
If the browser blocks the audible start, the player returns to the first frame
|
|
88
88
|
and exposes its sound-start control.
|
|
89
89
|
|
|
90
|
+
For a saved video rendered with `<VideoPlayer video={video} loop />`, the
|
|
91
|
+
soundtrack loops too. A track shorter than the visual timeline repeats without
|
|
92
|
+
a silent gap, and the player restarts it from the beginning when the visual
|
|
93
|
+
timeline wraps. This does not bypass browser autoplay policy: use muted autoplay
|
|
94
|
+
for unattended playback and let a viewer unmute, or start audible playback from
|
|
95
|
+
a user interaction.
|
|
96
|
+
|
|
90
97
|
## Media providers
|
|
91
98
|
|
|
92
99
|
VanillaSky is provider-independent. When `resolveMedia` is configured, the
|
package/docs/persistence.md
CHANGED
|
@@ -8,7 +8,7 @@ does not provide a database or hosted media store. Save the value returned by
|
|
|
8
8
|
no SDK serializer.
|
|
9
9
|
|
|
10
10
|
Every stored video has `schemaVersion: "0.1"`. This storage version is separate
|
|
11
|
-
from streaming protocol `0.
|
|
11
|
+
from streaming protocol `0.5`. The 0.1 policy supports the current storage
|
|
12
12
|
schema only: there are no compatibility aliases or implicit coercions.
|
|
13
13
|
|
|
14
14
|
## Load at the storage boundary
|
package/docs/prompt-and-input.md
CHANGED
|
@@ -95,8 +95,8 @@ Your provider streams text deltas. VanillaSky decodes them into typed planner
|
|
|
95
95
|
parts, validates each complete scene, and emits deterministic protocol events.
|
|
96
96
|
The model never returns React, HTML, CSS, or executable JavaScript.
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
Every generated `scene.add` is complete and passes validation before the player
|
|
99
|
+
sees it. Emitted scenes are immutable.
|
|
100
100
|
|
|
101
101
|
The standard planner contract requires one `scene.add` with
|
|
102
102
|
`placement: "closer"` immediately after the first playable body scene. The
|