llama-cpp-pro 0.2.2
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/CHANGELOG.md +295 -0
- package/LICENSE +21 -0
- package/LlamaCpp.podspec +33 -0
- package/LlamaCppCapacitor.podspec +33 -0
- package/Package.swift +29 -0
- package/README.md +93 -0
- package/android/build.gradle +88 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/CMakeLists-arm64.txt +138 -0
- package/android/src/main/CMakeLists-x86_64.txt +141 -0
- package/android/src/main/CMakeLists.txt +138 -0
- package/android/src/main/java/ai/annadata/plugin/capacitor/LlamaCpp.java +1340 -0
- package/android/src/main/java/ai/annadata/plugin/capacitor/LlamaCppPlugin.java +814 -0
- package/android/src/main/java/ai/annadata/plugin/capacitor/ModelAdmissionController.java +214 -0
- package/android/src/main/jni-chat-session.cpp +261 -0
- package/android/src/main/jni-lora.cpp +197 -0
- package/android/src/main/jni-multimodal.cpp +167 -0
- package/android/src/main/jni-tts.cpp +290 -0
- package/android/src/main/jni-utils.h +148 -0
- package/android/src/main/jni.cpp +1808 -0
- package/android/src/main/jniLibs/arm64-v8a/libllama-cpp-arm64.so +0 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/build-native.sh +280 -0
- package/cmake/desktop-metal-embed.cmake +30 -0
- package/cmake/desktop-sources.cmake +100 -0
- package/cmake/ggml-backends.cmake +44 -0
- package/cpp/LICENSE +21 -0
- package/cpp/README.md +15 -0
- package/cpp/anyascii.c +22223 -0
- package/cpp/anyascii.h +42 -0
- package/cpp/cap-completion.cpp +942 -0
- package/cpp/cap-completion.h +127 -0
- package/cpp/cap-embedding.cpp +193 -0
- package/cpp/cap-embedding.h +35 -0
- package/cpp/cap-ios-bridge.cpp +1810 -0
- package/cpp/cap-ios-bridge.h +61 -0
- package/cpp/cap-llama.cpp +412 -0
- package/cpp/cap-llama.h +161 -0
- package/cpp/cap-mtmd.hpp +602 -0
- package/cpp/cap-native-server.cpp +1095 -0
- package/cpp/cap-native-server.h +40 -0
- package/cpp/cap-tts.cpp +591 -0
- package/cpp/cap-tts.h +59 -0
- package/cpp/cap-wasm-fs.cpp +156 -0
- package/cpp/cap-wasm-jspi.cpp +19 -0
- package/cpp/cap-wasm-jspi.h +30 -0
- package/cpp/cap-wasm-vfs.cpp +22 -0
- package/cpp/chat-parser.cpp +393 -0
- package/cpp/chat-parser.h +120 -0
- package/cpp/chat.cpp +2315 -0
- package/cpp/chat.h +221 -0
- package/cpp/common.cpp +1664 -0
- package/cpp/common.h +744 -0
- package/cpp/ggml-alloc.c +1028 -0
- package/cpp/ggml-alloc.h +76 -0
- package/cpp/ggml-backend-impl.h +255 -0
- package/cpp/ggml-backend-reg.cpp +600 -0
- package/cpp/ggml-backend.cpp +2121 -0
- package/cpp/ggml-backend.h +354 -0
- package/cpp/ggml-common.h +1878 -0
- package/cpp/ggml-cpp.h +39 -0
- package/cpp/ggml-cpu/amx/amx.cpp +221 -0
- package/cpp/ggml-cpu/amx/amx.h +8 -0
- package/cpp/ggml-cpu/amx/common.h +91 -0
- package/cpp/ggml-cpu/amx/mmq.cpp +2512 -0
- package/cpp/ggml-cpu/amx/mmq.h +10 -0
- package/cpp/ggml-cpu/arch/arm/cpu-feats.cpp +94 -0
- package/cpp/ggml-cpu/arch/arm/quants.c +3650 -0
- package/cpp/ggml-cpu/arch/arm/repack.cpp +1891 -0
- package/cpp/ggml-cpu/arch/x86/cpu-feats.cpp +327 -0
- package/cpp/ggml-cpu/arch/x86/quants.c +3820 -0
- package/cpp/ggml-cpu/arch/x86/repack.cpp +6307 -0
- package/cpp/ggml-cpu/arch-fallback.h +215 -0
- package/cpp/ggml-cpu/binary-ops.cpp +158 -0
- package/cpp/ggml-cpu/binary-ops.h +16 -0
- package/cpp/ggml-cpu/common.h +73 -0
- package/cpp/ggml-cpu/ggml-cpu-impl.h +559 -0
- package/cpp/ggml-cpu/ggml-cpu.c +3578 -0
- package/cpp/ggml-cpu/ggml-cpu.cpp +672 -0
- package/cpp/ggml-cpu/ops.cpp +10587 -0
- package/cpp/ggml-cpu/ops.h +114 -0
- package/cpp/ggml-cpu/quants.c +1193 -0
- package/cpp/ggml-cpu/quants.h +97 -0
- package/cpp/ggml-cpu/repack.cpp +1982 -0
- package/cpp/ggml-cpu/repack.h +120 -0
- package/cpp/ggml-cpu/simd-mappings.h +1184 -0
- package/cpp/ggml-cpu/traits.cpp +36 -0
- package/cpp/ggml-cpu/traits.h +38 -0
- package/cpp/ggml-cpu/unary-ops.cpp +186 -0
- package/cpp/ggml-cpu/unary-ops.h +28 -0
- package/cpp/ggml-cpu/vec.cpp +348 -0
- package/cpp/ggml-cpu/vec.h +1121 -0
- package/cpp/ggml-cpu.h +145 -0
- package/cpp/ggml-impl.h +622 -0
- package/cpp/ggml-metal-impl.h +688 -0
- package/cpp/ggml-metal.h +66 -0
- package/cpp/ggml-metal.m +6833 -0
- package/cpp/ggml-metal.metal +10754 -0
- package/cpp/ggml-opt.cpp +1093 -0
- package/cpp/ggml-opt.h +256 -0
- package/cpp/ggml-quants.c +5324 -0
- package/cpp/ggml-quants.h +106 -0
- package/cpp/ggml-threading.cpp +12 -0
- package/cpp/ggml-threading.h +14 -0
- package/cpp/ggml.c +7108 -0
- package/cpp/ggml.h +2492 -0
- package/cpp/gguf.cpp +1358 -0
- package/cpp/gguf.h +202 -0
- package/cpp/json-partial.cpp +256 -0
- package/cpp/json-partial.h +38 -0
- package/cpp/json-schema-to-grammar.cpp +985 -0
- package/cpp/json-schema-to-grammar.h +21 -0
- package/cpp/llama-adapter.cpp +388 -0
- package/cpp/llama-adapter.h +76 -0
- package/cpp/llama-arch.cpp +2355 -0
- package/cpp/llama-arch.h +499 -0
- package/cpp/llama-batch.cpp +875 -0
- package/cpp/llama-batch.h +160 -0
- package/cpp/llama-chat.cpp +783 -0
- package/cpp/llama-chat.h +65 -0
- package/cpp/llama-context.cpp +2788 -0
- package/cpp/llama-context.h +306 -0
- package/cpp/llama-cparams.cpp +5 -0
- package/cpp/llama-cparams.h +41 -0
- package/cpp/llama-cpp.h +30 -0
- package/cpp/llama-grammar.cpp +1229 -0
- package/cpp/llama-grammar.h +173 -0
- package/cpp/llama-graph.cpp +1891 -0
- package/cpp/llama-graph.h +810 -0
- package/cpp/llama-hparams.cpp +180 -0
- package/cpp/llama-hparams.h +233 -0
- package/cpp/llama-impl.cpp +167 -0
- package/cpp/llama-impl.h +61 -0
- package/cpp/llama-io.cpp +15 -0
- package/cpp/llama-io.h +35 -0
- package/cpp/llama-kv-cache-iswa.cpp +318 -0
- package/cpp/llama-kv-cache-iswa.h +135 -0
- package/cpp/llama-kv-cache.cpp +2059 -0
- package/cpp/llama-kv-cache.h +374 -0
- package/cpp/llama-kv-cells.h +491 -0
- package/cpp/llama-memory-hybrid.cpp +258 -0
- package/cpp/llama-memory-hybrid.h +137 -0
- package/cpp/llama-memory-recurrent.cpp +1146 -0
- package/cpp/llama-memory-recurrent.h +179 -0
- package/cpp/llama-memory.cpp +59 -0
- package/cpp/llama-memory.h +119 -0
- package/cpp/llama-mmap.cpp +609 -0
- package/cpp/llama-mmap.h +68 -0
- package/cpp/llama-model-loader.cpp +1166 -0
- package/cpp/llama-model-loader.h +170 -0
- package/cpp/llama-model-saver.cpp +282 -0
- package/cpp/llama-model-saver.h +37 -0
- package/cpp/llama-model.cpp +19061 -0
- package/cpp/llama-model.h +491 -0
- package/cpp/llama-sampling.cpp +2575 -0
- package/cpp/llama-sampling.h +32 -0
- package/cpp/llama-vocab.cpp +3792 -0
- package/cpp/llama-vocab.h +176 -0
- package/cpp/llama.cpp +358 -0
- package/cpp/llama.h +1373 -0
- package/cpp/log.cpp +428 -0
- package/cpp/log.h +103 -0
- package/cpp/minja/chat-template.hpp +550 -0
- package/cpp/minja/minja.hpp +3009 -0
- package/cpp/nlohmann/json.hpp +25526 -0
- package/cpp/nlohmann/json_fwd.hpp +187 -0
- package/cpp/regex-partial.cpp +204 -0
- package/cpp/regex-partial.h +56 -0
- package/cpp/sampling.cpp +579 -0
- package/cpp/sampling.h +107 -0
- package/cpp/tools/mtmd/clip-impl.h +473 -0
- package/cpp/tools/mtmd/clip.cpp +4322 -0
- package/cpp/tools/mtmd/clip.h +106 -0
- package/cpp/tools/mtmd/miniaudio/miniaudio.h +93468 -0
- package/cpp/tools/mtmd/mtmd-audio.cpp +769 -0
- package/cpp/tools/mtmd/mtmd-audio.h +47 -0
- package/cpp/tools/mtmd/mtmd-helper.cpp +460 -0
- package/cpp/tools/mtmd/mtmd-helper.h +95 -0
- package/cpp/tools/mtmd/mtmd.cpp +1066 -0
- package/cpp/tools/mtmd/mtmd.h +302 -0
- package/cpp/tools/mtmd/stb/stb_image.h +7988 -0
- package/cpp/unicode-data.cpp +7034 -0
- package/cpp/unicode-data.h +20 -0
- package/cpp/unicode.cpp +1061 -0
- package/cpp/unicode.h +68 -0
- package/cpp/vendor/cpp-httplib/httplib.cpp +16509 -0
- package/cpp/vendor/cpp-httplib/httplib.h +3883 -0
- package/desktop/electron-builder.config.cjs +157 -0
- package/desktop/entitlements.mac.plist +12 -0
- package/desktop/package.json +11 -0
- package/desktop/resolve-package-root.cjs +88 -0
- package/desktop/src/main/backend-selector.cjs +148 -0
- package/desktop/src/main/gpu-probe.cjs +142 -0
- package/desktop/src/main/index.cjs +58 -0
- package/desktop/src/main/ipc-handlers.cjs +212 -0
- package/desktop/src/main/model-store.cjs +50 -0
- package/desktop/src/main/preload.cjs +39 -0
- package/desktop/src/main/sidecar-client.cjs +76 -0
- package/desktop/src/main/sidecar-manager.cjs +364 -0
- package/dist/docs.json +14357 -0
- package/dist/esm/definitions.d.ts +731 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/desktop.d.ts +37 -0
- package/dist/esm/desktop.js +133 -0
- package/dist/esm/desktop.js.map +1 -0
- package/dist/esm/index.d.ts +200 -0
- package/dist/esm/index.js +612 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/isomorphic/desktop.runtime.d.ts +37 -0
- package/dist/esm/isomorphic/desktop.runtime.js +36 -0
- package/dist/esm/isomorphic/desktop.runtime.js.map +1 -0
- package/dist/esm/isomorphic/errors.d.ts +6 -0
- package/dist/esm/isomorphic/errors.js +9 -0
- package/dist/esm/isomorphic/errors.js.map +1 -0
- package/dist/esm/isomorphic/model.admission.d.ts +17 -0
- package/dist/esm/isomorphic/model.admission.js +27 -0
- package/dist/esm/isomorphic/model.admission.js.map +1 -0
- package/dist/esm/isomorphic/model.scheduler.d.ts +31 -0
- package/dist/esm/isomorphic/model.scheduler.js +95 -0
- package/dist/esm/isomorphic/model.scheduler.js.map +1 -0
- package/dist/esm/isomorphic/provider.desktop.d.ts +40 -0
- package/dist/esm/isomorphic/provider.desktop.js +411 -0
- package/dist/esm/isomorphic/provider.desktop.js.map +1 -0
- package/dist/esm/isomorphic/provider.factory.d.ts +2 -0
- package/dist/esm/isomorphic/provider.factory.js +16 -0
- package/dist/esm/isomorphic/provider.factory.js.map +1 -0
- package/dist/esm/isomorphic/provider.interface.d.ts +76 -0
- package/dist/esm/isomorphic/provider.interface.js +2 -0
- package/dist/esm/isomorphic/provider.interface.js.map +1 -0
- package/dist/esm/isomorphic/provider.native.d.ts +18 -0
- package/dist/esm/isomorphic/provider.native.js +173 -0
- package/dist/esm/isomorphic/provider.native.js.map +1 -0
- package/dist/esm/isomorphic/provider.web.d.ts +88 -0
- package/dist/esm/isomorphic/provider.web.js +573 -0
- package/dist/esm/isomorphic/provider.web.js.map +1 -0
- package/dist/esm/isomorphic/sidecar-sse.d.ts +14 -0
- package/dist/esm/isomorphic/sidecar-sse.js +76 -0
- package/dist/esm/isomorphic/sidecar-sse.js.map +1 -0
- package/dist/esm/isomorphic/wasmMemoryCalibration.d.ts +26 -0
- package/dist/esm/isomorphic/wasmMemoryCalibration.js +60 -0
- package/dist/esm/isomorphic/wasmMemoryCalibration.js.map +1 -0
- package/dist/esm/isomorphic/wasmMemoryPolicy.d.ts +55 -0
- package/dist/esm/isomorphic/wasmMemoryPolicy.js +93 -0
- package/dist/esm/isomorphic/wasmMemoryPolicy.js.map +1 -0
- package/dist/esm/storage/manifest.d.ts +13 -0
- package/dist/esm/storage/manifest.js +87 -0
- package/dist/esm/storage/manifest.js.map +1 -0
- package/dist/esm/storage/opfs.store.d.ts +31 -0
- package/dist/esm/storage/opfs.store.js +241 -0
- package/dist/esm/storage/opfs.store.js.map +1 -0
- package/dist/esm/web.d.ts +206 -0
- package/dist/esm/web.js +521 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/esm/workers/async-file.d.ts +13 -0
- package/dist/esm/workers/async-file.js +12 -0
- package/dist/esm/workers/async-file.js.map +1 -0
- package/dist/esm/workers/heapfs.d.ts +55 -0
- package/dist/esm/workers/heapfs.js +115 -0
- package/dist/esm/workers/heapfs.js.map +1 -0
- package/dist/esm/workers/wasm.engine.d.ts +86 -0
- package/dist/esm/workers/wasm.engine.js +504 -0
- package/dist/esm/workers/wasm.engine.js.map +1 -0
- package/dist/esm/workers/worker.protocol.d.ts +160 -0
- package/dist/esm/workers/worker.protocol.js +2 -0
- package/dist/esm/workers/worker.protocol.js.map +1 -0
- package/dist/plugin.cjs +3179 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.js +3181 -0
- package/dist/plugin.js.map +1 -0
- package/dist/wasm/llama_engine.d.ts +74 -0
- package/dist/wasm/llama_engine.js +1829 -0
- package/dist/wasm/llama_engine.wasm +0 -0
- package/dist/wasm/llama_engine_emscripten.mjs +2 -0
- package/dist/wasm/package.json +16 -0
- package/dist/workers/llm.worker.js +1226 -0
- package/dist/workers/llm.worker.js.map +7 -0
- package/extraResources/llama-wasm/llama_engine.d.ts +74 -0
- package/extraResources/llama-wasm/llama_engine.js +1829 -0
- package/extraResources/llama-wasm/llama_engine.wasm +0 -0
- package/extraResources/llama-wasm/llama_engine_emscripten.mjs +2 -0
- package/extraResources/llama-wasm/package.json +16 -0
- package/extraResources/sidecar/README.md +11 -0
- package/extraResources/sidecar/darwin-arm64 +0 -0
- package/extraResources/sidecar/darwin-x64 +0 -0
- package/ios/CMakeLists-arm64.txt +161 -0
- package/ios/CMakeLists-x86_64.txt +188 -0
- package/ios/CMakeLists.txt +161 -0
- package/ios/Frameworks/llama-cpp.framework/Info.plist +28 -0
- package/ios/Frameworks/llama-cpp.framework/llama-cpp +0 -0
- package/ios/Sources/LlamaCppCapacitor/LlamaCpp.swift +1365 -0
- package/ios/Sources/LlamaCppCapacitor/LlamaCppPlugin.swift +694 -0
- package/ios/Sources/LlamaCppCapacitor/LlamaNativeBridge.swift +349 -0
- package/ios/Sources/LlamaCppCapacitor/ModelAdmissionController.swift +177 -0
- package/ios/embed-metal-shaders.sh +24 -0
- package/ios/metal-embed.cmake +29 -0
- package/package.json +281 -0
- package/scripts/build-js-preserve-wasm.cjs +53 -0
- package/scripts/build-sidecar-linux.sh +6 -0
- package/scripts/build-sidecar-win.bat +19 -0
- package/scripts/build-sidecar.sh +184 -0
- package/scripts/embed-llama-ios-app-framework.sh +63 -0
- package/scripts/ensure-desktop-sidecar-bundle.cjs +103 -0
- package/scripts/ensure-llama-ios-xcframework.sh +108 -0
- package/scripts/fix-esm-extensions.cjs +45 -0
- package/scripts/prepare-js-dist.cjs +28 -0
- package/scripts/stage-desktop-resources.cjs +116 -0
- package/sidecar/CMakeLists.txt +192 -0
- package/sidecar/cap-sidecar-main.cpp +68 -0
- package/types/llama-cpp-pro.d.ts +441 -0
|
@@ -0,0 +1,1226 @@
|
|
|
1
|
+
// src/workers/async-file.ts
|
|
2
|
+
var canUseAsyncFileRead = (wasmJspiBuild = false) => wasmJspiBuild;
|
|
3
|
+
var asyncReaderFromBytes = (bytes) => ({
|
|
4
|
+
sizeBytes: bytes.byteLength,
|
|
5
|
+
readChunk: (offset, length) => bytes.subarray(offset, Math.min(offset + length, bytes.byteLength))
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/workers/heapfs.ts
|
|
9
|
+
var fsNameToFile = {};
|
|
10
|
+
var fsIdToFile = {};
|
|
11
|
+
var currFileId = 0;
|
|
12
|
+
var patched = false;
|
|
13
|
+
var getHeapU8 = (mod) => {
|
|
14
|
+
const buf = mod.wasmMemory?.buffer ?? mod.HEAPU8?.buffer;
|
|
15
|
+
if (!buf) throw new Error("HeapFS requires WASM linear memory");
|
|
16
|
+
return new Uint8Array(buf);
|
|
17
|
+
};
|
|
18
|
+
var patchStream = (mod, stream) => {
|
|
19
|
+
const name = stream.node.name;
|
|
20
|
+
const f = fsNameToFile[name];
|
|
21
|
+
if (!f) return;
|
|
22
|
+
const heap = getHeapU8(mod);
|
|
23
|
+
const ptr = Number(f.ptr);
|
|
24
|
+
stream.node.contents = heap.subarray(ptr, ptr + f.size);
|
|
25
|
+
stream.node.usedBytes = f.size;
|
|
26
|
+
};
|
|
27
|
+
var patchHeapFS = (mod) => {
|
|
28
|
+
if (patched) return;
|
|
29
|
+
patched = true;
|
|
30
|
+
const ops = mod.MEMFS.stream_ops;
|
|
31
|
+
ops._read = ops._read ?? ops.read;
|
|
32
|
+
ops._write = ops._write ?? ops.write;
|
|
33
|
+
ops._llseek = ops._llseek ?? ops.llseek;
|
|
34
|
+
ops._allocate = ops._allocate ?? ops.allocate;
|
|
35
|
+
ops._mmap = ops._mmap ?? ops.mmap;
|
|
36
|
+
ops._msync = ops._msync ?? ops.msync;
|
|
37
|
+
ops.read = function(stream, ...rest) {
|
|
38
|
+
patchStream(mod, stream);
|
|
39
|
+
return ops._read.call(this, stream, ...rest);
|
|
40
|
+
};
|
|
41
|
+
mod.MEMFS.ops_table.file.stream.read = ops.read;
|
|
42
|
+
ops.llseek = function(stream, ...rest) {
|
|
43
|
+
patchStream(mod, stream);
|
|
44
|
+
return ops._llseek.call(this, stream, ...rest);
|
|
45
|
+
};
|
|
46
|
+
mod.MEMFS.ops_table.file.stream.llseek = ops.llseek;
|
|
47
|
+
ops.mmap = function(stream, length, position, prot, flags) {
|
|
48
|
+
patchStream(mod, stream);
|
|
49
|
+
const name = stream.node.name;
|
|
50
|
+
const f = fsNameToFile[name];
|
|
51
|
+
if (f) {
|
|
52
|
+
return { ptr: Number(f.ptr) + Number(position), allocated: false };
|
|
53
|
+
}
|
|
54
|
+
return ops._mmap.call(this, stream, length, position, prot, flags);
|
|
55
|
+
};
|
|
56
|
+
mod.MEMFS.ops_table.file.stream.mmap = ops.mmap;
|
|
57
|
+
mod.FS.mkdir("/models");
|
|
58
|
+
mod.FS.mount(mod.MEMFS, { root: "." }, "/models");
|
|
59
|
+
};
|
|
60
|
+
var ensureWasmTmpDir = (mod) => {
|
|
61
|
+
const fs = mod.FS;
|
|
62
|
+
if (fs.analyzePath("/tmp").exists) return;
|
|
63
|
+
try {
|
|
64
|
+
if (typeof fs.createPath === "function") {
|
|
65
|
+
fs.createPath("/", "tmp", true, true);
|
|
66
|
+
} else {
|
|
67
|
+
fs.mkdir("/tmp");
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
if (!fs.analyzePath("/tmp").exists) {
|
|
71
|
+
throw new Error("Failed to create MEMFS /tmp for model VFS streaming");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var heapfsAlloc = (mod, name, size, allocBuffer = true) => {
|
|
76
|
+
if (size < 1) throw new Error("HeapFS file size must be > 0");
|
|
77
|
+
const ptr = allocBuffer ? Number(mod.mmapAlloc(size)) : 0;
|
|
78
|
+
const file = { ptr, size, id: currFileId++ };
|
|
79
|
+
fsIdToFile[file.id] = file;
|
|
80
|
+
fsNameToFile[name] = file;
|
|
81
|
+
return file.id;
|
|
82
|
+
};
|
|
83
|
+
var heapfsWrite = (mod, id, buffer, offset) => {
|
|
84
|
+
const f = fsIdToFile[id];
|
|
85
|
+
if (!f) throw new Error(`HeapFS file id ${id} not found`);
|
|
86
|
+
const after = offset + buffer.byteLength;
|
|
87
|
+
if (after > f.size) {
|
|
88
|
+
throw new Error(`HeapFS write out of bounds: ${after} > ${f.size}`);
|
|
89
|
+
}
|
|
90
|
+
getHeapU8(mod).set(buffer, Number(f.ptr) + offset);
|
|
91
|
+
return buffer.byteLength;
|
|
92
|
+
};
|
|
93
|
+
var heapfsModelPath = (basename) => `/models/${basename}`;
|
|
94
|
+
var supportsHeapFS = (mod) => !!mod && typeof mod.mmapAlloc === "function" && typeof mod.MEMFS === "object" && typeof mod.FS === "object";
|
|
95
|
+
|
|
96
|
+
// src/workers/wasm.engine.ts
|
|
97
|
+
var safeJsonParse = (raw, fallback) => {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(raw);
|
|
100
|
+
} catch {
|
|
101
|
+
return fallback;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var LARGE_MODEL_BYTES = 500 * 1024 * 1024;
|
|
105
|
+
var wasmLoadOptsJson = (opts, overrides) => JSON.stringify({ use_mmap: false, ...opts ?? {}, ...overrides });
|
|
106
|
+
var wasmAsyncLoadOptsJson = (opts, overrides) => JSON.stringify({ use_mmap: false, ...opts ?? {}, ...overrides });
|
|
107
|
+
var loadViaAsyncFile = (mod, modelId, sizeBytes, readChunk, opts) => {
|
|
108
|
+
const begin = mod.model_vfs_begin;
|
|
109
|
+
const bind = mod.async_model_bind;
|
|
110
|
+
const finish = mod.load_model_from_vfs;
|
|
111
|
+
const abort = mod.model_vfs_abort;
|
|
112
|
+
if (!begin || !bind || !finish) {
|
|
113
|
+
throw new Error("Wasm module missing JSPI async file exports \u2014 rebuild with: npm run build:wasm:jspi");
|
|
114
|
+
}
|
|
115
|
+
const optsJson = wasmAsyncLoadOptsJson(opts);
|
|
116
|
+
const vfsPath = begin(sizeBytes, optsJson);
|
|
117
|
+
if (!vfsPath) throw new Error("model_vfs_begin returned empty path");
|
|
118
|
+
try {
|
|
119
|
+
bind(vfsPath, sizeBytes, (offset, length) => readChunk(offset, length));
|
|
120
|
+
finish(modelId, vfsPath, optsJson);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
abort?.(vfsPath);
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var isStackOverflowError = (err) => {
|
|
127
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
128
|
+
return /maximum call stack size exceeded/i.test(msg);
|
|
129
|
+
};
|
|
130
|
+
var wasmMemoryDiagnostics = (em) => {
|
|
131
|
+
if (!em) {
|
|
132
|
+
return { wasmMemoryAccessible: false };
|
|
133
|
+
}
|
|
134
|
+
const wasmMem = em.wasmMemory;
|
|
135
|
+
const buffer = wasmMem?.buffer ?? em.HEAPU8?.buffer;
|
|
136
|
+
if (!buffer) {
|
|
137
|
+
return { wasmMemoryAccessible: false };
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
wasmMemoryAccessible: true,
|
|
141
|
+
wasmLinearBytes: buffer.byteLength,
|
|
142
|
+
wasmLinearMb: +(buffer.byteLength / 1024 / 1024).toFixed(1),
|
|
143
|
+
wasmMemoryShared: wasmMem?.buffer instanceof SharedArrayBuffer
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
var resolveModuleCandidates = () => {
|
|
147
|
+
const candidates = [];
|
|
148
|
+
const g = globalThis;
|
|
149
|
+
for (const key of ["__LLAMA_WASM_MODULE_URL__", "__LLAMA_ENGINE_URL__"]) {
|
|
150
|
+
const customUrl = g?.[key];
|
|
151
|
+
if (typeof customUrl === "string" && customUrl.length > 0) {
|
|
152
|
+
candidates.push(customUrl);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const base = new URL("../../wasm/llama_engine.js", import.meta.url).href;
|
|
157
|
+
candidates.push(base);
|
|
158
|
+
candidates.push(new URL("../../dist/wasm/llama_engine.js", import.meta.url).href);
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
const origin = g?.location?.origin ?? "";
|
|
162
|
+
if (origin) {
|
|
163
|
+
candidates.push(`${origin}/llama-cpp/wasm/llama_engine.js`);
|
|
164
|
+
candidates.push(`${origin}/dist/wasm/llama_engine.js`);
|
|
165
|
+
candidates.push(`${origin}/wasm/llama_engine.js`);
|
|
166
|
+
}
|
|
167
|
+
return [...new Set(candidates)];
|
|
168
|
+
};
|
|
169
|
+
var loadWasmModule = async () => {
|
|
170
|
+
let lastError;
|
|
171
|
+
for (const url of resolveModuleCandidates()) {
|
|
172
|
+
try {
|
|
173
|
+
const mod = await import(
|
|
174
|
+
/* @vite-ignore */
|
|
175
|
+
url
|
|
176
|
+
);
|
|
177
|
+
if (mod && typeof mod.default === "function") {
|
|
178
|
+
await mod.default();
|
|
179
|
+
}
|
|
180
|
+
if (typeof mod.load_model === "function" && typeof mod.generate === "function" && typeof mod.embed === "function") {
|
|
181
|
+
return mod;
|
|
182
|
+
}
|
|
183
|
+
lastError = new Error(`Module loaded but missing required exports at ${url}`);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
lastError = error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Unable to load wasm wrapper module (llama_engine.js). Set window.__LLAMA_WASM_MODULE_URL__ to llama_engine.js. Last error: ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
var loadLlamaWasmEngine = async () => {
|
|
193
|
+
const mod = await loadWasmModule();
|
|
194
|
+
const emscripten = () => mod.getEmscriptenModule?.() ?? null;
|
|
195
|
+
const ensureHeapFS = () => {
|
|
196
|
+
const em = emscripten();
|
|
197
|
+
if (!em || !supportsHeapFS(em)) {
|
|
198
|
+
throw new Error("Wasm build missing HeapFS runtime (mmapAlloc/MEMFS/FS) \u2014 rebuild with npm run build:wasm");
|
|
199
|
+
}
|
|
200
|
+
patchHeapFS(em);
|
|
201
|
+
return em;
|
|
202
|
+
};
|
|
203
|
+
const ensureVfsReady = () => {
|
|
204
|
+
const em = emscripten();
|
|
205
|
+
if (em && supportsHeapFS(em)) {
|
|
206
|
+
ensureWasmTmpDir(em);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
return {
|
|
210
|
+
init: async () => {
|
|
211
|
+
(mod.init_engine ?? mod.init)?.();
|
|
212
|
+
const em = emscripten();
|
|
213
|
+
if (em && supportsHeapFS(em)) {
|
|
214
|
+
patchHeapFS(em);
|
|
215
|
+
ensureWasmTmpDir(em);
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
loadModel: async (modelId, modelBuffer, opts) => {
|
|
219
|
+
const bytes = new Uint8Array(modelBuffer);
|
|
220
|
+
const em = emscripten();
|
|
221
|
+
const asyncReady = typeof mod.can_use_async_file === "function" ? mod.can_use_async_file() : canUseAsyncFileRead(em?.__llamaWasmJspi ?? false);
|
|
222
|
+
if (asyncReady) {
|
|
223
|
+
const reader = asyncReaderFromBytes(bytes);
|
|
224
|
+
loadViaAsyncFile(mod, modelId, reader.sizeBytes, reader.readChunk, opts);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const begin = mod.model_vfs_begin;
|
|
228
|
+
const write = mod.model_vfs_write;
|
|
229
|
+
const finish = mod.load_model_from_vfs;
|
|
230
|
+
const abort = mod.model_vfs_abort;
|
|
231
|
+
if (begin && write && finish) {
|
|
232
|
+
ensureVfsReady();
|
|
233
|
+
const optsJson = wasmLoadOptsJson(opts);
|
|
234
|
+
const vfsPath = begin(bytes.length, optsJson);
|
|
235
|
+
if (!vfsPath) throw new Error("model_vfs_begin returned empty path");
|
|
236
|
+
try {
|
|
237
|
+
const CHUNK = 32 * 1024 * 1024;
|
|
238
|
+
for (let offset = 0; offset < bytes.length; offset += CHUNK) {
|
|
239
|
+
write(vfsPath, bytes.subarray(offset, offset + CHUNK));
|
|
240
|
+
}
|
|
241
|
+
finish(modelId, vfsPath, optsJson);
|
|
242
|
+
} catch (err) {
|
|
243
|
+
abort?.(vfsPath);
|
|
244
|
+
throw err;
|
|
245
|
+
}
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const loadModelFn = mod.load_model;
|
|
249
|
+
if (!loadModelFn) throw new Error("Wasm module missing load_model export");
|
|
250
|
+
loadModelFn(modelId, bytes, JSON.stringify(opts ?? {}));
|
|
251
|
+
},
|
|
252
|
+
loadModelFromOpfsReader: async (modelId, reader, opts) => {
|
|
253
|
+
const loadFromPath = mod.load_model_from_path;
|
|
254
|
+
const begin = mod.model_vfs_begin;
|
|
255
|
+
const write = mod.model_vfs_write;
|
|
256
|
+
const finish = mod.load_model_from_vfs;
|
|
257
|
+
const abort = mod.model_vfs_abort;
|
|
258
|
+
const chunkSize = 4 * 1024 * 1024;
|
|
259
|
+
const em = emscripten();
|
|
260
|
+
const asyncReady = typeof mod.can_use_async_file === "function" ? mod.can_use_async_file() : canUseAsyncFileRead(em?.__llamaWasmJspi ?? false);
|
|
261
|
+
const loadViaAsyncOpfs = () => {
|
|
262
|
+
loadViaAsyncFile(
|
|
263
|
+
mod,
|
|
264
|
+
modelId,
|
|
265
|
+
reader.sizeBytes,
|
|
266
|
+
(offset, length) => reader.readChunk(offset, length),
|
|
267
|
+
opts
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
const streamOpfsToVfs = (useMmap) => {
|
|
271
|
+
if (!begin || !write || !finish) {
|
|
272
|
+
throw new Error("Wasm module missing OPFS streaming exports (model_vfs_* / load_model_from_path)");
|
|
273
|
+
}
|
|
274
|
+
ensureVfsReady();
|
|
275
|
+
const vfsPath = begin(reader.sizeBytes, wasmLoadOptsJson(opts, { use_mmap: useMmap }));
|
|
276
|
+
if (!vfsPath) {
|
|
277
|
+
throw new Error("model_vfs_begin returned empty path");
|
|
278
|
+
}
|
|
279
|
+
const optsJson = wasmLoadOptsJson(opts, { use_mmap: useMmap });
|
|
280
|
+
try {
|
|
281
|
+
for (let offset = 0; offset < reader.sizeBytes; ) {
|
|
282
|
+
const chunk = reader.readChunk(offset, chunkSize);
|
|
283
|
+
if (chunk.byteLength === 0) {
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
write(vfsPath, chunk);
|
|
287
|
+
offset += chunk.byteLength;
|
|
288
|
+
}
|
|
289
|
+
finish(modelId, vfsPath, optsJson);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
abort?.(vfsPath);
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const tryHeapFSLoad = () => {
|
|
296
|
+
if (!loadFromPath) {
|
|
297
|
+
throw new Error("Wasm module missing load_model_from_path");
|
|
298
|
+
}
|
|
299
|
+
const emMod = ensureHeapFS();
|
|
300
|
+
const basename = `${modelId.replace(/[^\w.-]/g, "_")}.gguf`;
|
|
301
|
+
const vfsPath = heapfsModelPath(basename);
|
|
302
|
+
emMod.FS.createDataFile("/models", basename, new ArrayBuffer(0), true, true, true);
|
|
303
|
+
const fileId = heapfsAlloc(emMod, basename, reader.sizeBytes, true);
|
|
304
|
+
for (let offset = 0; offset < reader.sizeBytes; ) {
|
|
305
|
+
const chunk = reader.readChunk(offset, chunkSize);
|
|
306
|
+
if (chunk.byteLength === 0) break;
|
|
307
|
+
heapfsWrite(emMod, fileId, chunk, offset);
|
|
308
|
+
offset += chunk.byteLength;
|
|
309
|
+
}
|
|
310
|
+
loadFromPath(modelId, vfsPath, wasmLoadOptsJson(opts, { use_mmap: true }));
|
|
311
|
+
};
|
|
312
|
+
try {
|
|
313
|
+
if (asyncReady) {
|
|
314
|
+
loadViaAsyncOpfs();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const preferVfs = reader.sizeBytes >= LARGE_MODEL_BYTES || opts?.preferVfsStreaming === true;
|
|
318
|
+
if (preferVfs) {
|
|
319
|
+
streamOpfsToVfs(false);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (loadFromPath) {
|
|
323
|
+
try {
|
|
324
|
+
tryHeapFSLoad();
|
|
325
|
+
return;
|
|
326
|
+
} catch (heapErr) {
|
|
327
|
+
const reason = isStackOverflowError(heapErr) ? "HeapFS/mmap caused stack overflow" : "HeapFS load failed";
|
|
328
|
+
console.warn(`[llama-cpp] ${reason}; falling back to VFS streaming:`, heapErr);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
streamOpfsToVfs(false);
|
|
332
|
+
} finally {
|
|
333
|
+
reader.close();
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
unloadModel: async (modelId) => {
|
|
337
|
+
const unloadModel = mod.unload_model;
|
|
338
|
+
if (!unloadModel) throw new Error("Wasm module missing unload_model export");
|
|
339
|
+
unloadModel(modelId);
|
|
340
|
+
},
|
|
341
|
+
generate: async (modelId, req, onToken) => {
|
|
342
|
+
if (onToken && typeof mod.generate_stream === "function") {
|
|
343
|
+
const em = emscripten();
|
|
344
|
+
if (em?.__llamaWasmJspi) {
|
|
345
|
+
em.__llamaStreamOnToken = async (token, index) => {
|
|
346
|
+
onToken(token, index);
|
|
347
|
+
};
|
|
348
|
+
try {
|
|
349
|
+
const raw3 = mod.generate_stream(modelId, JSON.stringify(req ?? {}), () => {
|
|
350
|
+
});
|
|
351
|
+
return safeJsonParse(raw3, {
|
|
352
|
+
text: "",
|
|
353
|
+
tokens_predicted: 0,
|
|
354
|
+
tokens_evaluated: 0,
|
|
355
|
+
finish_reason: "error"
|
|
356
|
+
});
|
|
357
|
+
} finally {
|
|
358
|
+
em.__llamaStreamOnToken = void 0;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const raw2 = mod.generate_stream(modelId, JSON.stringify(req ?? {}), onToken);
|
|
362
|
+
return safeJsonParse(raw2, {
|
|
363
|
+
text: "",
|
|
364
|
+
tokens_predicted: 0,
|
|
365
|
+
tokens_evaluated: 0,
|
|
366
|
+
finish_reason: "error"
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
const generate = mod.generate;
|
|
370
|
+
if (!generate) throw new Error("Wasm module missing generate export");
|
|
371
|
+
const raw = generate(modelId, JSON.stringify(req ?? {}));
|
|
372
|
+
return safeJsonParse(raw, {
|
|
373
|
+
text: "",
|
|
374
|
+
tokens_predicted: 0,
|
|
375
|
+
tokens_evaluated: 0,
|
|
376
|
+
finish_reason: "error"
|
|
377
|
+
});
|
|
378
|
+
},
|
|
379
|
+
embed: async (modelId, input) => {
|
|
380
|
+
const embed = mod.embed;
|
|
381
|
+
if (!embed) throw new Error("Wasm module missing embed export");
|
|
382
|
+
const raw = embed(modelId, JSON.stringify({ input }));
|
|
383
|
+
return safeJsonParse(raw, { vectors: [] });
|
|
384
|
+
},
|
|
385
|
+
tokenize: async (modelId, text) => {
|
|
386
|
+
if (!mod.tokenize) {
|
|
387
|
+
throw new Error("Wasm module missing tokenize export \u2014 rebuild with npm run build:wasm");
|
|
388
|
+
}
|
|
389
|
+
const raw = mod.tokenize(modelId, text);
|
|
390
|
+
const parsed = safeJsonParse(raw, {});
|
|
391
|
+
const tokens = Array.isArray(parsed["tokens"]) ? parsed["tokens"] : [];
|
|
392
|
+
return { tokens, has_media: Boolean(parsed["has_media"]) };
|
|
393
|
+
},
|
|
394
|
+
detokenize: async (modelId, tokens) => {
|
|
395
|
+
if (!mod.detokenize) {
|
|
396
|
+
throw new Error("Wasm module missing detokenize export \u2014 rebuild with npm run build:wasm");
|
|
397
|
+
}
|
|
398
|
+
const raw = mod.detokenize(modelId, JSON.stringify(tokens));
|
|
399
|
+
const parsed = safeJsonParse(raw, {});
|
|
400
|
+
return { text: parsed.text ?? raw };
|
|
401
|
+
},
|
|
402
|
+
convertJsonSchemaToGrammar: async (schemaJson) => {
|
|
403
|
+
if (!mod.convert_json_schema_to_grammar) {
|
|
404
|
+
throw new Error("Wasm module missing convert_json_schema_to_grammar export \u2014 rebuild with npm run build:wasm");
|
|
405
|
+
}
|
|
406
|
+
return mod.convert_json_schema_to_grammar(schemaJson);
|
|
407
|
+
},
|
|
408
|
+
rerank: async (modelId, query, documents) => {
|
|
409
|
+
if (!mod.rerank) {
|
|
410
|
+
throw new Error("Wasm module missing rerank export \u2014 rebuild with npm run build:wasm");
|
|
411
|
+
}
|
|
412
|
+
const raw = mod.rerank(modelId, query, JSON.stringify(documents));
|
|
413
|
+
const parsed = safeJsonParse(raw, []);
|
|
414
|
+
if (!Array.isArray(parsed)) {
|
|
415
|
+
throw new Error(typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : "Invalid rerank response");
|
|
416
|
+
}
|
|
417
|
+
return parsed;
|
|
418
|
+
},
|
|
419
|
+
bench: async (modelId, pp, tg, pl, nr) => {
|
|
420
|
+
if (!mod.bench) {
|
|
421
|
+
throw new Error("Wasm module missing bench export \u2014 rebuild with npm run build:wasm");
|
|
422
|
+
}
|
|
423
|
+
return mod.bench(modelId, pp, tg, pl, nr);
|
|
424
|
+
},
|
|
425
|
+
saveSession: async (modelId, filepath, tokenSize) => {
|
|
426
|
+
if (!mod.save_session) {
|
|
427
|
+
throw new Error("Wasm module missing save_session export \u2014 rebuild with npm run build:wasm");
|
|
428
|
+
}
|
|
429
|
+
const raw = mod.save_session(modelId, filepath, tokenSize);
|
|
430
|
+
const parsed = safeJsonParse(raw, {});
|
|
431
|
+
if (parsed.error) throw new Error(parsed.error);
|
|
432
|
+
return { tokens_saved: parsed.tokens_saved ?? 0 };
|
|
433
|
+
},
|
|
434
|
+
loadSession: async (modelId, filepath) => {
|
|
435
|
+
if (!mod.load_session) {
|
|
436
|
+
throw new Error("Wasm module missing load_session export \u2014 rebuild with npm run build:wasm");
|
|
437
|
+
}
|
|
438
|
+
const raw = mod.load_session(modelId, filepath);
|
|
439
|
+
const parsed = safeJsonParse(raw, {});
|
|
440
|
+
if (parsed.error) throw new Error(parsed.error);
|
|
441
|
+
return {
|
|
442
|
+
tokens_loaded: parsed.tokens_loaded ?? 0,
|
|
443
|
+
prompt: parsed.prompt ?? ""
|
|
444
|
+
};
|
|
445
|
+
},
|
|
446
|
+
applyLoraAdapters: async (modelId, loraAdapters) => {
|
|
447
|
+
if (!mod.apply_lora_adapters) {
|
|
448
|
+
throw new Error("Wasm module missing apply_lora_adapters export \u2014 rebuild with npm run build:wasm");
|
|
449
|
+
}
|
|
450
|
+
mod.apply_lora_adapters(modelId, JSON.stringify(loraAdapters));
|
|
451
|
+
},
|
|
452
|
+
removeLoraAdapters: async (modelId) => {
|
|
453
|
+
if (!mod.remove_lora_adapters) {
|
|
454
|
+
throw new Error("Wasm module missing remove_lora_adapters export \u2014 rebuild with npm run build:wasm");
|
|
455
|
+
}
|
|
456
|
+
mod.remove_lora_adapters(modelId);
|
|
457
|
+
},
|
|
458
|
+
getLoadedLoraAdapters: async (modelId) => {
|
|
459
|
+
if (!mod.get_loaded_lora_adapters) {
|
|
460
|
+
throw new Error("Wasm module missing get_loaded_lora_adapters export \u2014 rebuild with npm run build:wasm");
|
|
461
|
+
}
|
|
462
|
+
const raw = mod.get_loaded_lora_adapters(modelId);
|
|
463
|
+
return safeJsonParse(raw, []);
|
|
464
|
+
},
|
|
465
|
+
initMultimodal: async (modelId, path, useGpu = false) => {
|
|
466
|
+
if (!mod.init_multimodal) {
|
|
467
|
+
throw new Error("Wasm module missing init_multimodal export \u2014 rebuild with npm run build:wasm");
|
|
468
|
+
}
|
|
469
|
+
const raw = mod.init_multimodal(modelId, path, useGpu);
|
|
470
|
+
const parsed = safeJsonParse(raw, {});
|
|
471
|
+
if (parsed.error) throw new Error(parsed.error);
|
|
472
|
+
return !!parsed.ok;
|
|
473
|
+
},
|
|
474
|
+
multimodalStatus: async (modelId) => {
|
|
475
|
+
if (!mod.multimodal_status) {
|
|
476
|
+
throw new Error("Wasm module missing multimodal_status export \u2014 rebuild with npm run build:wasm");
|
|
477
|
+
}
|
|
478
|
+
const raw = mod.multimodal_status(modelId);
|
|
479
|
+
return safeJsonParse(raw, {
|
|
480
|
+
enabled: false,
|
|
481
|
+
vision: false,
|
|
482
|
+
audio: false
|
|
483
|
+
});
|
|
484
|
+
},
|
|
485
|
+
releaseMultimodal: async (modelId) => {
|
|
486
|
+
mod.release_multimodal?.(modelId);
|
|
487
|
+
},
|
|
488
|
+
initVocoder: async (modelId, path, nBatch = 512) => {
|
|
489
|
+
if (!mod.init_vocoder) {
|
|
490
|
+
throw new Error("Wasm module missing init_vocoder export \u2014 rebuild with npm run build:wasm");
|
|
491
|
+
}
|
|
492
|
+
const raw = mod.init_vocoder(modelId, path, nBatch);
|
|
493
|
+
const parsed = safeJsonParse(raw, {});
|
|
494
|
+
if (parsed.error) throw new Error(parsed.error);
|
|
495
|
+
return !!parsed.ok;
|
|
496
|
+
},
|
|
497
|
+
vocoderEnabled: async (modelId) => {
|
|
498
|
+
if (!mod.vocoder_enabled) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
const raw = mod.vocoder_enabled(modelId);
|
|
502
|
+
const parsed = safeJsonParse(raw, {});
|
|
503
|
+
return !!parsed.enabled;
|
|
504
|
+
},
|
|
505
|
+
releaseVocoder: async (modelId) => {
|
|
506
|
+
mod.release_vocoder?.(modelId);
|
|
507
|
+
},
|
|
508
|
+
formattedAudioCompletion: async (modelId, speakerJson, textToSpeak) => {
|
|
509
|
+
if (!mod.formatted_audio_completion) {
|
|
510
|
+
throw new Error("Wasm module missing formatted_audio_completion export \u2014 rebuild with npm run build:wasm");
|
|
511
|
+
}
|
|
512
|
+
const raw = mod.formatted_audio_completion(modelId, speakerJson, textToSpeak);
|
|
513
|
+
const parsed = safeJsonParse(raw, {});
|
|
514
|
+
if (parsed.error) throw new Error(parsed.error);
|
|
515
|
+
return { prompt: parsed.prompt ?? "", grammar: parsed.grammar };
|
|
516
|
+
},
|
|
517
|
+
audioGuideTokens: async (modelId, textToSpeak) => {
|
|
518
|
+
if (!mod.audio_guide_tokens) {
|
|
519
|
+
throw new Error("Wasm module missing audio_guide_tokens export \u2014 rebuild with npm run build:wasm");
|
|
520
|
+
}
|
|
521
|
+
const raw = mod.audio_guide_tokens(modelId, textToSpeak);
|
|
522
|
+
const parsed = safeJsonParse(raw, []);
|
|
523
|
+
if (!Array.isArray(parsed)) {
|
|
524
|
+
throw new Error(typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : "Invalid audio guide tokens response");
|
|
525
|
+
}
|
|
526
|
+
return parsed;
|
|
527
|
+
},
|
|
528
|
+
decodeAudioTokens: async (modelId, tokens) => {
|
|
529
|
+
if (!mod.decode_audio_tokens) {
|
|
530
|
+
throw new Error("Wasm module missing decode_audio_tokens export \u2014 rebuild with npm run build:wasm");
|
|
531
|
+
}
|
|
532
|
+
const raw = mod.decode_audio_tokens(modelId, JSON.stringify(tokens));
|
|
533
|
+
const parsed = safeJsonParse(raw, []);
|
|
534
|
+
if (!Array.isArray(parsed)) {
|
|
535
|
+
throw new Error(typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : "Invalid decode audio response");
|
|
536
|
+
}
|
|
537
|
+
return parsed;
|
|
538
|
+
},
|
|
539
|
+
health: async () => {
|
|
540
|
+
const base = mod.health ? safeJsonParse(mod.health(), {}) : {};
|
|
541
|
+
const em = emscripten();
|
|
542
|
+
return {
|
|
543
|
+
...base,
|
|
544
|
+
...wasmMemoryDiagnostics(em),
|
|
545
|
+
wasmJspi: em?.__llamaWasmJspi ?? false,
|
|
546
|
+
wasmAsyncFile: em?.__llamaWasmAsyncFile ?? mod.can_use_async_file?.() ?? false,
|
|
547
|
+
wasmPthread: em?.__llamaWasmPthread ?? false
|
|
548
|
+
};
|
|
549
|
+
},
|
|
550
|
+
memory: async () => {
|
|
551
|
+
const em = emscripten();
|
|
552
|
+
return { pressure: "unknown", ...wasmMemoryDiagnostics(em) };
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// src/isomorphic/errors.ts
|
|
558
|
+
var LlmError = class extends Error {
|
|
559
|
+
constructor(code, message, meta) {
|
|
560
|
+
super(message);
|
|
561
|
+
this.name = "LlmError";
|
|
562
|
+
this.code = code;
|
|
563
|
+
this.meta = meta;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/storage/manifest.ts
|
|
568
|
+
var MANIFEST_FILE = ".llm-manifest.json";
|
|
569
|
+
var getStorageApi = () => {
|
|
570
|
+
const storageApi = globalThis?.navigator?.storage;
|
|
571
|
+
if (!storageApi || typeof storageApi.getDirectory !== "function") {
|
|
572
|
+
throw new LlmError(
|
|
573
|
+
"STORAGE_UNAVAILABLE",
|
|
574
|
+
"OPFS is not available in this runtime. navigator.storage.getDirectory is missing."
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return storageApi;
|
|
578
|
+
};
|
|
579
|
+
var getRootDirectory = async () => {
|
|
580
|
+
const storageApi = getStorageApi();
|
|
581
|
+
try {
|
|
582
|
+
return await storageApi.getDirectory();
|
|
583
|
+
} catch (error) {
|
|
584
|
+
throw new LlmError("STORAGE_IO_FAILED", "Failed to access OPFS root directory.", {
|
|
585
|
+
cause: String(error)
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
var readTextFile = async (fileHandle) => {
|
|
590
|
+
const file = await fileHandle.getFile();
|
|
591
|
+
return file.text();
|
|
592
|
+
};
|
|
593
|
+
var writeTextFile = async (fileHandle, content) => {
|
|
594
|
+
const writable = await fileHandle.createWritable();
|
|
595
|
+
try {
|
|
596
|
+
await writable.write(content);
|
|
597
|
+
} finally {
|
|
598
|
+
await writable.close();
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
async function loadManifestInternal() {
|
|
602
|
+
const root = await getRootDirectory();
|
|
603
|
+
try {
|
|
604
|
+
const handle = await root.getFileHandle(MANIFEST_FILE, { create: true });
|
|
605
|
+
const content = await readTextFile(handle);
|
|
606
|
+
if (!content.trim()) {
|
|
607
|
+
return {};
|
|
608
|
+
}
|
|
609
|
+
const parsed = JSON.parse(content);
|
|
610
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
611
|
+
return {};
|
|
612
|
+
}
|
|
613
|
+
return parsed;
|
|
614
|
+
} catch (error) {
|
|
615
|
+
throw new LlmError("STORAGE_IO_FAILED", "Failed to read OPFS manifest.", {
|
|
616
|
+
cause: String(error)
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async function saveManifestInternal(manifest) {
|
|
621
|
+
const root = await getRootDirectory();
|
|
622
|
+
try {
|
|
623
|
+
const handle = await root.getFileHandle(MANIFEST_FILE, { create: true });
|
|
624
|
+
await writeTextFile(handle, JSON.stringify(manifest, null, 2));
|
|
625
|
+
} catch (error) {
|
|
626
|
+
throw new LlmError("STORAGE_IO_FAILED", "Failed to write OPFS manifest.", {
|
|
627
|
+
cause: String(error)
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
async function getManifestEntry(modelId) {
|
|
632
|
+
const manifest = await loadManifestInternal();
|
|
633
|
+
return manifest[modelId];
|
|
634
|
+
}
|
|
635
|
+
async function upsertManifestEntry(entry) {
|
|
636
|
+
const manifest = await loadManifestInternal();
|
|
637
|
+
manifest[entry.modelId] = entry;
|
|
638
|
+
await saveManifestInternal(manifest);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/storage/opfs.store.ts
|
|
642
|
+
var getStorageApi2 = () => {
|
|
643
|
+
const storageApi = globalThis?.navigator?.storage;
|
|
644
|
+
if (!storageApi || typeof storageApi.getDirectory !== "function") {
|
|
645
|
+
throw new LlmError(
|
|
646
|
+
"STORAGE_UNAVAILABLE",
|
|
647
|
+
"OPFS is not available in this runtime. navigator.storage.getDirectory is missing."
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
return storageApi;
|
|
651
|
+
};
|
|
652
|
+
var getRootDirectory2 = async () => {
|
|
653
|
+
const storageApi = getStorageApi2();
|
|
654
|
+
try {
|
|
655
|
+
return await storageApi.getDirectory();
|
|
656
|
+
} catch (error) {
|
|
657
|
+
throw new LlmError("STORAGE_IO_FAILED", "Failed to access OPFS root directory.", {
|
|
658
|
+
cause: String(error)
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
var ensureParentDirAndFileHandle = async (path, create = true) => {
|
|
663
|
+
const root = await getRootDirectory2();
|
|
664
|
+
const parts = path.split("/").filter(Boolean);
|
|
665
|
+
const fileName = parts.pop();
|
|
666
|
+
if (!fileName) {
|
|
667
|
+
throw new LlmError("STORAGE_IO_FAILED", `Invalid OPFS path '${path}'.`);
|
|
668
|
+
}
|
|
669
|
+
let current = root;
|
|
670
|
+
for (const dir of parts) {
|
|
671
|
+
current = await current.getDirectoryHandle(dir, { create: true });
|
|
672
|
+
}
|
|
673
|
+
return current.getFileHandle(fileName, { create });
|
|
674
|
+
};
|
|
675
|
+
var OPFS_MODEL_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
676
|
+
async function openOpfsModelSyncReader(modelId) {
|
|
677
|
+
const entry = await getManifestEntry(modelId);
|
|
678
|
+
if (!entry) {
|
|
679
|
+
throw new LlmError("MODEL_NOT_LOADED", `Model '${modelId}' is not present in OPFS manifest.`);
|
|
680
|
+
}
|
|
681
|
+
const fileHandle = await ensureParentDirAndFileHandle(entry.path, false);
|
|
682
|
+
if (typeof fileHandle.createSyncAccessHandle !== "function") {
|
|
683
|
+
throw new LlmError(
|
|
684
|
+
"STORAGE_UNAVAILABLE",
|
|
685
|
+
"OPFS sync access handles are not available in this browser/worker context.",
|
|
686
|
+
{ modelId }
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
let accessHandle;
|
|
690
|
+
try {
|
|
691
|
+
accessHandle = await fileHandle.createSyncAccessHandle();
|
|
692
|
+
} catch (error) {
|
|
693
|
+
throw new LlmError("STORAGE_IO_FAILED", `Failed to open OPFS sync handle for '${modelId}'.`, {
|
|
694
|
+
modelId,
|
|
695
|
+
path: entry.path,
|
|
696
|
+
cause: String(error)
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
const sizeBytes = accessHandle.getSize();
|
|
700
|
+
await upsertManifestEntry({ ...entry, lastUsedAt: Date.now() });
|
|
701
|
+
return {
|
|
702
|
+
sizeBytes,
|
|
703
|
+
readChunk(offset, length = OPFS_MODEL_CHUNK_BYTES) {
|
|
704
|
+
const toRead = Math.min(length, sizeBytes - offset);
|
|
705
|
+
if (toRead <= 0) {
|
|
706
|
+
return new Uint8Array(0);
|
|
707
|
+
}
|
|
708
|
+
const buf = new Uint8Array(toRead);
|
|
709
|
+
const bytesRead = accessHandle.read(buf, { at: offset });
|
|
710
|
+
return buf.subarray(0, bytesRead);
|
|
711
|
+
},
|
|
712
|
+
close() {
|
|
713
|
+
accessHandle.close();
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
async function readModelBufferFromOpfs(modelId) {
|
|
718
|
+
const file = await readModelFromOpfs(modelId);
|
|
719
|
+
const buffer = await file.arrayBuffer();
|
|
720
|
+
return { buffer, sizeBytes: file.size };
|
|
721
|
+
}
|
|
722
|
+
async function readModelFromOpfs(modelId) {
|
|
723
|
+
const entry = await getManifestEntry(modelId);
|
|
724
|
+
if (!entry) {
|
|
725
|
+
throw new LlmError("MODEL_NOT_LOADED", `Model '${modelId}' is not present in OPFS manifest.`);
|
|
726
|
+
}
|
|
727
|
+
try {
|
|
728
|
+
const fileHandle = await ensureParentDirAndFileHandle(entry.path, false);
|
|
729
|
+
const file = await fileHandle.getFile();
|
|
730
|
+
await upsertManifestEntry({ ...entry, lastUsedAt: Date.now() });
|
|
731
|
+
return file;
|
|
732
|
+
} catch (error) {
|
|
733
|
+
throw new LlmError("STORAGE_IO_FAILED", `Failed to read model '${modelId}' from OPFS.`, {
|
|
734
|
+
modelId,
|
|
735
|
+
path: entry.path,
|
|
736
|
+
cause: String(error)
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/isomorphic/wasmMemoryCalibration.ts
|
|
742
|
+
var WARM_HEAP_BYTES = 64 * 1024 * 1024;
|
|
743
|
+
|
|
744
|
+
// src/isomorphic/wasmMemoryPolicy.ts
|
|
745
|
+
var WASM_MAX_CONCURRENT_MODELS = 5;
|
|
746
|
+
var WASM_POOL_RESERVE_BYTES = 64 * 1024 * 1024;
|
|
747
|
+
|
|
748
|
+
// src/workers/llm.worker.ts
|
|
749
|
+
var state = {
|
|
750
|
+
initialized: false,
|
|
751
|
+
wasmEngineContextInitialized: false,
|
|
752
|
+
loadedModels: /* @__PURE__ */ new Set(),
|
|
753
|
+
modelLoadOutcomes: /* @__PURE__ */ new Map(),
|
|
754
|
+
modelLoadFailureReasons: /* @__PURE__ */ new Map(),
|
|
755
|
+
modelLoadInflight: /* @__PURE__ */ new Map(),
|
|
756
|
+
engine: null
|
|
757
|
+
};
|
|
758
|
+
var isModelReadyInWorker = (modelId) => state.wasmEngineContextInitialized && state.engine != null && state.loadedModels.has(modelId);
|
|
759
|
+
var clearModelLoadMemo = (modelId) => {
|
|
760
|
+
state.loadedModels.delete(modelId);
|
|
761
|
+
state.modelLoadOutcomes.delete(modelId);
|
|
762
|
+
state.modelLoadFailureReasons.delete(modelId);
|
|
763
|
+
state.modelLoadInflight.delete(modelId);
|
|
764
|
+
};
|
|
765
|
+
var postEvent = (evt) => {
|
|
766
|
+
self.postMessage(evt);
|
|
767
|
+
};
|
|
768
|
+
var postError = (id, code, message, meta) => {
|
|
769
|
+
postEvent({ id, type: "ERROR", code, message, meta });
|
|
770
|
+
};
|
|
771
|
+
var ensureEngine = () => {
|
|
772
|
+
if (!state.engine) {
|
|
773
|
+
throw new Error("WASM engine is not initialized. Send INIT first.");
|
|
774
|
+
}
|
|
775
|
+
return state.engine;
|
|
776
|
+
};
|
|
777
|
+
var tryLoadEngine = async () => {
|
|
778
|
+
const engine = await loadLlamaWasmEngine();
|
|
779
|
+
if (!engine || typeof engine.loadModel !== "function" || typeof engine.generate !== "function" || typeof engine.embed !== "function") {
|
|
780
|
+
throw new Error("Loaded wasm engine does not expose required methods.");
|
|
781
|
+
}
|
|
782
|
+
return engine;
|
|
783
|
+
};
|
|
784
|
+
var loadModelFromOpfs = async (engine, modelId, opts) => {
|
|
785
|
+
if (typeof engine.loadModelFromOpfsReader !== "function") {
|
|
786
|
+
throw new Error(
|
|
787
|
+
"WASM module is missing OPFS streaming exports (model_vfs_*). Rebuild with: npm run build:wasm"
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
try {
|
|
791
|
+
const reader = await openOpfsModelSyncReader(modelId);
|
|
792
|
+
await engine.loadModelFromOpfsReader(modelId, reader, opts);
|
|
793
|
+
return;
|
|
794
|
+
} catch (error) {
|
|
795
|
+
const code = error?.code;
|
|
796
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
797
|
+
const syncUnavailable = code === "STORAGE_UNAVAILABLE" || /sync access handle/i.test(message);
|
|
798
|
+
if (!syncUnavailable) {
|
|
799
|
+
throw error;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
console.warn(
|
|
803
|
+
`[llama-cpp] OPFS sync access handle unavailable for '${modelId}'; falling back to full-buffer load (not suitable for models >2GB).`
|
|
804
|
+
);
|
|
805
|
+
const { buffer, sizeBytes } = await readModelBufferFromOpfs(modelId);
|
|
806
|
+
await engine.loadModel(modelId, buffer, { ...opts ?? {}, modelBytes: sizeBytes });
|
|
807
|
+
};
|
|
808
|
+
self.onmessage = async (evt) => {
|
|
809
|
+
const req = evt.data;
|
|
810
|
+
try {
|
|
811
|
+
switch (req.type) {
|
|
812
|
+
case "INIT": {
|
|
813
|
+
if (!state.initialized) {
|
|
814
|
+
state.engine = await tryLoadEngine();
|
|
815
|
+
await state.engine.init?.();
|
|
816
|
+
state.initialized = true;
|
|
817
|
+
state.wasmEngineContextInitialized = true;
|
|
818
|
+
}
|
|
819
|
+
postEvent({
|
|
820
|
+
id: req.id,
|
|
821
|
+
type: "RESULT",
|
|
822
|
+
payload: { ok: true, initialized: state.initialized }
|
|
823
|
+
});
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
case "LOAD_MODEL": {
|
|
827
|
+
if (!state.initialized) {
|
|
828
|
+
postError(req.id, "WASM_INIT_FAILED", "Worker is not initialized. Send INIT first.");
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
const requestModelId = req.modelId;
|
|
832
|
+
if (isModelReadyInWorker(requestModelId)) {
|
|
833
|
+
postEvent({
|
|
834
|
+
id: req.id,
|
|
835
|
+
type: "RESULT",
|
|
836
|
+
payload: { ok: true, modelId: requestModelId, alreadyLoaded: true, ready: true }
|
|
837
|
+
});
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
const cachedFailure = state.modelLoadFailureReasons.get(requestModelId);
|
|
841
|
+
if (state.modelLoadOutcomes.get(requestModelId) === "failed" && cachedFailure) {
|
|
842
|
+
postError(req.id, "INSUFFICIENT_MEMORY", cachedFailure, {
|
|
843
|
+
modelId: requestModelId,
|
|
844
|
+
cachedFailure: true
|
|
845
|
+
});
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
let inflight = state.modelLoadInflight.get(requestModelId);
|
|
849
|
+
if (!inflight) {
|
|
850
|
+
const engine = ensureEngine();
|
|
851
|
+
inflight = (async () => {
|
|
852
|
+
try {
|
|
853
|
+
await loadModelFromOpfs(engine, requestModelId, req.opts);
|
|
854
|
+
state.loadedModels.add(requestModelId);
|
|
855
|
+
state.modelLoadOutcomes.set(requestModelId, "loaded");
|
|
856
|
+
state.modelLoadFailureReasons.delete(requestModelId);
|
|
857
|
+
} catch (readErr) {
|
|
858
|
+
const reason = readErr instanceof Error ? readErr.message : String(readErr);
|
|
859
|
+
state.modelLoadOutcomes.set(requestModelId, "failed");
|
|
860
|
+
state.modelLoadFailureReasons.set(requestModelId, reason);
|
|
861
|
+
throw readErr;
|
|
862
|
+
}
|
|
863
|
+
})().finally(() => {
|
|
864
|
+
state.modelLoadInflight.delete(requestModelId);
|
|
865
|
+
});
|
|
866
|
+
state.modelLoadInflight.set(requestModelId, inflight);
|
|
867
|
+
}
|
|
868
|
+
try {
|
|
869
|
+
await inflight;
|
|
870
|
+
let measuredFootprintBytes;
|
|
871
|
+
let wasmLinearBytes;
|
|
872
|
+
try {
|
|
873
|
+
const engine = ensureEngine();
|
|
874
|
+
const mem = engine ? await engine.memory?.() : void 0;
|
|
875
|
+
if (mem && typeof mem.wasmLinearBytes === "number") {
|
|
876
|
+
wasmLinearBytes = mem.wasmLinearBytes;
|
|
877
|
+
}
|
|
878
|
+
if (Array.isArray(mem?.loadedModels)) {
|
|
879
|
+
const row = mem.loadedModels.find(
|
|
880
|
+
(m) => m?.modelId === requestModelId
|
|
881
|
+
);
|
|
882
|
+
if (row && typeof row.measuredFootprintBytes === "number" && row.measuredFootprintBytes > 0) {
|
|
883
|
+
measuredFootprintBytes = row.measuredFootprintBytes;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
888
|
+
postEvent({
|
|
889
|
+
id: req.id,
|
|
890
|
+
type: "RESULT",
|
|
891
|
+
payload: {
|
|
892
|
+
ok: true,
|
|
893
|
+
modelId: requestModelId,
|
|
894
|
+
ready: true,
|
|
895
|
+
wasmLinearBytes,
|
|
896
|
+
measuredFootprintBytes
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
} catch (readErr) {
|
|
900
|
+
const reason = state.modelLoadFailureReasons.get(requestModelId) ?? (readErr instanceof Error ? readErr.message : String(readErr));
|
|
901
|
+
postError(
|
|
902
|
+
req.id,
|
|
903
|
+
"STORAGE_IO_FAILED",
|
|
904
|
+
`Failed to read model '${requestModelId}' from OPFS in worker: ${reason}`,
|
|
905
|
+
{ modelId: requestModelId, cachedFailure: true }
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
case "UNLOAD_MODEL": {
|
|
911
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
912
|
+
clearModelLoadMemo(req.modelId);
|
|
913
|
+
postEvent({
|
|
914
|
+
id: req.id,
|
|
915
|
+
type: "RESULT",
|
|
916
|
+
payload: { ok: true, modelId: req.modelId, alreadyUnloaded: true }
|
|
917
|
+
});
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const engine = ensureEngine();
|
|
921
|
+
await engine.unloadModel(req.modelId);
|
|
922
|
+
clearModelLoadMemo(req.modelId);
|
|
923
|
+
postEvent({
|
|
924
|
+
id: req.id,
|
|
925
|
+
type: "RESULT",
|
|
926
|
+
payload: { ok: true, modelId: req.modelId }
|
|
927
|
+
});
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
case "GENERATE": {
|
|
931
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
932
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
const engine = ensureEngine();
|
|
936
|
+
const onToken = req.req.stream ? (token, index) => {
|
|
937
|
+
postEvent({
|
|
938
|
+
id: req.id,
|
|
939
|
+
type: "TOKEN",
|
|
940
|
+
modelId: req.modelId,
|
|
941
|
+
token,
|
|
942
|
+
index
|
|
943
|
+
});
|
|
944
|
+
} : void 0;
|
|
945
|
+
const result = await engine.generate(req.modelId, req.req, onToken);
|
|
946
|
+
postEvent({
|
|
947
|
+
id: req.id,
|
|
948
|
+
type: "RESULT",
|
|
949
|
+
payload: result
|
|
950
|
+
});
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
case "EMBED": {
|
|
954
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
955
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
const engine = ensureEngine();
|
|
959
|
+
const result = await engine.embed(req.modelId, req.input);
|
|
960
|
+
postEvent({
|
|
961
|
+
id: req.id,
|
|
962
|
+
type: "RESULT",
|
|
963
|
+
payload: result
|
|
964
|
+
});
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
case "TOKENIZE": {
|
|
968
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
969
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const engine = ensureEngine();
|
|
973
|
+
if (typeof engine.tokenize !== "function") {
|
|
974
|
+
postError(req.id, "INFERENCE_FAILED", "tokenize is not supported by this WASM build \u2014 rebuild with npm run build:wasm");
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
const result = await engine.tokenize(req.modelId, req.text);
|
|
978
|
+
postEvent({ id: req.id, type: "RESULT", payload: result });
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
case "DETOKENIZE": {
|
|
982
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
983
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
const engine = ensureEngine();
|
|
987
|
+
if (typeof engine.detokenize !== "function") {
|
|
988
|
+
postError(req.id, "INFERENCE_FAILED", "detokenize is not supported by this WASM build \u2014 rebuild with npm run build:wasm");
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const result = await engine.detokenize(req.modelId, req.tokens);
|
|
992
|
+
postEvent({ id: req.id, type: "RESULT", payload: result });
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
case "CONVERT_GRAMMAR": {
|
|
996
|
+
const engine = ensureEngine();
|
|
997
|
+
if (typeof engine.convertJsonSchemaToGrammar !== "function") {
|
|
998
|
+
postError(req.id, "INFERENCE_FAILED", "convertJsonSchemaToGrammar is not supported by this WASM build \u2014 rebuild with npm run build:wasm");
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
const grammar = await engine.convertJsonSchemaToGrammar(req.schemaJson);
|
|
1002
|
+
postEvent({ id: req.id, type: "RESULT", payload: { grammar } });
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
case "HEALTH": {
|
|
1006
|
+
const details = state.engine ? await state.engine.health?.() : void 0;
|
|
1007
|
+
postEvent({
|
|
1008
|
+
id: req.id,
|
|
1009
|
+
type: "RESULT",
|
|
1010
|
+
payload: {
|
|
1011
|
+
ok: state.initialized,
|
|
1012
|
+
initialized: state.initialized,
|
|
1013
|
+
loadedModels: state.loadedModels.size,
|
|
1014
|
+
details: details ?? {}
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
case "MEMORY": {
|
|
1020
|
+
const details = state.engine ? await state.engine.memory?.() : void 0;
|
|
1021
|
+
const loadedModelIds = [...state.loadedModels];
|
|
1022
|
+
postEvent({
|
|
1023
|
+
id: req.id,
|
|
1024
|
+
type: "RESULT",
|
|
1025
|
+
payload: {
|
|
1026
|
+
pressure: "unknown",
|
|
1027
|
+
loadedModelIds,
|
|
1028
|
+
loadedModelCount: loadedModelIds.length,
|
|
1029
|
+
maxModels: WASM_MAX_CONCURRENT_MODELS,
|
|
1030
|
+
...details ?? {}
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
case "RERANK": {
|
|
1036
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1037
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const engine = ensureEngine();
|
|
1041
|
+
if (typeof engine.rerank !== "function") {
|
|
1042
|
+
postError(req.id, "INFERENCE_FAILED", "rerank is not supported by this WASM build");
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
const results = await engine.rerank(req.modelId, req.query, req.documents);
|
|
1046
|
+
postEvent({ id: req.id, type: "RESULT", payload: { results } });
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
case "BENCH": {
|
|
1050
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1051
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
const engine = ensureEngine();
|
|
1055
|
+
if (typeof engine.bench !== "function") {
|
|
1056
|
+
postError(req.id, "INFERENCE_FAILED", "bench is not supported by this WASM build");
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const result = await engine.bench(req.modelId, req.pp, req.tg, req.pl, req.nr);
|
|
1060
|
+
postEvent({ id: req.id, type: "RESULT", payload: { result } });
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
case "SAVE_SESSION": {
|
|
1064
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1065
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
const engine = ensureEngine();
|
|
1069
|
+
if (typeof engine.saveSession !== "function") {
|
|
1070
|
+
postError(req.id, "INFERENCE_FAILED", "saveSession is not supported by this WASM build");
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
const result = await engine.saveSession(req.modelId, req.filepath, req.tokenSize);
|
|
1074
|
+
postEvent({ id: req.id, type: "RESULT", payload: result });
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
case "LOAD_SESSION": {
|
|
1078
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1079
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
const engine = ensureEngine();
|
|
1083
|
+
if (typeof engine.loadSession !== "function") {
|
|
1084
|
+
postError(req.id, "INFERENCE_FAILED", "loadSession is not supported by this WASM build");
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const result = await engine.loadSession(req.modelId, req.filepath);
|
|
1088
|
+
postEvent({ id: req.id, type: "RESULT", payload: result });
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
case "APPLY_LORA": {
|
|
1092
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1093
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
const engine = ensureEngine();
|
|
1097
|
+
if (typeof engine.applyLoraAdapters !== "function") {
|
|
1098
|
+
postError(req.id, "INFERENCE_FAILED", "applyLoraAdapters is not supported by this WASM build");
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
await engine.applyLoraAdapters(req.modelId, req.loraAdapters);
|
|
1102
|
+
postEvent({ id: req.id, type: "RESULT", payload: { ok: true } });
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
case "REMOVE_LORA": {
|
|
1106
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1107
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
const engine = ensureEngine();
|
|
1111
|
+
await engine.removeLoraAdapters?.(req.modelId);
|
|
1112
|
+
postEvent({ id: req.id, type: "RESULT", payload: { ok: true } });
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
case "GET_LORA": {
|
|
1116
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1117
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
const engine = ensureEngine();
|
|
1121
|
+
const adapters = await engine.getLoadedLoraAdapters?.(req.modelId) ?? [];
|
|
1122
|
+
postEvent({ id: req.id, type: "RESULT", payload: { adapters } });
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
case "INIT_MULTIMODAL": {
|
|
1126
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1127
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
const engine = ensureEngine();
|
|
1131
|
+
const ok = await engine.initMultimodal?.(req.modelId, req.path, req.useGpu ?? false);
|
|
1132
|
+
postEvent({ id: req.id, type: "RESULT", payload: { ok: !!ok } });
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
case "MULTIMODAL_STATUS": {
|
|
1136
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1137
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
const engine = ensureEngine();
|
|
1141
|
+
const status = await engine.multimodalStatus?.(req.modelId);
|
|
1142
|
+
postEvent({ id: req.id, type: "RESULT", payload: status ?? { enabled: false, vision: false, audio: false } });
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
case "RELEASE_MULTIMODAL": {
|
|
1146
|
+
const engine = ensureEngine();
|
|
1147
|
+
await engine.releaseMultimodal?.(req.modelId);
|
|
1148
|
+
postEvent({ id: req.id, type: "RESULT", payload: { ok: true } });
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
case "INIT_VOCODER": {
|
|
1152
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1153
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const engine = ensureEngine();
|
|
1157
|
+
const ok = await engine.initVocoder?.(req.modelId, req.path, req.nBatch ?? 512);
|
|
1158
|
+
postEvent({ id: req.id, type: "RESULT", payload: { ok: !!ok } });
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
case "VOCODER_ENABLED": {
|
|
1162
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1163
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
const engine = ensureEngine();
|
|
1167
|
+
const enabled = await engine.vocoderEnabled?.(req.modelId);
|
|
1168
|
+
postEvent({ id: req.id, type: "RESULT", payload: { enabled: !!enabled } });
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
case "RELEASE_VOCODER": {
|
|
1172
|
+
const engine = ensureEngine();
|
|
1173
|
+
await engine.releaseVocoder?.(req.modelId);
|
|
1174
|
+
postEvent({ id: req.id, type: "RESULT", payload: { ok: true } });
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
case "FORMATTED_AUDIO": {
|
|
1178
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1179
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
const engine = ensureEngine();
|
|
1183
|
+
const result = await engine.formattedAudioCompletion?.(
|
|
1184
|
+
req.modelId,
|
|
1185
|
+
req.speakerJson,
|
|
1186
|
+
req.textToSpeak
|
|
1187
|
+
);
|
|
1188
|
+
postEvent({ id: req.id, type: "RESULT", payload: result ?? { prompt: "" } });
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
case "AUDIO_GUIDE_TOKENS": {
|
|
1192
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1193
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
const engine = ensureEngine();
|
|
1197
|
+
const tokens = await engine.audioGuideTokens?.(req.modelId, req.textToSpeak);
|
|
1198
|
+
postEvent({ id: req.id, type: "RESULT", payload: { tokens: tokens ?? [] } });
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
case "DECODE_AUDIO_TOKENS": {
|
|
1202
|
+
if (!state.loadedModels.has(req.modelId)) {
|
|
1203
|
+
postError(req.id, "MODEL_NOT_LOADED", `Model '${req.modelId}' is not loaded in worker.`);
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
const engine = ensureEngine();
|
|
1207
|
+
const audio = await engine.decodeAudioTokens?.(req.modelId, req.tokens);
|
|
1208
|
+
postEvent({ id: req.id, type: "RESULT", payload: { audio: audio ?? [] } });
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
default: {
|
|
1212
|
+
const unknownReq = req;
|
|
1213
|
+
postError(
|
|
1214
|
+
typeof unknownReq?.id === "string" ? unknownReq.id : "unknown",
|
|
1215
|
+
"INVALID_REQUEST",
|
|
1216
|
+
`Unknown worker request type '${unknownReq?.type}'.`
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
} catch (error) {
|
|
1221
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1222
|
+
const code = req.type === "INIT" ? "WASM_INIT_FAILED" : "INFERENCE_FAILED";
|
|
1223
|
+
postError(req.id, code, message, { requestType: req.type });
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
//# sourceMappingURL=llm.worker.js.map
|