vargai 0.4.0-alpha51 → 0.4.0-alpha53
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/CLAUDE.md +10 -0
- package/package.json +4 -2
- package/src/ai-sdk/cache.ts +11 -5
- package/src/ai-sdk/middleware/placeholder.ts +2 -1
- package/src/react/renderers/render.ts +44 -21
package/CLAUDE.md
CHANGED
|
@@ -123,3 +123,13 @@ bun --hot ./index.ts
|
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
|
|
126
|
+
|
|
127
|
+
## Cache Policy
|
|
128
|
+
|
|
129
|
+
**NEVER clear or delete the user's cache**, even when debugging.
|
|
130
|
+
|
|
131
|
+
- Video/image generation costs real money ($0.05–$0.50+ per generation) and takes 60–180 seconds
|
|
132
|
+
- The cache is the user's asset — treat it as production data, not disposable debug state
|
|
133
|
+
- If you need to regenerate, modify the prompt slightly or use a different cache key
|
|
134
|
+
- If cache must be cleared, always ask the user explicitly first
|
|
135
|
+
- Suggest `--no-cache` flag for one-off re-renders instead of deleting cached files
|
package/package.json
CHANGED
|
@@ -69,7 +69,8 @@
|
|
|
69
69
|
"sharp": "^0.34.5",
|
|
70
70
|
"zod": "^4.2.1"
|
|
71
71
|
},
|
|
72
|
-
"
|
|
72
|
+
"sideEffects": false,
|
|
73
|
+
"version": "0.4.0-alpha53",
|
|
73
74
|
"exports": {
|
|
74
75
|
".": "./src/index.ts",
|
|
75
76
|
"./ai": "./src/ai-sdk/index.ts",
|
|
@@ -80,6 +81,7 @@
|
|
|
80
81
|
"./studio": "./src/studio/index.ts",
|
|
81
82
|
"./jsx-runtime": "./src/react/runtime/jsx-runtime.ts",
|
|
82
83
|
"./jsx-dev-runtime": "./src/react/runtime/jsx-dev-runtime.ts",
|
|
83
|
-
"./editly": "./src/ai-sdk/providers/editly/index.ts"
|
|
84
|
+
"./editly": "./src/ai-sdk/providers/editly/index.ts",
|
|
85
|
+
"./file": "./src/ai-sdk/file.ts"
|
|
84
86
|
}
|
|
85
87
|
}
|
package/src/ai-sdk/cache.ts
CHANGED
|
@@ -11,7 +11,10 @@ export interface WithCacheOptions {
|
|
|
11
11
|
|
|
12
12
|
type CacheKeyDeps = (string | number | boolean | null | undefined)[];
|
|
13
13
|
|
|
14
|
-
type WithCacheKey<T> = Omit<T, "cacheKey"> & {
|
|
14
|
+
type WithCacheKey<T> = Omit<T, "cacheKey" | "skipCacheWrite"> & {
|
|
15
|
+
cacheKey?: CacheKeyDeps;
|
|
16
|
+
skipCacheWrite?: boolean;
|
|
17
|
+
};
|
|
15
18
|
|
|
16
19
|
type CachedFn<T, R> = (options: WithCacheKey<T>) => Promise<R>;
|
|
17
20
|
|
|
@@ -60,7 +63,8 @@ function parseTTL(ttl: number | string | undefined): number | undefined {
|
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
/** Build a cache key string from a prefix and an array of dependencies. */
|
|
67
|
+
export function depsToKey(prefix: string, deps: CacheKeyDeps): string {
|
|
64
68
|
const depsStr = deps.map((d) => String(d ?? "")).join(":");
|
|
65
69
|
return prefix ? `${prefix}:${depsStr}` : depsStr;
|
|
66
70
|
}
|
|
@@ -116,7 +120,7 @@ export function withCache<T extends object, R>(
|
|
|
116
120
|
const ttl = parseTTL(options.ttl ?? DEFAULT_TTL);
|
|
117
121
|
const prefix = fn.name || "anonymous";
|
|
118
122
|
return async (opts: WithCacheKey<T>): Promise<R> => {
|
|
119
|
-
const { cacheKey, ...rest } = opts;
|
|
123
|
+
const { cacheKey, skipCacheWrite, ...rest } = opts;
|
|
120
124
|
|
|
121
125
|
if (!cacheKey) {
|
|
122
126
|
return fn(rest as T);
|
|
@@ -128,8 +132,10 @@ export function withCache<T extends object, R>(
|
|
|
128
132
|
return cached as R;
|
|
129
133
|
}
|
|
130
134
|
const result = await fn(rest as T);
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
if (!skipCacheWrite) {
|
|
136
|
+
const flattened = flatten(result);
|
|
137
|
+
await storage.set(key, flattened, ttl);
|
|
138
|
+
}
|
|
133
139
|
|
|
134
140
|
return result;
|
|
135
141
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { unlink } from "node:fs/promises";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { $ } from "bun";
|
|
5
4
|
|
|
6
5
|
export interface PlaceholderOptions {
|
|
7
6
|
type: "image" | "video" | "audio";
|
|
@@ -82,6 +81,8 @@ export async function generatePlaceholder(
|
|
|
82
81
|
`placeholder_${Date.now()}_${Math.random().toString(36).slice(2)}.${ext}`,
|
|
83
82
|
);
|
|
84
83
|
|
|
84
|
+
const { $ } = await import("bun");
|
|
85
|
+
|
|
85
86
|
try {
|
|
86
87
|
if (type === "audio") {
|
|
87
88
|
await $`ffmpeg -y -f lavfi -i anullsrc=r=44100:cl=stereo -t ${duration} -c:a libmp3lame ${outputPath}`.quiet();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ImageModelV3 } from "@ai-sdk/provider";
|
|
1
2
|
import { generateImage, wrapImageModel } from "ai";
|
|
2
3
|
import { type CacheStorage, withCache } from "../../ai-sdk/cache";
|
|
3
4
|
import type { File, File as VargFile } from "../../ai-sdk/file";
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
placeholderFallbackMiddleware,
|
|
9
10
|
wrapVideoModel,
|
|
10
11
|
} from "../../ai-sdk/middleware";
|
|
12
|
+
|
|
11
13
|
import { editly, localBackend } from "../../ai-sdk/providers/editly";
|
|
12
14
|
import type {
|
|
13
15
|
AudioTrack,
|
|
@@ -58,6 +60,28 @@ function resolveCacheStorage(
|
|
|
58
60
|
return cache;
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
function toImageModelV3(
|
|
64
|
+
model: Parameters<typeof generateImage>[0]["model"],
|
|
65
|
+
): ImageModelV3 {
|
|
66
|
+
if (typeof model === "object" && model.specificationVersion === "v3") {
|
|
67
|
+
return model;
|
|
68
|
+
}
|
|
69
|
+
// for string IDs and v2 models, create a shell that satisfies the type.
|
|
70
|
+
// in preview mode the middleware intercepts before doGenerate is called.
|
|
71
|
+
const modelId = typeof model === "string" ? model : model.modelId;
|
|
72
|
+
return {
|
|
73
|
+
specificationVersion: "v3",
|
|
74
|
+
provider: "placeholder",
|
|
75
|
+
modelId,
|
|
76
|
+
maxImagesPerCall: 1,
|
|
77
|
+
doGenerate: async () => {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`toImageModelV3 shell: doGenerate should not be called in preview mode (model: ${modelId})`,
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
61
85
|
export async function renderRoot(
|
|
62
86
|
element: VargElement<"render">,
|
|
63
87
|
options: RenderOptions,
|
|
@@ -84,23 +108,19 @@ export async function renderRoot(
|
|
|
84
108
|
: generateVideo;
|
|
85
109
|
|
|
86
110
|
const wrapGenerateImage: typeof generateImage = async (opts) => {
|
|
87
|
-
if (
|
|
88
|
-
typeof opts.model === "string" ||
|
|
89
|
-
opts.model.specificationVersion !== "v3"
|
|
90
|
-
) {
|
|
91
|
-
return cachedGenerateImage(opts);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
111
|
if (mode === "preview") {
|
|
95
112
|
trackPlaceholder("image");
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
113
|
+
return cachedGenerateImage({
|
|
114
|
+
...opts,
|
|
115
|
+
model: wrapImageModel({
|
|
116
|
+
model: toImageModelV3(opts.model),
|
|
117
|
+
middleware: imagePlaceholderFallbackMiddleware({
|
|
118
|
+
mode: "preview",
|
|
119
|
+
onFallback: () => {},
|
|
120
|
+
}),
|
|
101
121
|
}),
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
skipCacheWrite: true,
|
|
123
|
+
} as Parameters<typeof cachedGenerateImage>[0]);
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
return cachedGenerateImage(opts);
|
|
@@ -109,14 +129,17 @@ export async function renderRoot(
|
|
|
109
129
|
const wrapGenerateVideo: typeof generateVideo = async (opts) => {
|
|
110
130
|
if (mode === "preview") {
|
|
111
131
|
trackPlaceholder("video");
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
return cachedGenerateVideo({
|
|
133
|
+
...opts,
|
|
134
|
+
model: wrapVideoModel({
|
|
135
|
+
model: opts.model,
|
|
136
|
+
middleware: placeholderFallbackMiddleware({
|
|
137
|
+
mode: "preview",
|
|
138
|
+
onFallback: () => {},
|
|
139
|
+
}),
|
|
117
140
|
}),
|
|
118
|
-
|
|
119
|
-
|
|
141
|
+
skipCacheWrite: true,
|
|
142
|
+
} as Parameters<typeof cachedGenerateVideo>[0]);
|
|
120
143
|
}
|
|
121
144
|
|
|
122
145
|
return cachedGenerateVideo(opts);
|