@qvac/sdk 0.10.1 → 0.11.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/NOTICE +92 -54
- package/dist/client/api/cancel.d.ts +24 -32
- package/dist/client/api/cancel.js +58 -31
- package/dist/client/api/client-request-id.d.ts +17 -0
- package/dist/client/api/client-request-id.js +27 -0
- package/dist/client/api/completion-stream.d.ts +1 -1
- package/dist/client/api/completion-stream.js +34 -3
- package/dist/client/api/diffusion.d.ts +46 -0
- package/dist/client/api/diffusion.js +50 -1
- package/dist/client/api/download-asset.d.ts +12 -2
- package/dist/client/api/download-asset.js +21 -3
- package/dist/client/api/embed.d.ts +8 -4
- package/dist/client/api/embed.js +14 -1
- package/dist/client/api/get-model-info.d.ts +1 -1
- package/dist/client/api/index.d.ts +1 -0
- package/dist/client/api/index.js +1 -0
- package/dist/client/api/load-model.d.ts +12 -4
- package/dist/client/api/load-model.js +25 -1
- package/dist/client/api/rag.d.ts +9 -3
- package/dist/client/api/rag.js +23 -3
- package/dist/client/api/transcribe.d.ts +20 -8
- package/dist/client/api/transcribe.js +58 -4
- package/dist/client/api/unload-model.d.ts +5 -2
- package/dist/client/api/unload-model.js +9 -4
- package/dist/client/api/upscale.d.ts +45 -0
- package/dist/client/api/upscale.js +99 -0
- package/dist/client/rpc/expo-rpc-client.js +2 -1
- package/dist/client/rpc/node-rpc-client.js +18 -7
- package/dist/client/rpc/rpc-client.d.ts +2 -2
- package/dist/client/rpc/rpc-client.js +10 -4
- package/dist/client/rpc/rpc-error.d.ts +9 -0
- package/dist/client/rpc/rpc-error.js +92 -0
- package/dist/examples/cancel-by-request-id.d.ts +38 -0
- package/dist/examples/cancel-by-request-id.js +103 -0
- package/dist/examples/diffusion-esrgan-upscale.d.ts +2 -0
- package/dist/examples/diffusion-esrgan-upscale.js +78 -0
- package/dist/examples/download-with-cancel.js +10 -10
- package/dist/examples/embed-multi-gpu.d.ts +2 -0
- package/dist/examples/embed-multi-gpu.js +46 -0
- package/dist/examples/llamacpp-multi-gpu.d.ts +2 -0
- package/dist/examples/llamacpp-multi-gpu.js +57 -0
- package/dist/examples/rag/rag-hyperdb/cancellation.js +20 -17
- package/dist/examples/tools/llamacpp-tools-gemma4.d.ts +2 -0
- package/dist/examples/tools/llamacpp-tools-gemma4.js +76 -0
- package/dist/examples/tools/llamacpp-tools-qwen35.d.ts +2 -0
- package/dist/examples/tools/llamacpp-tools-qwen35.js +70 -0
- package/dist/examples/transcription/whispercpp-microphone-conversation.d.ts +2 -0
- package/dist/examples/transcription/whispercpp-microphone-conversation.js +102 -0
- package/dist/examples/tts/supertonic.js +1 -1
- package/dist/expo/plugins/withMobileBundle.d.ts +20 -4
- package/dist/expo/plugins/withMobileBundle.js +44 -6
- package/dist/index.d.ts +5 -2
- package/dist/index.js +21 -1
- package/dist/models/registry/models.d.ts +186 -318
- package/dist/models/registry/models.js +1522 -1667
- package/dist/pear/pre.d.ts +3 -0
- package/dist/pear/pre.js +3 -0
- package/dist/schemas/cancel.d.ts +167 -28
- package/dist/schemas/cancel.js +119 -19
- package/dist/schemas/common.d.ts +427 -208
- package/dist/schemas/common.js +3 -1
- package/dist/schemas/completion-event.d.ts +14 -0
- package/dist/schemas/completion-event.js +11 -1
- package/dist/schemas/completion-stream.d.ts +15 -1
- package/dist/schemas/completion-stream.js +25 -7
- package/dist/schemas/download-asset.d.ts +15 -9
- package/dist/schemas/download-asset.js +17 -6
- package/dist/schemas/embed.d.ts +1 -0
- package/dist/schemas/embed.js +5 -0
- package/dist/schemas/error.d.ts +31 -0
- package/dist/schemas/error.js +29 -1
- package/dist/schemas/finetune.d.ts +2 -0
- package/dist/schemas/finetune.js +5 -0
- package/dist/schemas/get-model-info.d.ts +6 -6
- package/dist/schemas/llamacpp-config.d.ts +54 -8
- package/dist/schemas/llamacpp-config.js +12 -0
- package/dist/schemas/load-model.d.ts +1911 -1260
- package/dist/schemas/load-model.js +15 -0
- package/dist/schemas/model-src-utils.d.ts +14 -14
- package/dist/schemas/model-types.d.ts +10 -10
- package/dist/schemas/ocr.d.ts +3 -3
- package/dist/schemas/plugin.d.ts +40 -1
- package/dist/schemas/plugin.js +10 -1
- package/dist/schemas/rag.d.ts +9 -0
- package/dist/schemas/rag.js +17 -9
- package/dist/schemas/registry.d.ts +30 -30
- package/dist/schemas/reload-config.d.ts +14 -14
- package/dist/schemas/sdcpp-config.d.ts +131 -17
- package/dist/schemas/sdcpp-config.js +163 -1
- package/dist/schemas/sdk-config.d.ts +649 -271
- package/dist/schemas/sdk-errors-client.d.ts +1 -0
- package/dist/schemas/sdk-errors-client.js +5 -0
- package/dist/schemas/sdk-errors-server.d.ts +5 -0
- package/dist/schemas/sdk-errors-server.js +25 -0
- package/dist/schemas/text-to-speech.d.ts +78 -78
- package/dist/schemas/transcription-config.d.ts +24 -24
- package/dist/schemas/transcription.d.ts +56 -2
- package/dist/schemas/transcription.js +27 -0
- package/dist/schemas/translate.d.ts +2 -0
- package/dist/schemas/translate.js +13 -2
- package/dist/schemas/translation-config.d.ts +40 -40
- package/dist/schemas/unload-model.d.ts +2 -1
- package/dist/schemas/unload-model.js +4 -1
- package/dist/scripts/run-unit-tests.js +15 -2
- package/dist/server/bare/delegate-rpc-client.js +10 -8
- package/dist/server/bare/ops/cancel.d.ts +27 -1
- package/dist/server/bare/ops/cancel.js +36 -10
- package/dist/server/bare/ops/embed.d.ts +3 -3
- package/dist/server/bare/ops/embed.js +144 -28
- package/dist/server/bare/ops/kv-cache-utils.d.ts +0 -7
- package/dist/server/bare/ops/kv-cache-utils.js +8 -47
- package/dist/server/bare/ops/transcribe.d.ts +10 -5
- package/dist/server/bare/ops/transcribe.js +196 -41
- package/dist/server/bare/ops/translate.d.ts +1 -1
- package/dist/server/bare/ops/translate.js +197 -88
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.d.ts +12 -3
- package/dist/server/bare/plugins/llamacpp-completion/ops/completion-stream.js +170 -172
- package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +125 -21
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.d.ts +154 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-session.js +448 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.d.ts +21 -73
- package/dist/server/bare/plugins/llamacpp-completion/ops/kv-cache-state.js +18 -101
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +30 -3
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +157 -85
- package/dist/server/bare/plugins/llamacpp-completion/transform.d.ts +10 -0
- package/dist/server/bare/plugins/llamacpp-completion/transform.js +34 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +7 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +10 -1
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +17 -15
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.js +5 -1
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +3 -3
- package/dist/server/bare/plugins/onnx-ocr/plugin.js +3 -0
- package/dist/server/bare/plugins/onnx-tts/plugin.d.ts +39 -39
- package/dist/server/bare/plugins/onnx-tts/plugin.js +6 -0
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +29 -21
- package/dist/server/bare/plugins/parakeet-transcription/plugin.js +4 -1
- package/dist/server/bare/plugins/sdcpp-generation/ops/diffusion.js +8 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.d.ts +3 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/upscale.js +40 -0
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +91 -16
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +97 -7
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +22 -3
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.js +50 -14
- package/dist/server/bare/rag-hyperdb/index.d.ts +1 -1
- package/dist/server/bare/rag-hyperdb/index.js +1 -1
- package/dist/server/bare/rag-hyperdb/rag-operation-manager.d.ts +26 -4
- package/dist/server/bare/rag-hyperdb/rag-operation-manager.js +65 -28
- package/dist/server/bare/runtime/disposable-scope.d.ts +36 -0
- package/dist/server/bare/runtime/disposable-scope.js +69 -0
- package/dist/server/bare/runtime/index.d.ts +8 -0
- package/dist/server/bare/runtime/index.js +3 -0
- package/dist/server/bare/runtime/request-context.d.ts +49 -0
- package/dist/server/bare/runtime/request-context.js +1 -0
- package/dist/server/bare/runtime/request-id.d.ts +12 -0
- package/dist/server/bare/runtime/request-id.js +14 -0
- package/dist/server/bare/runtime/request-registry-singleton.d.ts +4 -0
- package/dist/server/bare/runtime/request-registry-singleton.js +27 -0
- package/dist/server/bare/runtime/request-registry.d.ts +136 -0
- package/dist/server/bare/runtime/request-registry.js +297 -0
- package/dist/server/bare/runtime/with-request-context.d.ts +22 -0
- package/dist/server/bare/runtime/with-request-context.js +53 -0
- package/dist/server/bare/types/addon-responses.d.ts +11 -1
- package/dist/server/rpc/handler-registry.js +26 -7
- package/dist/server/rpc/handlers/cancel-delegated.js +28 -31
- package/dist/server/rpc/handlers/cancelHandler.d.ts +30 -1
- package/dist/server/rpc/handlers/cancelHandler.js +54 -22
- package/dist/server/rpc/handlers/delete-cache.d.ts +9 -0
- package/dist/server/rpc/handlers/delete-cache.js +13 -15
- package/dist/server/rpc/handlers/download-asset.js +133 -40
- package/dist/server/rpc/handlers/index.d.ts +2 -0
- package/dist/server/rpc/handlers/index.js +2 -0
- package/dist/server/rpc/handlers/load-model/download-manager.d.ts +60 -2
- package/dist/server/rpc/handlers/load-model/download-manager.js +141 -8
- package/dist/server/rpc/handlers/load-model/handler.js +213 -98
- package/dist/server/rpc/handlers/load-model/http.js +3 -3
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +1 -1
- package/dist/server/rpc/handlers/load-model/registry.js +1 -1
- package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +20 -1
- package/dist/server/rpc/handlers/load-model/resolve-session.js +6 -5
- package/dist/server/rpc/handlers/load-model/resolve.d.ts +3 -2
- package/dist/server/rpc/handlers/load-model/resolve.js +9 -6
- package/dist/server/rpc/handlers/load-model/types.d.ts +24 -0
- package/dist/server/rpc/handlers/rag.js +133 -15
- package/dist/server/rpc/handlers/upscale-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/upscale-stream.js +4 -0
- package/dist/server/utils/completion-normalizer.js +8 -0
- package/dist/server/utils/tools/dialect.js +4 -0
- package/dist/server/utils/tools/parser.js +16 -1
- package/dist/server/utils/tools/parsers/gemma4native.d.ts +4 -0
- package/dist/server/utils/tools/parsers/gemma4native.js +60 -0
- package/dist/server/utils/tools/parsers/qwen35.d.ts +4 -0
- package/dist/server/utils/tools/parsers/qwen35.js +120 -0
- package/dist/utils/aggregate-events.d.ts +9 -0
- package/dist/utils/aggregate-events.js +18 -3
- package/dist/utils/decorate-promise.d.ts +34 -0
- package/dist/utils/decorate-promise.js +35 -0
- package/dist/utils/errors-client.d.ts +3 -0
- package/dist/utils/errors-client.js +5 -0
- package/dist/utils/errors-server.d.ts +85 -0
- package/dist/utils/errors-server.js +97 -0
- package/package.json +20 -15
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { loadModel, unloadModel, diffusion, SD_V2_1_1B_Q8_0, REALESRGAN_X4PLUS_ANIME_6B, } from "@qvac/sdk";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
// ESRGAN upscale example.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// bun run examples/diffusion-esrgan-upscale.ts [esrganSrc] [prompt] [outputDir]
|
|
8
|
+
const esrganArg = process.argv[2];
|
|
9
|
+
const promptArg = process.argv[3];
|
|
10
|
+
const outputDirArg = process.argv[4];
|
|
11
|
+
const esrganModelSrc = esrganArg ??
|
|
12
|
+
REALESRGAN_X4PLUS_ANIME_6B;
|
|
13
|
+
const prompt = promptArg ??
|
|
14
|
+
"an illustrated red fox portrait, clean line art, soft watercolor background, detailed fur, crisp eyes";
|
|
15
|
+
const negative_prompt = "blurry, low quality, watermark, text";
|
|
16
|
+
const outputDir = outputDirArg ?? ".";
|
|
17
|
+
const seed = 42;
|
|
18
|
+
try {
|
|
19
|
+
console.log("Loading SD 2.1 + ESRGAN upscaler...");
|
|
20
|
+
const modelId = await loadModel({
|
|
21
|
+
modelSrc: SD_V2_1_1B_Q8_0,
|
|
22
|
+
modelType: "diffusion",
|
|
23
|
+
modelConfig: {
|
|
24
|
+
prediction: "v",
|
|
25
|
+
upscaler: {
|
|
26
|
+
type: "esrgan",
|
|
27
|
+
model_src: esrganModelSrc,
|
|
28
|
+
tile_size: 128,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
onProgress: (p) => console.log(`Loading: ${p.percentage.toFixed(1)}%`),
|
|
32
|
+
});
|
|
33
|
+
console.log(`Model loaded: ${modelId}`);
|
|
34
|
+
// Source size is intentionally small — each ESRGAN repeat multiplies dimensions.
|
|
35
|
+
const baseParams = {
|
|
36
|
+
modelId,
|
|
37
|
+
prompt,
|
|
38
|
+
negative_prompt,
|
|
39
|
+
width: 128,
|
|
40
|
+
height: 128,
|
|
41
|
+
steps: 5,
|
|
42
|
+
cfg_scale: 7.5,
|
|
43
|
+
seed,
|
|
44
|
+
};
|
|
45
|
+
console.log(`\nGenerating ESRGAN x4 upscale: "${prompt}"`);
|
|
46
|
+
const single = diffusion({ ...baseParams, upscale: true });
|
|
47
|
+
for await (const { step, totalSteps } of single.progressStream) {
|
|
48
|
+
process.stdout.write(`\rStep ${step}/${totalSteps}\x1b[K`);
|
|
49
|
+
}
|
|
50
|
+
console.log();
|
|
51
|
+
const singleBuffers = await single.outputs;
|
|
52
|
+
for (let i = 0; i < singleBuffers.length; i++) {
|
|
53
|
+
const out = path.join(outputDir, `sd2_esrgan_x4_seed${seed}_${i}.png`);
|
|
54
|
+
fs.writeFileSync(out, singleBuffers[i]);
|
|
55
|
+
console.log(`Saved: ${out}`);
|
|
56
|
+
}
|
|
57
|
+
console.log("Stats:", await single.stats);
|
|
58
|
+
console.log("\nGenerating ESRGAN two-pass x16 upscale...");
|
|
59
|
+
const twoPass = diffusion({ ...baseParams, upscale: { repeats: 2 } });
|
|
60
|
+
for await (const { step, totalSteps } of twoPass.progressStream) {
|
|
61
|
+
process.stdout.write(`\rStep ${step}/${totalSteps}\x1b[K`);
|
|
62
|
+
}
|
|
63
|
+
console.log();
|
|
64
|
+
const twoPassBuffers = await twoPass.outputs;
|
|
65
|
+
for (let i = 0; i < twoPassBuffers.length; i++) {
|
|
66
|
+
const out = path.join(outputDir, `sd2_esrgan_x16_seed${seed}_${i}.png`);
|
|
67
|
+
fs.writeFileSync(out, twoPassBuffers[i]);
|
|
68
|
+
console.log(`Saved: ${out}`);
|
|
69
|
+
}
|
|
70
|
+
console.log("Stats:", await twoPass.stats);
|
|
71
|
+
await unloadModel({ modelId, clearStorage: false });
|
|
72
|
+
console.log("Done.");
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error("❌ Error:", error);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
@@ -4,8 +4,11 @@ console.log(`\n💡 Press Ctrl+C to pause the download (it will resume on restar
|
|
|
4
4
|
let modelId;
|
|
5
5
|
let cancelled = false;
|
|
6
6
|
try {
|
|
7
|
-
// Download model with progress tracking and cancellation
|
|
8
|
-
|
|
7
|
+
// Download model with progress tracking and cancellation. The
|
|
8
|
+
// `downloadAsset(...)` call returns a *decorated* promise: the
|
|
9
|
+
// promise resolves to the modelId, and the same value carries a
|
|
10
|
+
// synchronous `requestId` field so we can cancel before it settles.
|
|
11
|
+
const download = downloadAsset({
|
|
9
12
|
assetSrc: LLAMA_3_2_1B_INST_Q4_0,
|
|
10
13
|
onProgress: (progress) => {
|
|
11
14
|
const downloadedMB = (progress.downloaded / 1024 / 1024).toFixed(2);
|
|
@@ -18,17 +21,14 @@ try {
|
|
|
18
21
|
console.log(`📊 Progress: ${percentage}% (${downloadedMB}MB / ${totalMB}MB)`);
|
|
19
22
|
console.log(progress);
|
|
20
23
|
cancelled = true;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
downloadKey: progress.downloadKey,
|
|
26
|
-
// clearCache: true, // Uncomment to delete partial file instead of resuming
|
|
27
|
-
});
|
|
28
|
-
}
|
|
24
|
+
void cancel({
|
|
25
|
+
requestId: download.requestId,
|
|
26
|
+
// clearCache: true, // Uncomment to delete partial file instead of resuming
|
|
27
|
+
});
|
|
29
28
|
}
|
|
30
29
|
},
|
|
31
30
|
});
|
|
31
|
+
await download;
|
|
32
32
|
console.log(`\n✅ Model downloaded successfully! Model ID: ${modelId}`);
|
|
33
33
|
console.log("🎯 Download completed without interruption");
|
|
34
34
|
void close();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { loadModel, embed, unloadModel, EMBEDDINGGEMMA_300M_Q8_0 } from "@qvac/sdk";
|
|
2
|
+
// Multi-GPU embedding distributes model layers across multiple GPUs using
|
|
3
|
+
// llama.cpp's built-in split modes. Two strategies are available:
|
|
4
|
+
//
|
|
5
|
+
// - "layer": splits layers and KV cache across GPUs.
|
|
6
|
+
// - "row": splits layers and KV cache across GPUs, and uses tensor parallelism
|
|
7
|
+
// where supported.
|
|
8
|
+
//
|
|
9
|
+
// tensorSplit controls the proportion of work assigned to each GPU.
|
|
10
|
+
// "1,1" distributes evenly across two GPUs; "3,1" assigns 75% to GPU 0.
|
|
11
|
+
//
|
|
12
|
+
// mainGpu selects which GPU is used for the entire model when splitMode is
|
|
13
|
+
// "none", and pins the primary device in multi-GPU mode.
|
|
14
|
+
// Accepts an integer device index (0, 1, ...) or "integrated" / "dedicated".
|
|
15
|
+
try {
|
|
16
|
+
const modelId = await loadModel({
|
|
17
|
+
modelSrc: EMBEDDINGGEMMA_300M_Q8_0,
|
|
18
|
+
modelType: "llamacpp-embedding",
|
|
19
|
+
modelConfig: {
|
|
20
|
+
splitMode: "layer",
|
|
21
|
+
tensorSplit: "1,1",
|
|
22
|
+
mainGpu: 0,
|
|
23
|
+
gpuLayers: 99,
|
|
24
|
+
verbosity: 0,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const texts = [
|
|
28
|
+
"Multi-GPU embedding distributes layer computation across GPUs.",
|
|
29
|
+
"Each GPU handles a subset of layers, improving throughput for large models.",
|
|
30
|
+
"The tensor-split ratio controls how much work each GPU receives.",
|
|
31
|
+
];
|
|
32
|
+
for (const text of texts) {
|
|
33
|
+
const { embedding, stats } = await embed({ modelId, text });
|
|
34
|
+
console.log(`Embedded ${text.slice(0, 50)}...`);
|
|
35
|
+
console.log(` Dimensions: ${embedding.length}`);
|
|
36
|
+
if (stats) {
|
|
37
|
+
console.log(` Backend: ${stats.backendDevice}, TPS: ${stats.tokensPerSecond?.toFixed(1)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
await unloadModel({ modelId, clearStorage: false });
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error("Error:", error);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { completion, loadModel, unloadModel, VERBOSITY, LLAMA_3_2_1B_INST_Q4_0 } from "@qvac/sdk";
|
|
2
|
+
// Multi-GPU inference distributes a model across multiple GPUs using llama.cpp's
|
|
3
|
+
// built-in split modes. Two strategies are available:
|
|
4
|
+
//
|
|
5
|
+
// - "layer": splits layers and KV cache across GPUs.
|
|
6
|
+
// - "row": splits layers and KV cache across GPUs, and uses tensor parallelism
|
|
7
|
+
// where supported.
|
|
8
|
+
//
|
|
9
|
+
// tensor-split controls the proportion of work assigned to each GPU.
|
|
10
|
+
// "1,1" distributes evenly across two GPUs; "3,1" assigns 75% to GPU 0.
|
|
11
|
+
//
|
|
12
|
+
// Usage:
|
|
13
|
+
// bun run bare:example dist/examples/llamacpp-multi-gpu.js
|
|
14
|
+
// bun run bare:example dist/examples/llamacpp-multi-gpu.js '<model-url>'
|
|
15
|
+
const modelSrc = process.argv[2] ?? LLAMA_3_2_1B_INST_Q4_0;
|
|
16
|
+
try {
|
|
17
|
+
const modelId = await loadModel({
|
|
18
|
+
modelSrc,
|
|
19
|
+
modelType: "llm",
|
|
20
|
+
modelConfig: {
|
|
21
|
+
"split-mode": "layer",
|
|
22
|
+
"tensor-split": "1,1",
|
|
23
|
+
"main-gpu": 0,
|
|
24
|
+
ctx_size: 4096,
|
|
25
|
+
gpu_layers: 99,
|
|
26
|
+
verbosity: VERBOSITY.ERROR,
|
|
27
|
+
},
|
|
28
|
+
onProgress: (progress) => {
|
|
29
|
+
if (progress.shardInfo) {
|
|
30
|
+
const { shardInfo } = progress;
|
|
31
|
+
console.log(`Downloading ${shardInfo.shardName} (${shardInfo.currentShard}/${shardInfo.totalShards}) ` +
|
|
32
|
+
`— overall: ${shardInfo.overallPercentage.toFixed(1)}%`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log(`Downloading: ${progress.percentage.toFixed(1)}%`);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
const history = [
|
|
40
|
+
{
|
|
41
|
+
role: "user",
|
|
42
|
+
content: "Explain the difference between pipeline and tensor parallelism in one paragraph.",
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
const result = completion({ modelId, history, stream: true });
|
|
46
|
+
for await (const token of result.tokenStream) {
|
|
47
|
+
process.stdout.write(token);
|
|
48
|
+
}
|
|
49
|
+
const stats = await result.stats;
|
|
50
|
+
console.log("\n\nStats:", stats);
|
|
51
|
+
await unloadModel({ modelId, clearStorage: false });
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error("Error:", error);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
@@ -31,24 +31,27 @@ try {
|
|
|
31
31
|
console.log(`📄 Processing ${documents.length} documents...\n`);
|
|
32
32
|
let progressCount = 0;
|
|
33
33
|
let cancelled = false;
|
|
34
|
+
// Capture the decorated promise so we can cancel by `requestId` (the
|
|
35
|
+
// primary cancel path). For a "cancel everything RAG on this model"
|
|
36
|
+
// sweep, use `cancel({ modelId, kind: "rag" })` instead.
|
|
37
|
+
const ingest = ragIngest({
|
|
38
|
+
modelId,
|
|
39
|
+
workspace: WORKSPACE,
|
|
40
|
+
documents,
|
|
41
|
+
progressInterval: 50,
|
|
42
|
+
onProgress: (stage, current, total) => {
|
|
43
|
+
progressCount++;
|
|
44
|
+
const pct = total > 0 ? Math.round((current / total) * 100) : 0;
|
|
45
|
+
console.log(` [${stage}] ${current}/${total} (${pct}%)`);
|
|
46
|
+
if (!cancelled && stage === "embedding" && current > 10) {
|
|
47
|
+
console.log("\n🛑 Triggering cancellation...\n");
|
|
48
|
+
cancelled = true;
|
|
49
|
+
void cancel({ requestId: ingest.requestId });
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
});
|
|
34
53
|
try {
|
|
35
|
-
await
|
|
36
|
-
modelId,
|
|
37
|
-
workspace: WORKSPACE,
|
|
38
|
-
documents,
|
|
39
|
-
progressInterval: 50,
|
|
40
|
-
onProgress: (stage, current, total) => {
|
|
41
|
-
progressCount++;
|
|
42
|
-
const pct = total > 0 ? Math.round((current / total) * 100) : 0;
|
|
43
|
-
console.log(` [${stage}] ${current}/${total} (${pct}%)`);
|
|
44
|
-
// Cancel during embedding stage after a few updates
|
|
45
|
-
if (!cancelled && stage === "embedding" && current > 10) {
|
|
46
|
-
console.log("\n🛑 Triggering cancellation...\n");
|
|
47
|
-
cancelled = true;
|
|
48
|
-
void cancel({ operation: "rag", workspace: WORKSPACE });
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
});
|
|
54
|
+
await ingest;
|
|
52
55
|
console.log("\n⚠️ Ingest completed (cancellation didn't interrupt in time)");
|
|
53
56
|
}
|
|
54
57
|
catch (error) {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-calling example using the Gemma4 native dialect.
|
|
3
|
+
*
|
|
4
|
+
* Gemma4 emits tool calls in a JS-literal format with custom quote tokens:
|
|
5
|
+
* <|tool_call>call:NAME{key:<|"|>val<|"|>,...}<tool_call|>
|
|
6
|
+
*
|
|
7
|
+
* Reasoning output (thinking) is emitted inside <|channel>thought...<channel|>
|
|
8
|
+
* frames, which are stripped from contentDelta and forwarded as thinkingDelta
|
|
9
|
+
* when captureThinking is true.
|
|
10
|
+
*
|
|
11
|
+
* The dialect is auto-detected from the model name/path when the file name
|
|
12
|
+
* contains "gemma4" or "gemma-4". Pass toolDialect: "gemma4" explicitly to
|
|
13
|
+
* completion() if auto-detection does not pick it up for a given file name.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* bun run bare:example dist/examples/tools/llamacpp-tools-gemma4.js <model-url>
|
|
17
|
+
*/
|
|
18
|
+
import { completion, loadModel, unloadModel, } from "@qvac/sdk";
|
|
19
|
+
import { tools, mockExecute } from "./shared.js";
|
|
20
|
+
// bartowski's pack tags <eos> as the EOG token (matching the base tokenizer),
|
|
21
|
+
// ensuring generation stops correctly; unsloth's variant maps it differently.
|
|
22
|
+
const GEMMA4_HF = "https://huggingface.co/bartowski/google_gemma-4-E2B-it-GGUF/resolve/main/google_gemma-4-E2B-it-Q4_K_M.gguf";
|
|
23
|
+
const modelSrc = process.argv[2] ?? GEMMA4_HF;
|
|
24
|
+
let modelId;
|
|
25
|
+
try {
|
|
26
|
+
modelId = await loadModel({
|
|
27
|
+
modelSrc,
|
|
28
|
+
modelType: "llm",
|
|
29
|
+
modelConfig: { ctx_size: 4096, tools: true },
|
|
30
|
+
onProgress: (progress) => console.log(`Loading: ${progress.percentage.toFixed(1)}%`),
|
|
31
|
+
});
|
|
32
|
+
console.log(`Model loaded: ${modelId}`);
|
|
33
|
+
const history = [
|
|
34
|
+
{
|
|
35
|
+
role: "system",
|
|
36
|
+
content: "You are a helpful assistant that can call tools to look up weather and horoscopes.",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
role: "user",
|
|
40
|
+
content: "What's the weather in Tokyo and my horoscope for Aquarius?",
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
const result = completion({ modelId, history, stream: true, tools });
|
|
44
|
+
const tokensTask = (async () => {
|
|
45
|
+
for await (const token of result.tokenStream) {
|
|
46
|
+
process.stdout.write(token);
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
const toolsTask = (async () => {
|
|
50
|
+
for await (const evt of result.toolCallStream) {
|
|
51
|
+
if (evt.type === "toolCall") {
|
|
52
|
+
console.log(`\n-> ${evt.call.name}(${JSON.stringify(evt.call.arguments)})`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
})();
|
|
56
|
+
await Promise.all([tokensTask, toolsTask]);
|
|
57
|
+
const toolCalls = await result.toolCalls;
|
|
58
|
+
console.log("\n\nFinal tool calls:");
|
|
59
|
+
if (toolCalls.length > 0) {
|
|
60
|
+
for (const call of toolCalls) {
|
|
61
|
+
console.log(` - ${call.name}(${JSON.stringify(call.arguments)})`);
|
|
62
|
+
const toolResult = mockExecute(call.name, call.arguments);
|
|
63
|
+
console.log(` result: ${toolResult}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log(" (none)");
|
|
68
|
+
}
|
|
69
|
+
await unloadModel({ modelId, clearStorage: false });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error("Error:", error);
|
|
73
|
+
if (modelId)
|
|
74
|
+
await unloadModel({ modelId, clearStorage: false }).catch(() => { });
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-calling example using the Qwen3.5 dialect.
|
|
3
|
+
*
|
|
4
|
+
* Qwen3.5 emits tool calls in a Pythonic-XML format:
|
|
5
|
+
* <tool_call><function=NAME><parameter=KEY>VALUE</parameter></function></tool_call>
|
|
6
|
+
*
|
|
7
|
+
* The dialect is auto-detected from the model name/path when the model file
|
|
8
|
+
* contains "qwen3.5", "qwen3-5", "qwen3.6", or "qwen3-6". Pass
|
|
9
|
+
* toolDialect: "qwen35" explicitly if auto-detection does not pick it up.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* bun run bare:example dist/examples/tools/llamacpp-tools-qwen35.js <model-url>
|
|
13
|
+
*/
|
|
14
|
+
import { completion, loadModel, unloadModel, } from "@qvac/sdk";
|
|
15
|
+
import { tools, mockExecute } from "./shared.js";
|
|
16
|
+
const QWEN35_HF = "https://huggingface.co/unsloth/Qwen3.5-0.8B-GGUF/resolve/main/Qwen3.5-0.8B-Q8_0.gguf";
|
|
17
|
+
const modelSrc = process.argv[2] ?? QWEN35_HF;
|
|
18
|
+
let modelId;
|
|
19
|
+
try {
|
|
20
|
+
modelId = await loadModel({
|
|
21
|
+
modelSrc,
|
|
22
|
+
modelType: "llm",
|
|
23
|
+
modelConfig: { ctx_size: 4096, tools: true },
|
|
24
|
+
onProgress: (progress) => console.log(`Loading: ${progress.percentage.toFixed(1)}%`),
|
|
25
|
+
});
|
|
26
|
+
console.log(`Model loaded: ${modelId}`);
|
|
27
|
+
const history = [
|
|
28
|
+
{
|
|
29
|
+
role: "system",
|
|
30
|
+
content: "You are a helpful assistant that can call tools to look up weather and horoscopes.",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
role: "user",
|
|
34
|
+
content: "What's the weather in Tokyo and my horoscope for Aquarius?",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
const result = completion({ modelId, history, stream: true, tools });
|
|
38
|
+
const tokensTask = (async () => {
|
|
39
|
+
for await (const token of result.tokenStream) {
|
|
40
|
+
process.stdout.write(token);
|
|
41
|
+
}
|
|
42
|
+
})();
|
|
43
|
+
const toolsTask = (async () => {
|
|
44
|
+
for await (const evt of result.toolCallStream) {
|
|
45
|
+
if (evt.type === "toolCall") {
|
|
46
|
+
console.log(`\n-> ${evt.call.name}(${JSON.stringify(evt.call.arguments)})`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})();
|
|
50
|
+
await Promise.all([tokensTask, toolsTask]);
|
|
51
|
+
const toolCalls = await result.toolCalls;
|
|
52
|
+
console.log("\n\nFinal tool calls:");
|
|
53
|
+
if (toolCalls.length > 0) {
|
|
54
|
+
for (const call of toolCalls) {
|
|
55
|
+
console.log(` - ${call.name}(${JSON.stringify(call.arguments)})`);
|
|
56
|
+
const toolResult = mockExecute(call.name, call.arguments);
|
|
57
|
+
console.log(` result: ${toolResult}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.log(" (none)");
|
|
62
|
+
}
|
|
63
|
+
await unloadModel({ modelId, clearStorage: false });
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error("Error:", error);
|
|
67
|
+
if (modelId)
|
|
68
|
+
await unloadModel({ modelId, clearStorage: false }).catch(() => { });
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microphone → Whisper streaming transcription with conversation events
|
|
3
|
+
* (VAD state updates + end-of-turn detection).
|
|
4
|
+
*
|
|
5
|
+
* Demonstrates the opt-in conversation API:
|
|
6
|
+
* transcribeStream({ modelId, emitVadEvents: true, endOfTurnSilenceMs: N })
|
|
7
|
+
*
|
|
8
|
+
* The session yields a discriminated union of events:
|
|
9
|
+
* - { type: "text", text } transcript chunks
|
|
10
|
+
* - { type: "vad", speaking, probability } VAD state updates
|
|
11
|
+
* - { type: "endOfTurn", silenceDurationMs } silence boundary
|
|
12
|
+
*
|
|
13
|
+
* Speak into your mic; transcripts appear as you pause, VAD ticks while
|
|
14
|
+
* you speak, and an end-of-turn event fires after 800ms of silence.
|
|
15
|
+
* Press Ctrl+C to quit.
|
|
16
|
+
*
|
|
17
|
+
* Requirements: FFmpeg installed, microphone access.
|
|
18
|
+
*/
|
|
19
|
+
import { loadModel, unloadModel, transcribeStream, WHISPER_TINY, VAD_SILERO_5_1_2, } from "@qvac/sdk";
|
|
20
|
+
import { spawnSync } from "child_process";
|
|
21
|
+
import { startMicrophone } from "../audio/mic-input";
|
|
22
|
+
const SAMPLE_RATE = 16000;
|
|
23
|
+
try {
|
|
24
|
+
const r = spawnSync("ffmpeg", ["-version"], { stdio: "ignore" });
|
|
25
|
+
if (r.error || r.status !== 0)
|
|
26
|
+
throw new Error("FFmpeg not found");
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
console.error("❌ Error: FFmpeg is required. Install it and try again.");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
let modelId = null;
|
|
33
|
+
let ffmpeg = null;
|
|
34
|
+
async function cleanup() {
|
|
35
|
+
console.log("\n\nStopping...");
|
|
36
|
+
ffmpeg?.kill();
|
|
37
|
+
if (modelId)
|
|
38
|
+
await unloadModel({ modelId });
|
|
39
|
+
console.log("Done.");
|
|
40
|
+
}
|
|
41
|
+
process.on("SIGINT", () => {
|
|
42
|
+
void cleanup().finally(() => process.exit(0));
|
|
43
|
+
});
|
|
44
|
+
process.on("SIGTERM", () => {
|
|
45
|
+
void cleanup().finally(() => process.exit(0));
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
console.log("Loading model (whisper-tiny + Silero VAD)...");
|
|
49
|
+
modelId = await loadModel({
|
|
50
|
+
modelSrc: WHISPER_TINY,
|
|
51
|
+
modelType: "whisper",
|
|
52
|
+
modelConfig: {
|
|
53
|
+
vadModelSrc: VAD_SILERO_5_1_2,
|
|
54
|
+
audio_format: "f32le",
|
|
55
|
+
strategy: "greedy",
|
|
56
|
+
n_threads: 4,
|
|
57
|
+
language: "en",
|
|
58
|
+
no_timestamps: true,
|
|
59
|
+
suppress_blank: true,
|
|
60
|
+
suppress_nst: true,
|
|
61
|
+
temperature: 0.0,
|
|
62
|
+
vad_params: {
|
|
63
|
+
threshold: 0.6,
|
|
64
|
+
min_speech_duration_ms: 250,
|
|
65
|
+
min_silence_duration_ms: 300,
|
|
66
|
+
max_speech_duration_s: 15.0,
|
|
67
|
+
speech_pad_ms: 100,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
console.log("Model loaded.\n");
|
|
72
|
+
ffmpeg = startMicrophone({ sampleRate: SAMPLE_RATE, format: "f32le" });
|
|
73
|
+
const session = await transcribeStream({
|
|
74
|
+
modelId,
|
|
75
|
+
emitVadEvents: true,
|
|
76
|
+
endOfTurnSilenceMs: 800,
|
|
77
|
+
});
|
|
78
|
+
ffmpeg.stdout.on("data", (chunk) => session.write(chunk));
|
|
79
|
+
console.log("Listening... speak and pause to see transcripts, VAD events, and end-of-turn signals.\n");
|
|
80
|
+
let lastSpeaking = false;
|
|
81
|
+
for await (const event of session) {
|
|
82
|
+
switch (event.type) {
|
|
83
|
+
case "text":
|
|
84
|
+
console.log(`> ${event.text.trim()}`);
|
|
85
|
+
break;
|
|
86
|
+
case "vad":
|
|
87
|
+
if (event.speaking !== lastSpeaking) {
|
|
88
|
+
console.log(`[vad] speaking=${event.speaking} probability=${event.probability.toFixed(2)}`);
|
|
89
|
+
lastSpeaking = event.speaking;
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
case "endOfTurn":
|
|
93
|
+
console.log(`[endOfTurn] silence ${event.silenceDurationMs}ms — turn complete\n`);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.error("❌ Error:", error);
|
|
100
|
+
await cleanup();
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
@@ -12,7 +12,7 @@ try {
|
|
|
12
12
|
language: "en",
|
|
13
13
|
ttsSpeed: 1.05,
|
|
14
14
|
ttsNumInferenceSteps: 5,
|
|
15
|
-
ttsSupertonicMultilingual:
|
|
15
|
+
ttsSupertonicMultilingual: true,
|
|
16
16
|
ttsTextEncoderSrc: TTS_SUPERTONIC2_OFFICIAL_TEXT_ENCODER_SUPERTONE_FP32.src,
|
|
17
17
|
ttsDurationPredictorSrc: TTS_SUPERTONIC2_OFFICIAL_DURATION_PREDICTOR_SUPERTONE_FP32.src,
|
|
18
18
|
ttsVectorEstimatorSrc: TTS_SUPERTONIC2_OFFICIAL_VECTOR_ESTIMATOR_SUPERTONE_FP32.src,
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import type { ExpoConfig } from "expo/config";
|
|
2
|
+
declare const MOBILE_HOSTS: string[];
|
|
2
3
|
/**
|
|
3
|
-
* Expo plugin
|
|
4
|
+
* Expo plugin: bundle, verify, then copy the mobile worker bundle.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Flow: `bundle sdk` -> assert `qvac/worker.bundle.js` exists ->
|
|
7
|
+
* `verify bundle` -> copy to `<sdkPackageDir>/dist/worker.mobile.bundle.js`.
|
|
8
|
+
* Requires a local `@qvac/cli` install. Uses `qvac.config.*` if present.
|
|
8
9
|
*/
|
|
9
10
|
declare function withMobileBundle(config: ExpoConfig): ExpoConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Resolves the qvac CLI command.
|
|
13
|
+
*
|
|
14
|
+
* Prefers local @qvac/cli installation for version consistency,
|
|
15
|
+
* falls back to npx for convenience when CLI is not installed.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveCliCommand(projectRoot: string): string;
|
|
18
|
+
/** Builds the `qvac verify bundle` command string (pure helper for tests). */
|
|
19
|
+
export declare function buildVerifyBundleCommand(opts: {
|
|
20
|
+
cliCommand: string;
|
|
21
|
+
bundlePath: string;
|
|
22
|
+
hosts: string[];
|
|
23
|
+
configPath?: string;
|
|
24
|
+
}): string;
|
|
25
|
+
export { MOBILE_HOSTS };
|
|
10
26
|
export default withMobileBundle;
|
|
11
27
|
//# sourceMappingURL=withMobileBundle.d.ts.map
|