@qvac/sdk 0.2.3-dev.1760899404.f30d071 โ†’ 0.2.3-dev.1760967206.fa44856

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.
@@ -41,9 +41,10 @@ try {
41
41
  documents: medicalArticle,
42
42
  chunk: true,
43
43
  chunkOpts: {
44
- chunkSize: 30,
44
+ chunkSize: 100,
45
45
  chunkOverlap: 10,
46
46
  chunkStrategy: "paragraph",
47
+ splitStrategy: "token",
47
48
  },
48
49
  });
49
50
  console.log(`โœ… Created ${medicalResult.processed.length} chunks from medical article`);
@@ -486,6 +486,7 @@ export declare const requestSchema: z.ZodUnion<readonly [z.ZodObject<{
486
486
  paragraph: "paragraph";
487
487
  }>>;
488
488
  splitStrategy: z.ZodOptional<z.ZodEnum<{
489
+ token: "token";
489
490
  character: "character";
490
491
  word: "word";
491
492
  sentence: "sentence";
@@ -25,6 +25,7 @@ declare const ragSaveEmbeddingsParamsSchema: z.ZodObject<{
25
25
  paragraph: "paragraph";
26
26
  }>>;
27
27
  splitStrategy: z.ZodOptional<z.ZodEnum<{
28
+ token: "token";
28
29
  character: "character";
29
30
  word: "word";
30
31
  sentence: "sentence";
@@ -45,6 +46,7 @@ export declare const ragSaveEmbeddingsOperationSchema: z.ZodObject<{
45
46
  paragraph: "paragraph";
46
47
  }>>;
47
48
  splitStrategy: z.ZodOptional<z.ZodEnum<{
49
+ token: "token";
48
50
  character: "character";
49
51
  word: "word";
50
52
  sentence: "sentence";
@@ -95,6 +97,7 @@ export declare const ragRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
95
97
  paragraph: "paragraph";
96
98
  }>>;
97
99
  splitStrategy: z.ZodOptional<z.ZodEnum<{
100
+ token: "token";
98
101
  character: "character";
99
102
  word: "word";
100
103
  sentence: "sentence";
@@ -4,7 +4,9 @@ const llmChunkOptsSchema = z.object({
4
4
  chunkSize: z.number().optional(),
5
5
  chunkOverlap: z.number().optional(),
6
6
  chunkStrategy: z.enum(["character", "paragraph"]).optional(),
7
- splitStrategy: z.enum(["character", "word", "sentence", "line"]).optional(),
7
+ splitStrategy: z
8
+ .enum(["character", "word", "token", "sentence", "line"])
9
+ .optional(),
8
10
  });
9
11
  const ragSaveEmbeddingsResultSchema = z.object({
10
12
  status: z.enum(["fulfilled", "rejected"]),
@@ -4,28 +4,9 @@ export declare const registerDownload: (key: string, entry: DownloadEntry) => vo
4
4
  export declare const unregisterDownload: (key: string) => void;
5
5
  export declare const createHyperdriveDownloadKey: (hyperdriveKey: string, modelFileName: string) => string;
6
6
  export declare const createHttpDownloadKey: (url: string) => string;
7
- export declare const getAllActiveDownloads: () => Map<string, DownloadEntry>;
8
7
  export declare const setClearCacheFlag: (downloadKey: string, clearCache: boolean) => void;
9
8
  export declare const shouldClearCache: (downloadKey: string) => boolean;
10
9
  export declare const clearClearCacheFlag: (downloadKey: string) => void;
11
10
  export declare const createCancelFunction: (downloadKey: string, clearCache?: boolean) => () => void;
12
11
  export declare const cancelAllDownloads: () => void;
13
- export declare const getDownloadStats: () => {
14
- activeCount: number;
15
- downloads: ({
16
- hyperdriveKey: string;
17
- modelFileName: string;
18
- key: string;
19
- startTime: number;
20
- duration: number;
21
- type: "hyperdrive" | "http";
22
- } | {
23
- url: string;
24
- modelPath: string;
25
- key: string;
26
- startTime: number;
27
- duration: number;
28
- type: "hyperdrive" | "http";
29
- })[];
30
- };
31
12
  //# sourceMappingURL=download-manager.d.ts.map
@@ -12,7 +12,6 @@ export const unregisterDownload = (key) => {
12
12
  };
13
13
  export const createHyperdriveDownloadKey = (hyperdriveKey, modelFileName) => `${hyperdriveKey}:${modelFileName}`;
14
14
  export const createHttpDownloadKey = (url) => `http:${url}`;
15
- export const getAllActiveDownloads = () => new Map(activeDownloads);
16
15
  export const setClearCacheFlag = (downloadKey, clearCache) => {
17
16
  if (clearCache) {
18
17
  clearCacheFlags.set(downloadKey, true);
@@ -39,24 +38,6 @@ export const cancelAllDownloads = () => {
39
38
  console.log(`๐Ÿงน Cancelling ${activeDownloads.size} active downloads`);
40
39
  Array.from(activeDownloads.keys()).forEach((key) => createCancelFunction(key)());
41
40
  };
42
- export const getDownloadStats = () => ({
43
- activeCount: activeDownloads.size,
44
- downloads: Array.from(activeDownloads.entries()).map(([key, entry]) => ({
45
- key,
46
- startTime: entry.startTime,
47
- duration: Date.now() - entry.startTime,
48
- type: entry.type,
49
- ...(entry.type === "hyperdrive"
50
- ? {
51
- hyperdriveKey: entry.hyperdriveKey,
52
- modelFileName: entry.modelFileName,
53
- }
54
- : {
55
- url: entry.url,
56
- modelPath: entry.modelPath,
57
- }),
58
- })),
59
- });
60
41
  // Register cleanup handlers for graceful shutdown
61
42
  let cleanupRegistered = false;
62
43
  let isCleaningUp = false;
@@ -79,10 +60,14 @@ const registerCleanupHandlers = () => {
79
60
  console.error("โŒ Error during download cleanup:", error);
80
61
  }
81
62
  };
82
- process.on("SIGTERM", () => {
83
- void cleanup();
84
- });
85
- process.on("SIGINT", () => {
86
- void cleanup();
87
- });
63
+ let handlingExit = false;
64
+ const exitHandler = async () => {
65
+ if (handlingExit)
66
+ return;
67
+ handlingExit = true;
68
+ await cleanup();
69
+ process.exit(0);
70
+ };
71
+ process.once("SIGTERM", () => void exitHandler());
72
+ process.once("SIGINT", () => void exitHandler());
88
73
  };
@@ -10,6 +10,24 @@ import { getEnv } from "../../../worker.js";
10
10
  import { getModelsCacheDir, generateShortHash } from "../../../utils/index.js";
11
11
  import { getModelBySrc } from "../../../../models/hyperdrive/index.js";
12
12
  import { getActiveDownload, registerDownload, unregisterDownload, createHyperdriveDownloadKey, shouldClearCache, clearClearCacheFlag, } from "./download-manager.js";
13
+ function createProgressHandler(blob, progressCallback, downloadKey, initialBytes = 0) {
14
+ let downloadedBytes = initialBytes;
15
+ return (index, bytes) => {
16
+ if (index >= blob.blockOffset &&
17
+ index < blob.blockOffset + blob.blockLength) {
18
+ downloadedBytes += bytes;
19
+ if (progressCallback && downloadKey) {
20
+ progressCallback({
21
+ type: "modelProgress",
22
+ downloaded: downloadedBytes,
23
+ total: blob.byteLength,
24
+ percentage: blob.byteLength > 0 ? (downloadedBytes / blob.byteLength) * 100 : 0,
25
+ downloadKey,
26
+ });
27
+ }
28
+ }
29
+ };
30
+ }
13
31
  async function setupHyperdrive(hyperdriveKey, corestoreDir) {
14
32
  const corestore = new Corestore(corestoreDir);
15
33
  await corestore.ready();
@@ -28,6 +46,14 @@ async function setupHyperdrive(hyperdriveKey, corestoreDir) {
28
46
  }
29
47
  async function cleanupHyperdrive(setup) {
30
48
  console.log("๐Ÿงน Cleaning up hyperdrive setup");
49
+ if (!setup.swarm.suspended) {
50
+ try {
51
+ await setup.swarm.suspend();
52
+ }
53
+ catch {
54
+ // If suspend fails, continue with cleanup
55
+ }
56
+ }
31
57
  await setup.swarm.destroy();
32
58
  await setup.drive.close();
33
59
  await setup.corestore.close();
@@ -43,42 +69,75 @@ export async function deleteCorestoreDirectory(corestoreDir) {
43
69
  }
44
70
  async function findFileInHyperdrive(drive, modelFileName) {
45
71
  const paths = [modelFileName, `/${modelFileName}`, `./${modelFileName}`];
46
- let entry = null;
47
- let targetPath = modelFileName;
48
72
  for (const pathVariation of paths) {
49
73
  try {
50
- entry = await drive.entry(pathVariation);
74
+ const entry = await drive.entry(pathVariation);
51
75
  if (entry) {
52
- targetPath = pathVariation;
53
- break;
76
+ return { entry, targetPath: pathVariation };
54
77
  }
55
78
  }
56
79
  catch {
57
80
  // Continue trying other paths
58
81
  }
59
82
  }
60
- if (!entry) {
61
- // Try listing available files to help debug
62
- try {
63
- const rootFiles = drive.list("/", false);
64
- const availableFiles = [];
65
- for await (const file of rootFiles) {
83
+ // If not found, list available files for debugging
84
+ const availableFiles = [];
85
+ try {
86
+ // Try both with and without leading slash
87
+ for (const prefix of ["/", ""]) {
88
+ const files = drive.list(prefix, false);
89
+ for await (const file of files) {
66
90
  availableFiles.push(file.key);
67
91
  }
68
- if (availableFiles.length === 0) {
69
- // Try without leading slash
70
- const noSlashFiles = drive.list("", false);
71
- for await (const file of noSlashFiles) {
72
- availableFiles.push(file.key);
73
- }
74
- }
75
- throw new Error(`File ${modelFileName} not found in hyperdrive. Available files: ${availableFiles.join(", ")}`);
92
+ if (availableFiles.length > 0)
93
+ break;
76
94
  }
77
- catch (listError) {
78
- throw new Error(`File ${modelFileName} not found in hyperdrive. Could not list available files: ${listError instanceof Error ? listError.message : String(listError)}`);
95
+ }
96
+ catch {
97
+ // Ignore listing errors
98
+ }
99
+ const filesInfo = availableFiles.length > 0
100
+ ? `Available files: ${availableFiles.join(", ")}`
101
+ : "Could not list available files";
102
+ throw new Error(`File ${modelFileName} not found in hyperdrive. ${filesInfo}`);
103
+ }
104
+ async function checkFileDownloaded(drive, entry) {
105
+ const blobs = await drive.getBlobs();
106
+ const blob = entry?.value?.blob;
107
+ if (!blob) {
108
+ return {
109
+ blobs,
110
+ blocksDownloaded: 0,
111
+ totalBlocks: 0,
112
+ totalBytes: 0,
113
+ };
114
+ }
115
+ // Check how many blocks are already downloaded
116
+ let blocksDownloaded = 0;
117
+ for (let i = blob.blockOffset; i < blob.blockOffset + blob.blockLength; i++) {
118
+ const hasBlock = await blobs.core.has(i);
119
+ if (hasBlock) {
120
+ blocksDownloaded++;
79
121
  }
80
122
  }
81
- return { entry, targetPath };
123
+ return {
124
+ blobs,
125
+ blocksDownloaded,
126
+ totalBlocks: blob.blockLength,
127
+ totalBytes: blob.byteLength,
128
+ };
129
+ }
130
+ async function validateFileExistence(drive, file) {
131
+ const { entry, targetPath } = await findFileInHyperdrive(drive, file);
132
+ const { blobs, blocksDownloaded, totalBlocks, totalBytes } = await checkFileDownloaded(drive, entry);
133
+ return {
134
+ entry,
135
+ blobs,
136
+ targetPath,
137
+ blocksDownloaded,
138
+ totalBlocks,
139
+ totalBytes,
140
+ };
82
141
  }
83
142
  async function validateFileChecksum(filePath, expectedChecksum) {
84
143
  console.log(`๐Ÿ” Validating SHA256 checksum...`);
@@ -153,13 +212,15 @@ async function performHyperdriveDownload(modelPath, modelFileName, hyperdriveKey
153
212
  }
154
213
  console.log(`Downloading model from hyperdrive ${hyperdriveKey} to ${modelPath}`);
155
214
  const setup = await setupHyperdrive(hyperdriveKey, corestoreDir);
156
- let downloadedBytes = 0;
157
- let totalBytes = 0;
158
215
  // Set up abort handler
159
216
  let readStream;
160
217
  let writeStream;
161
218
  const cleanup = () => {
162
219
  console.log("๐Ÿ›‘ Aborting hyperdrive download...");
220
+ setup.swarm.suspended = true;
221
+ setup.swarm.suspend().catch(() => {
222
+ // Ignore errors during emergency suspend
223
+ });
163
224
  // Destroy streams if they exist
164
225
  if (readStream) {
165
226
  readStream.destroy();
@@ -169,48 +230,19 @@ async function performHyperdriveDownload(modelPath, modelFileName, hyperdriveKey
169
230
  }
170
231
  };
171
232
  signal?.addEventListener("abort", cleanup);
233
+ let downloadHandler;
172
234
  try {
173
- const result = await findFileInHyperdrive(setup.drive, modelFileName);
174
- const { entry, targetPath } = result;
175
- totalBytes = entry?.value?.blob?.byteLength || 0;
176
- console.log(`๐Ÿ“ Model size: ${totalBytes} bytes (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`);
177
- // Get blobs and file blob range
178
- const blobs = await setup.drive.getBlobs();
235
+ const { entry, blobs, targetPath, blocksDownloaded, totalBlocks, totalBytes, } = await validateFileExistence(setup.drive, modelFileName);
179
236
  const blob = entry?.value?.blob;
180
237
  if (!blob) {
181
238
  throw new Error(`No blob found for ${modelFileName}`);
182
239
  }
183
- // Check what blocks are already downloaded in corestore for this specific file
184
- let alreadyDownloaded = 0;
185
- for (let i = blob.blockOffset; i < blob.blockOffset + blob.blockLength; i++) {
186
- const hasBlock = await blobs.core.has(i);
187
- if (hasBlock) {
188
- alreadyDownloaded++;
189
- }
190
- }
191
- downloadedBytes = 0;
192
- if (alreadyDownloaded > 0) {
193
- console.log(`๐Ÿ“ฅ Resuming download (${alreadyDownloaded}/${blob.blockLength} blocks already in corestore)`);
194
- }
195
- else {
196
- console.log(`๐Ÿ“ฅ Starting fresh download`);
197
- }
240
+ // Calculate already downloaded bytes for resuming
241
+ const bytesPerBlock = totalBlocks > 0 ? totalBytes / totalBlocks : 0;
242
+ const initialBytes = Math.floor(blocksDownloaded * bytesPerBlock);
198
243
  // Track only downloads for blocks within our file's blob range
199
- blobs.core.on("download", (index, bytes) => {
200
- if (index >= blob.blockOffset &&
201
- index < blob.blockOffset + blob.blockLength) {
202
- downloadedBytes += bytes;
203
- if (progressCallback) {
204
- progressCallback({
205
- type: "modelProgress",
206
- downloaded: downloadedBytes,
207
- total: totalBytes,
208
- percentage: totalBytes > 0 ? (downloadedBytes / totalBytes) * 100 : 0,
209
- downloadKey,
210
- });
211
- }
212
- }
213
- });
244
+ downloadHandler = createProgressHandler(blob, progressCallback, downloadKey, initialBytes);
245
+ blobs.core.on("download", downloadHandler);
214
246
  // Check if aborted before starting streams
215
247
  if (signal?.aborted) {
216
248
  throw new Error("Download cancelled");
@@ -223,15 +255,13 @@ async function performHyperdriveDownload(modelPath, modelFileName, hyperdriveKey
223
255
  readStream.pipe(writeStream);
224
256
  // Wait for download to complete
225
257
  await new Promise((resolve, reject) => {
226
- writeStream.on("finish", () => resolve(undefined));
258
+ writeStream.on("finish", resolve);
227
259
  writeStream.on("error", reject);
228
260
  readStream.on("error", reject);
229
261
  // Also reject on abort
230
- if (signal) {
231
- signal.addEventListener("abort", () => {
232
- reject(new Error("Download cancelled"));
233
- }, { once: true });
234
- }
262
+ signal?.addEventListener("abort", () => {
263
+ reject(new Error("Download cancelled"));
264
+ }, { once: true });
235
265
  });
236
266
  console.log(`โœ… Model downloaded successfully to ${modelPath}`);
237
267
  // Delete corestore directory after successful download (only when not seeding, to save space)
@@ -249,22 +279,29 @@ async function performHyperdriveDownload(modelPath, modelFileName, hyperdriveKey
249
279
  }
250
280
  finally {
251
281
  signal?.removeEventListener("abort", cleanup);
282
+ if (downloadHandler) {
283
+ try {
284
+ const { blobs } = await validateFileExistence(setup.drive, modelFileName);
285
+ blobs.core.off("download", downloadHandler);
286
+ }
287
+ catch {
288
+ // If we can't get blobs, setup is already closed
289
+ }
290
+ }
252
291
  if (!seed) {
253
292
  await cleanupHyperdrive(setup);
254
293
  }
255
294
  // Only delete corestore and partial file if user explicitly requested clearCache
256
- if (signal?.aborted) {
257
- if (shouldClearCache(downloadKey)) {
258
- try {
259
- await fsPromises.unlink(modelPath);
260
- }
261
- catch {
262
- // no-op
263
- }
264
- await deleteCorestoreDirectory(corestoreDir);
295
+ if (signal?.aborted && shouldClearCache(downloadKey)) {
296
+ try {
297
+ await fsPromises.unlink(modelPath);
298
+ }
299
+ catch {
300
+ // no-op
265
301
  }
266
- clearClearCacheFlag(downloadKey);
302
+ await deleteCorestoreDirectory(corestoreDir);
267
303
  }
304
+ clearClearCacheFlag(downloadKey);
268
305
  }
269
306
  }
270
307
  export async function downloadModelFromHyperdrive(hyperdriveKey, modelFileName, seed, progressCallback, expectedChecksum) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qvac/sdk",
3
- "version": "0.2.3-dev.1760899404.f30d071",
3
+ "version": "0.2.3-dev.1760967206.fa44856",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -59,7 +59,7 @@
59
59
  "@qvac/error": "^0.1.0",
60
60
  "@qvac/langdetect-text": "^0.1.0",
61
61
  "@qvac/logging": "^0.1.0",
62
- "@qvac/rag": "^0.1.0",
62
+ "@qvac/rag": "^0.2.0",
63
63
  "@qvac/transcription-whispercpp": "^0.1.4",
64
64
  "@qvac/translation-nmtcpp": "^0.1.0",
65
65
  "fast-safe-stringify": "2.1.1",