vargai 0.3.1 → 0.4.0-alpha.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/README.md +1 -38
- package/biome.json +6 -1
- package/docs/index.html +1130 -0
- package/docs/prompting.md +326 -0
- package/docs/react.md +834 -0
- package/package.json +11 -6
- package/src/ai-sdk/index.ts +2 -21
- package/src/cli/commands/index.ts +1 -4
- package/src/cli/commands/render.tsx +71 -0
- package/src/cli/index.ts +2 -0
- package/src/react/cli.ts +52 -0
- package/src/react/elements.ts +146 -0
- package/src/react/examples/branching.tsx +66 -0
- package/src/react/examples/captions-demo.tsx +37 -0
- package/src/react/examples/character-video.tsx +84 -0
- package/src/react/examples/grid.tsx +53 -0
- package/src/react/examples/layouts-demo.tsx +57 -0
- package/src/react/examples/madi.tsx +60 -0
- package/src/react/examples/music-test.tsx +35 -0
- package/src/react/examples/onlyfans-1m/workflow.tsx +88 -0
- package/src/react/examples/orange-portrait.tsx +41 -0
- package/src/react/examples/split-element-demo.tsx +60 -0
- package/src/react/examples/split-layout-demo.tsx +60 -0
- package/src/react/examples/split.tsx +41 -0
- package/src/react/examples/video-grid.tsx +46 -0
- package/src/react/index.ts +43 -0
- package/src/react/layouts/grid.tsx +28 -0
- package/src/react/layouts/index.ts +2 -0
- package/src/react/layouts/split.tsx +20 -0
- package/src/react/react.test.ts +309 -0
- package/src/react/render.ts +21 -0
- package/src/react/renderers/animate.ts +59 -0
- package/src/react/renderers/captions.ts +297 -0
- package/src/react/renderers/clip.ts +248 -0
- package/src/react/renderers/context.ts +17 -0
- package/src/react/renderers/image.ts +109 -0
- package/src/react/renderers/index.ts +22 -0
- package/src/react/renderers/music.ts +60 -0
- package/src/react/renderers/packshot.ts +84 -0
- package/src/react/renderers/progress.ts +173 -0
- package/src/react/renderers/render.ts +243 -0
- package/src/react/renderers/slider.ts +69 -0
- package/src/react/renderers/speech.ts +53 -0
- package/src/react/renderers/split.ts +91 -0
- package/src/react/renderers/subtitle.ts +16 -0
- package/src/react/renderers/swipe.ts +75 -0
- package/src/react/renderers/title.ts +17 -0
- package/src/react/renderers/utils.ts +124 -0
- package/src/react/renderers/video.ts +127 -0
- package/src/react/runtime/jsx-dev-runtime.ts +43 -0
- package/src/react/runtime/jsx-runtime.ts +35 -0
- package/src/react/types.ts +232 -0
- package/src/studio/index.ts +26 -0
- package/src/studio/scanner.ts +102 -0
- package/src/studio/server.ts +554 -0
- package/src/studio/stages.ts +251 -0
- package/src/studio/step-renderer.ts +279 -0
- package/src/studio/types.ts +60 -0
- package/src/studio/ui/cache.html +303 -0
- package/src/studio/ui/index.html +1820 -0
- package/tsconfig.cli.json +8 -0
- package/tsconfig.json +3 -1
- package/bun.lock +0 -1255
- package/docs/plan.md +0 -66
- package/docs/todo.md +0 -14
- package/src/ai-sdk/middleware/index.ts +0 -25
- package/src/ai-sdk/middleware/placeholder.ts +0 -111
- package/src/ai-sdk/middleware/wrap-image-model.ts +0 -86
- package/src/ai-sdk/middleware/wrap-music-model.ts +0 -108
- package/src/ai-sdk/middleware/wrap-video-model.ts +0 -115
- /package/docs/{varg-sdk.md → sdk.md} +0 -0
- /package/src/ai-sdk/providers/{elevenlabs.ts → elevenlabs-provider.ts} +0 -0
- /package/src/ai-sdk/providers/{fal.ts → fal-provider.ts} +0 -0
package/docs/plan.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# varg ts-sdk migration plan
|
|
2
|
-
|
|
3
|
-
## core
|
|
4
|
-
|
|
5
|
-
- [ ] File (load from path, url, buffer)
|
|
6
|
-
- [ ] File.toTemp() - save to temp file for editly integration
|
|
7
|
-
- [ ] scene`` tagged template for composing elements in prompts
|
|
8
|
-
- [ ] types (AspectRatio, Element, etc)
|
|
9
|
-
|
|
10
|
-
## providers
|
|
11
|
-
|
|
12
|
-
- [ ] @varg/fal (kling, nano-banana, lipsync)
|
|
13
|
-
- [ ] @varg/elevenlabs (tts, music)
|
|
14
|
-
- [ ] @varg/higgsfield (soul, characters)
|
|
15
|
-
- [ ] @varg/heygen (talking heads)
|
|
16
|
-
- [ ] @varg/openai (sora, gpt-image, dall-e)
|
|
17
|
-
- [ ] @varg/replicate (birefnet, generic models)
|
|
18
|
-
|
|
19
|
-
## generation
|
|
20
|
-
|
|
21
|
-
- [ ] generateImage (text to image)
|
|
22
|
-
- [ ] generateImage (image to image / edit)
|
|
23
|
-
- [ ] generateVideo (text to video)
|
|
24
|
-
- [ ] generateVideo (image to video)
|
|
25
|
-
- [ ] generateSpeech (voiceover via elevenlabs)
|
|
26
|
-
- [ ] generateMusic (music generation via elevenlabs)
|
|
27
|
-
- [ ] generateLipsync (sync video to audio via fal)
|
|
28
|
-
- [ ] generateTalkingHead (photo to talking video via heygen)
|
|
29
|
-
- [ ] generateElement (character/item/style reference for consistent generation)
|
|
30
|
-
|
|
31
|
-
## editing (use editly directly)
|
|
32
|
-
|
|
33
|
-
users use editly npm package directly for video composition.
|
|
34
|
-
we provide `File.toTemp()` to bridge ai outputs → editly paths.
|
|
35
|
-
|
|
36
|
-
editly covers these py-sdk features:
|
|
37
|
-
- [ ] crop_to_aspect_ratio → editly clips with resizeMode
|
|
38
|
-
- [ ] mix_audio_with_ducking → editly audioNorm + audioTracks
|
|
39
|
-
- [ ] apply_zoom_effect → editly zoomDirection on layers
|
|
40
|
-
- [ ] create_ken_burns_effect → editly zoomDirection: "in" | "out"
|
|
41
|
-
- [ ] create_slideshow → editly clips[] with transitions
|
|
42
|
-
- [ ] concatenate_videos → editly clips[] with transitions
|
|
43
|
-
- [ ] picture_in_picture → editly layers with position/size
|
|
44
|
-
- [ ] blur_background_resize → editly resizeMode: "contain-blur"
|
|
45
|
-
- [ ] 67 gl-transitions (fade, crossfade, wipe, cube, etc.)
|
|
46
|
-
|
|
47
|
-
## custom effects (not in editly)
|
|
48
|
-
|
|
49
|
-
- [ ] addCaptions (tiktok-style word-by-word) - custom fabric.js layer for editly
|
|
50
|
-
- [ ] createSlider (before/after reveal animation)
|
|
51
|
-
- [ ] createPushTransition (card push effect)
|
|
52
|
-
- [ ] createSwipeAnimation (tinder-style card swipes)
|
|
53
|
-
- [ ] createSplitScreen (side-by-side comparison)
|
|
54
|
-
- [ ] createPackshot (end card with cta button + blinking cta)
|
|
55
|
-
|
|
56
|
-
## image processing
|
|
57
|
-
|
|
58
|
-
- [ ] removeBackground (via replicate birefnet)
|
|
59
|
-
|
|
60
|
-
## notes
|
|
61
|
-
|
|
62
|
-
- editly used directly (not wrapped)
|
|
63
|
-
- File.toTemp() bridges ai outputs to editly string paths
|
|
64
|
-
- providers are separate packages (@varg/fal, @varg/elevenlabs, etc)
|
|
65
|
-
- generateElement returns { images, text } for use in prompts
|
|
66
|
-
- scene`` template composes elements in prompts
|
package/docs/todo.md
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
# TODO
|
|
3
|
-
|
|
4
|
-
• [ ] fal
|
|
5
|
-
• [ ] higgsfield
|
|
6
|
-
• [ ] captions (exactly as in TikTok)
|
|
7
|
-
• [ ] editing (simple concat of video frames)
|
|
8
|
-
• [ ] voiceover via elevenlabs
|
|
9
|
-
• [ ] music generation via elevenlabs
|
|
10
|
-
• [ ] adding music on top of video
|
|
11
|
-
• [ ] lypsync (via sync models over fal)
|
|
12
|
-
• [ ] resize with blur: i.e. 9:16 is resided to 4:5 by adding sides with a blur on all video
|
|
13
|
-
• [ ] Picture-in-the-picture: one video on top of another
|
|
14
|
-
• [ ] fade / slideshow transitions
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
generatePlaceholder,
|
|
3
|
-
type PlaceholderOptions,
|
|
4
|
-
type PlaceholderResult,
|
|
5
|
-
} from "./placeholder";
|
|
6
|
-
export {
|
|
7
|
-
type ImagePlaceholderFallbackOptions,
|
|
8
|
-
imagePlaceholderFallbackMiddleware,
|
|
9
|
-
withImagePlaceholderFallback,
|
|
10
|
-
} from "./wrap-image-model";
|
|
11
|
-
export {
|
|
12
|
-
type MusicModelMiddleware,
|
|
13
|
-
type MusicPlaceholderFallbackOptions,
|
|
14
|
-
musicPlaceholderFallbackMiddleware,
|
|
15
|
-
withMusicPlaceholderFallback,
|
|
16
|
-
wrapMusicModel,
|
|
17
|
-
} from "./wrap-music-model";
|
|
18
|
-
export {
|
|
19
|
-
type PlaceholderFallbackOptions,
|
|
20
|
-
placeholderFallbackMiddleware,
|
|
21
|
-
type RenderMode,
|
|
22
|
-
type VideoModelMiddleware,
|
|
23
|
-
withPlaceholderFallback,
|
|
24
|
-
wrapVideoModel,
|
|
25
|
-
} from "./wrap-video-model";
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { unlink } from "node:fs/promises";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { $ } from "bun";
|
|
5
|
-
|
|
6
|
-
export interface PlaceholderOptions {
|
|
7
|
-
type: "image" | "video" | "audio";
|
|
8
|
-
prompt: string;
|
|
9
|
-
duration?: number;
|
|
10
|
-
width?: number;
|
|
11
|
-
height?: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface PlaceholderResult {
|
|
15
|
-
data: Uint8Array;
|
|
16
|
-
placeholder: true;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function promptToColor(prompt: string): string {
|
|
20
|
-
let hash = 0;
|
|
21
|
-
for (let i = 0; i < prompt.length; i++) {
|
|
22
|
-
const char = prompt.charCodeAt(i);
|
|
23
|
-
hash = (hash << 5) - hash + char;
|
|
24
|
-
hash = hash & hash;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const h = Math.abs(hash) % 360;
|
|
28
|
-
const s = 40 + (Math.abs(hash >> 8) % 30);
|
|
29
|
-
const l = 35 + (Math.abs(hash >> 16) % 20);
|
|
30
|
-
|
|
31
|
-
return `hsl(${h},${s}%,${l}%)`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function hslToHex(hsl: string): string {
|
|
35
|
-
const match = hsl.match(/hsl\((\d+),(\d+)%,(\d+)%\)/);
|
|
36
|
-
if (!match) return "333333";
|
|
37
|
-
|
|
38
|
-
const h = Number.parseInt(match[1]!) / 360;
|
|
39
|
-
const s = Number.parseInt(match[2]!) / 100;
|
|
40
|
-
const l = Number.parseInt(match[3]!) / 100;
|
|
41
|
-
|
|
42
|
-
const hue2rgb = (p: number, q: number, t: number) => {
|
|
43
|
-
if (t < 0) t += 1;
|
|
44
|
-
if (t > 1) t -= 1;
|
|
45
|
-
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
46
|
-
if (t < 1 / 2) return q;
|
|
47
|
-
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
48
|
-
return p;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
52
|
-
const p = 2 * l - q;
|
|
53
|
-
const r = Math.round(hue2rgb(p, q, h + 1 / 3) * 255);
|
|
54
|
-
const g = Math.round(hue2rgb(p, q, h) * 255);
|
|
55
|
-
const b = Math.round(hue2rgb(p, q, h - 1 / 3) * 255);
|
|
56
|
-
|
|
57
|
-
return `${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function truncatePrompt(text: string, maxLen: number): string {
|
|
61
|
-
const clean = text.replace(/[^a-zA-Z0-9 .,!?-]/g, "");
|
|
62
|
-
if (clean.length <= maxLen) return clean;
|
|
63
|
-
return `${clean.slice(0, maxLen - 3)}...`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function generatePlaceholder(
|
|
67
|
-
options: PlaceholderOptions,
|
|
68
|
-
): Promise<PlaceholderResult> {
|
|
69
|
-
const { type, prompt, duration = 3, width = 1080, height = 1920 } = options;
|
|
70
|
-
|
|
71
|
-
const color = promptToColor(prompt);
|
|
72
|
-
const hexColor = hslToHex(color);
|
|
73
|
-
const labelFontSize = Math.floor(Math.min(width, height) / 20);
|
|
74
|
-
const promptFontSize = Math.floor(Math.min(width, height) / 35);
|
|
75
|
-
const maxChars = Math.floor((width * 0.7) / (promptFontSize * 0.5));
|
|
76
|
-
const typeLabel = type.toUpperCase();
|
|
77
|
-
const promptText = truncatePrompt(prompt, maxChars);
|
|
78
|
-
|
|
79
|
-
const ext = type === "audio" ? "mp3" : type === "image" ? "png" : "mp4";
|
|
80
|
-
const outputPath = join(
|
|
81
|
-
tmpdir(),
|
|
82
|
-
`placeholder_${Date.now()}_${Math.random().toString(36).slice(2)}.${ext}`,
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
if (type === "audio") {
|
|
87
|
-
await $`ffmpeg -y -f lavfi -i anullsrc=r=44100:cl=stereo -t ${duration} -c:a libmp3lame ${outputPath}`.quiet();
|
|
88
|
-
} else if (type === "image") {
|
|
89
|
-
const colorInput = `color=c=0x${hexColor}:s=${width}x${height}:d=1`;
|
|
90
|
-
const labelY = `(h/2)-${labelFontSize}`;
|
|
91
|
-
const promptY = `(h/2)+${Math.floor(labelFontSize * 0.5)}`;
|
|
92
|
-
const drawLabel = `drawtext=text='${typeLabel}':fontcolor=white:fontsize=${labelFontSize}:x=(w-text_w)/2:y=${labelY}`;
|
|
93
|
-
const drawPrompt = `drawtext=text='${promptText}':fontcolor=white@0.7:fontsize=${promptFontSize}:x=(w-text_w)/2:y=${promptY}`;
|
|
94
|
-
await $`ffmpeg -y -f lavfi -i ${colorInput} -vf ${drawLabel},${drawPrompt} -frames:v 1 -update 1 ${outputPath}`.quiet();
|
|
95
|
-
} else {
|
|
96
|
-
const colorInput = `color=c=0x${hexColor}:s=${width}x${height}:d=${duration}:r=30`;
|
|
97
|
-
const labelY = `(h/2)-${labelFontSize}`;
|
|
98
|
-
const promptY = `(h/2)+${Math.floor(labelFontSize * 0.5)}`;
|
|
99
|
-
const drawLabel = `drawtext=text='${typeLabel}':fontcolor=white:fontsize=${labelFontSize}:x=(w-text_w)/2:y=${labelY}`;
|
|
100
|
-
const drawPrompt = `drawtext=text='${promptText}':fontcolor=white@0.7:fontsize=${promptFontSize}:x=(w-text_w)/2:y=${promptY}`;
|
|
101
|
-
await $`ffmpeg -y -f lavfi -i ${colorInput} -vf ${drawLabel},${drawPrompt} -c:v libx264 -preset ultrafast -pix_fmt yuv420p ${outputPath}`.quiet();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const data = await Bun.file(outputPath).bytes();
|
|
105
|
-
await unlink(outputPath).catch(() => {});
|
|
106
|
-
return { data: new Uint8Array(data), placeholder: true };
|
|
107
|
-
} catch (e) {
|
|
108
|
-
await unlink(outputPath).catch(() => {});
|
|
109
|
-
throw e;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ImageModelV3,
|
|
3
|
-
ImageModelV3CallOptions,
|
|
4
|
-
ImageModelV3Middleware,
|
|
5
|
-
} from "@ai-sdk/provider";
|
|
6
|
-
import { wrapImageModel } from "ai";
|
|
7
|
-
import { generatePlaceholder } from "./placeholder";
|
|
8
|
-
import type { RenderMode } from "./wrap-video-model";
|
|
9
|
-
|
|
10
|
-
export interface ImagePlaceholderFallbackOptions {
|
|
11
|
-
mode: RenderMode;
|
|
12
|
-
onFallback?: (error: Error, prompt: string) => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function imagePlaceholderFallbackMiddleware(
|
|
16
|
-
options: ImagePlaceholderFallbackOptions,
|
|
17
|
-
): ImageModelV3Middleware {
|
|
18
|
-
const { mode, onFallback } = options;
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
specificationVersion: "v3",
|
|
22
|
-
wrapGenerate: async ({ doGenerate, params, model }) => {
|
|
23
|
-
const createPlaceholderResult = async () => {
|
|
24
|
-
const [width, height] = (params.size?.split("x").map(Number) ?? [
|
|
25
|
-
1024, 1024,
|
|
26
|
-
]) as [number, number];
|
|
27
|
-
const prompt =
|
|
28
|
-
typeof params.prompt === "string"
|
|
29
|
-
? params.prompt
|
|
30
|
-
: ((params.prompt as { text?: string } | undefined)?.text ??
|
|
31
|
-
"placeholder");
|
|
32
|
-
|
|
33
|
-
const placeholder = await generatePlaceholder({
|
|
34
|
-
type: "image",
|
|
35
|
-
prompt,
|
|
36
|
-
width,
|
|
37
|
-
height,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
images: [placeholder.data],
|
|
42
|
-
warnings: [
|
|
43
|
-
{
|
|
44
|
-
type: "other" as const,
|
|
45
|
-
message: "placeholder: provider skipped or failed",
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
response: {
|
|
49
|
-
timestamp: new Date(),
|
|
50
|
-
modelId: model.modelId,
|
|
51
|
-
headers: undefined,
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
if (mode === "preview") {
|
|
57
|
-
return createPlaceholderResult();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
return await doGenerate();
|
|
62
|
-
} catch (e) {
|
|
63
|
-
if (mode === "strict") throw e;
|
|
64
|
-
|
|
65
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
66
|
-
const promptText =
|
|
67
|
-
typeof params.prompt === "string"
|
|
68
|
-
? params.prompt
|
|
69
|
-
: ((params.prompt as { text?: string } | undefined)?.text ??
|
|
70
|
-
"placeholder");
|
|
71
|
-
onFallback?.(error, promptText);
|
|
72
|
-
return createPlaceholderResult();
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function withImagePlaceholderFallback(
|
|
79
|
-
model: ImageModelV3,
|
|
80
|
-
options: ImagePlaceholderFallbackOptions,
|
|
81
|
-
): ImageModelV3 {
|
|
82
|
-
return wrapImageModel({
|
|
83
|
-
model,
|
|
84
|
-
middleware: imagePlaceholderFallbackMiddleware(options),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import type { MusicModelV3, MusicModelV3CallOptions } from "../music-model";
|
|
2
|
-
import { generatePlaceholder } from "./placeholder";
|
|
3
|
-
import type { RenderMode } from "./wrap-video-model";
|
|
4
|
-
|
|
5
|
-
export interface MusicModelMiddleware {
|
|
6
|
-
transformParams?: (options: {
|
|
7
|
-
params: MusicModelV3CallOptions;
|
|
8
|
-
model: MusicModelV3;
|
|
9
|
-
}) => PromiseLike<MusicModelV3CallOptions> | MusicModelV3CallOptions;
|
|
10
|
-
|
|
11
|
-
wrapGenerate?: (options: {
|
|
12
|
-
doGenerate: () => PromiseLike<
|
|
13
|
-
Awaited<ReturnType<MusicModelV3["doGenerate"]>>
|
|
14
|
-
>;
|
|
15
|
-
params: MusicModelV3CallOptions;
|
|
16
|
-
model: MusicModelV3;
|
|
17
|
-
}) => PromiseLike<Awaited<ReturnType<MusicModelV3["doGenerate"]>>>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function wrapMusicModel({
|
|
21
|
-
model,
|
|
22
|
-
middleware,
|
|
23
|
-
}: {
|
|
24
|
-
model: MusicModelV3;
|
|
25
|
-
middleware: MusicModelMiddleware;
|
|
26
|
-
}): MusicModelV3 {
|
|
27
|
-
const { transformParams, wrapGenerate } = middleware;
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
specificationVersion: "v3",
|
|
31
|
-
provider: model.provider,
|
|
32
|
-
modelId: model.modelId,
|
|
33
|
-
|
|
34
|
-
async doGenerate(params: MusicModelV3CallOptions) {
|
|
35
|
-
const transformedParams = transformParams
|
|
36
|
-
? await transformParams({ params, model })
|
|
37
|
-
: params;
|
|
38
|
-
|
|
39
|
-
const doGenerate = () => model.doGenerate(transformedParams);
|
|
40
|
-
|
|
41
|
-
return wrapGenerate
|
|
42
|
-
? wrapGenerate({ doGenerate, params: transformedParams, model })
|
|
43
|
-
: doGenerate();
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface MusicPlaceholderFallbackOptions {
|
|
49
|
-
mode: RenderMode;
|
|
50
|
-
onFallback?: (error: Error, prompt: string) => void;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function musicPlaceholderFallbackMiddleware(
|
|
54
|
-
options: MusicPlaceholderFallbackOptions,
|
|
55
|
-
): MusicModelMiddleware {
|
|
56
|
-
const { mode, onFallback } = options;
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
wrapGenerate: async ({ doGenerate, params, model }) => {
|
|
60
|
-
const createPlaceholderResult = async () => {
|
|
61
|
-
const placeholder = await generatePlaceholder({
|
|
62
|
-
type: "audio",
|
|
63
|
-
prompt: params.prompt,
|
|
64
|
-
duration: params.duration ?? 10,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
audio: placeholder.data,
|
|
69
|
-
warnings: [
|
|
70
|
-
{
|
|
71
|
-
type: "other" as const,
|
|
72
|
-
message: "placeholder: provider skipped or failed",
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
response: {
|
|
76
|
-
timestamp: new Date(),
|
|
77
|
-
modelId: model.modelId,
|
|
78
|
-
headers: undefined,
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
if (mode === "preview") {
|
|
84
|
-
return createPlaceholderResult();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
return await doGenerate();
|
|
89
|
-
} catch (e) {
|
|
90
|
-
if (mode === "strict") throw e;
|
|
91
|
-
|
|
92
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
93
|
-
onFallback?.(error, params.prompt);
|
|
94
|
-
return createPlaceholderResult();
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function withMusicPlaceholderFallback(
|
|
101
|
-
model: MusicModelV3,
|
|
102
|
-
options: MusicPlaceholderFallbackOptions,
|
|
103
|
-
): MusicModelV3 {
|
|
104
|
-
return wrapMusicModel({
|
|
105
|
-
model,
|
|
106
|
-
middleware: musicPlaceholderFallbackMiddleware(options),
|
|
107
|
-
});
|
|
108
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import type { VideoModelV3, VideoModelV3CallOptions } from "../video-model";
|
|
2
|
-
import { generatePlaceholder } from "./placeholder";
|
|
3
|
-
|
|
4
|
-
export type RenderMode = "strict" | "default" | "preview";
|
|
5
|
-
|
|
6
|
-
export interface VideoModelMiddleware {
|
|
7
|
-
transformParams?: (options: {
|
|
8
|
-
params: VideoModelV3CallOptions;
|
|
9
|
-
model: VideoModelV3;
|
|
10
|
-
}) => PromiseLike<VideoModelV3CallOptions> | VideoModelV3CallOptions;
|
|
11
|
-
|
|
12
|
-
wrapGenerate?: (options: {
|
|
13
|
-
doGenerate: () => PromiseLike<
|
|
14
|
-
Awaited<ReturnType<VideoModelV3["doGenerate"]>>
|
|
15
|
-
>;
|
|
16
|
-
params: VideoModelV3CallOptions;
|
|
17
|
-
model: VideoModelV3;
|
|
18
|
-
}) => PromiseLike<Awaited<ReturnType<VideoModelV3["doGenerate"]>>>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function wrapVideoModel({
|
|
22
|
-
model,
|
|
23
|
-
middleware,
|
|
24
|
-
}: {
|
|
25
|
-
model: VideoModelV3;
|
|
26
|
-
middleware: VideoModelMiddleware;
|
|
27
|
-
}): VideoModelV3 {
|
|
28
|
-
const { transformParams, wrapGenerate } = middleware;
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
specificationVersion: "v3",
|
|
32
|
-
provider: model.provider,
|
|
33
|
-
modelId: model.modelId,
|
|
34
|
-
maxVideosPerCall: model.maxVideosPerCall,
|
|
35
|
-
|
|
36
|
-
async doGenerate(params: VideoModelV3CallOptions) {
|
|
37
|
-
const transformedParams = transformParams
|
|
38
|
-
? await transformParams({ params, model })
|
|
39
|
-
: params;
|
|
40
|
-
|
|
41
|
-
const doGenerate = () => model.doGenerate(transformedParams);
|
|
42
|
-
|
|
43
|
-
return wrapGenerate
|
|
44
|
-
? wrapGenerate({ doGenerate, params: transformedParams, model })
|
|
45
|
-
: doGenerate();
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface PlaceholderFallbackOptions {
|
|
51
|
-
mode: RenderMode;
|
|
52
|
-
onFallback?: (error: Error, prompt: string) => void;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function placeholderFallbackMiddleware(
|
|
56
|
-
options: PlaceholderFallbackOptions,
|
|
57
|
-
): VideoModelMiddleware {
|
|
58
|
-
const { mode, onFallback } = options;
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
wrapGenerate: async ({ doGenerate, params, model }) => {
|
|
62
|
-
const createPlaceholderResult = async () => {
|
|
63
|
-
const [width, height] = (params.resolution?.split("x").map(Number) ?? [
|
|
64
|
-
1080, 1920,
|
|
65
|
-
]) as [number, number];
|
|
66
|
-
const placeholder = await generatePlaceholder({
|
|
67
|
-
type: "video",
|
|
68
|
-
prompt: params.prompt,
|
|
69
|
-
duration: params.duration ?? 3,
|
|
70
|
-
width,
|
|
71
|
-
height,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
videos: [placeholder.data],
|
|
76
|
-
warnings: [
|
|
77
|
-
{
|
|
78
|
-
type: "other" as const,
|
|
79
|
-
message: "placeholder: provider skipped or failed",
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
-
response: {
|
|
83
|
-
timestamp: new Date(),
|
|
84
|
-
modelId: model.modelId,
|
|
85
|
-
headers: undefined,
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
if (mode === "preview") {
|
|
91
|
-
return createPlaceholderResult();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
return await doGenerate();
|
|
96
|
-
} catch (e) {
|
|
97
|
-
if (mode === "strict") throw e;
|
|
98
|
-
|
|
99
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
100
|
-
onFallback?.(error, params.prompt);
|
|
101
|
-
return createPlaceholderResult();
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function withPlaceholderFallback(
|
|
108
|
-
model: VideoModelV3,
|
|
109
|
-
options: PlaceholderFallbackOptions,
|
|
110
|
-
): VideoModelV3 {
|
|
111
|
-
return wrapVideoModel({
|
|
112
|
-
model,
|
|
113
|
-
middleware: placeholderFallbackMiddleware(options),
|
|
114
|
-
});
|
|
115
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|