vargai 0.4.0-alpha76 → 0.4.0-alpha78

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/package.json CHANGED
@@ -71,7 +71,7 @@
71
71
  "zod": "^4.2.1"
72
72
  },
73
73
  "sideEffects": false,
74
- "version": "0.4.0-alpha76",
74
+ "version": "0.4.0-alpha78",
75
75
  "exports": {
76
76
  ".": "./src/index.ts",
77
77
  "./ai": "./src/ai-sdk/index.ts",
@@ -1,5 +1,6 @@
1
1
  import { existsSync, statSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
+ import { ResolvedElement } from "../resolved-element";
3
4
  import type { VargElement, VargNode } from "../types";
4
5
 
5
6
  export function resolvePath(path: string): string {
@@ -84,13 +85,36 @@ function serializeValue(v: unknown): string {
84
85
  }
85
86
  return v;
86
87
  }
88
+ // Never put raw binary data in cache keys — use semantic identity instead.
89
+ // Audio segments can be 48-110KB; base64-encoding them would exceed
90
+ // Upstash Redis' 32KB key size limit.
87
91
  if (v instanceof Uint8Array) {
88
- return Buffer.from(v).toString("base64");
92
+ return `uint8:${v.byteLength}`;
93
+ }
94
+ // ResolvedElement (e.g. a speech segment used as Video audio input):
95
+ // serialize by content identity (type + text + duration), not binary data.
96
+ if (v instanceof ResolvedElement) {
97
+ const parts = [v.type];
98
+ for (const child of v.children) {
99
+ if (typeof child === "string") parts.push(child);
100
+ }
101
+ if (v.meta.duration) parts.push(String(v.meta.duration));
102
+ if (v.meta.file?.url) parts.push(v.meta.file.url);
103
+ return `resolved(${parts.join(",")})`;
104
+ }
105
+ if (isVargElement(v)) {
106
+ return `element:${computeCacheKey(v).join(":")}`;
89
107
  }
90
108
  if (Array.isArray(v)) {
91
109
  return `[${v.map(serializeValue).join(",")}]`;
92
110
  }
93
111
  if (v && typeof v === "object") {
112
+ // Skip File-like objects with binary data — use URL if available
113
+ if ("_data" in v && "_mediaType" in v) {
114
+ const url = (v as { _url?: string | null })._url;
115
+ const mediaType = (v as { _mediaType: string })._mediaType;
116
+ return url ? `file(${url})` : `file(${mediaType})`;
117
+ }
94
118
  const entries = Object.entries(v)
95
119
  .map(([key, val]) => `${key}:${serializeValue(val)}`)
96
120
  .join(",");
@@ -128,7 +152,7 @@ export function computeCacheKey(element: VargElement): CacheKeyPart[] {
128
152
  } else if (v === null || v === undefined) {
129
153
  key.push(k, v);
130
154
  } else if (v instanceof Uint8Array) {
131
- key.push(k, Buffer.from(v).toString("base64"));
155
+ key.push(k, `uint8:${v.byteLength}`);
132
156
  } else if (isVargElement(v)) {
133
157
  key.push(k, ...computeCacheKey(v));
134
158
  } else if (Array.isArray(v) || typeof v === "object") {
@@ -187,6 +187,12 @@ async function sliceSegments(
187
187
  descriptors.map(async (desc) => {
188
188
  const bytes = await sliceAudio(fullFile, desc.start, desc.end);
189
189
  const segmentFile = File.fromBuffer(bytes, "audio/mpeg");
190
+ // Upload segment to storage so downstream cache keys use the URL
191
+ // instead of serializing raw audio bytes (which can exceed Redis key limits).
192
+ const ctx = getResolveContext();
193
+ if (ctx?.storage) {
194
+ await segmentFile.upload(ctx.storage);
195
+ }
190
196
 
191
197
  // Rebase word timings relative to the segment's sliced audio (t=0)
192
198
  const segmentWords = allWords