@agorapete/wllama 3.5.1-q2.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.
Files changed (86) hide show
  1. package/.gitmodules +3 -0
  2. package/.prettierignore +38 -0
  3. package/AGENTS.md +1 -0
  4. package/CMakeLists.txt +131 -0
  5. package/LICENCE +21 -0
  6. package/README-dev.md +178 -0
  7. package/README.md +225 -0
  8. package/README_banner.png +0 -0
  9. package/assets/screenshot_0.png +0 -0
  10. package/cpp/generate_glue_prototype.js +115 -0
  11. package/cpp/glue.hpp +664 -0
  12. package/cpp/test_glue.cpp +80 -0
  13. package/cpp/wllama-context.h +1172 -0
  14. package/cpp/wllama-fs.h +148 -0
  15. package/cpp/wllama.cpp +187 -0
  16. package/cpp/wllama.h +6 -0
  17. package/esm/cache-manager.d.ts +130 -0
  18. package/esm/debug.d.ts +28 -0
  19. package/esm/glue/glue.d.ts +22 -0
  20. package/esm/glue/messages.d.ts +146 -0
  21. package/esm/huggingface.d.ts +31 -0
  22. package/esm/index.cjs +3406 -0
  23. package/esm/index.d.ts +8 -0
  24. package/esm/index.js +3387 -0
  25. package/esm/index.min.js +1 -0
  26. package/esm/index.min.js.map +1 -0
  27. package/esm/model-manager.d.ts +136 -0
  28. package/esm/storage/cos.d.ts +36 -0
  29. package/esm/storage/index.d.ts +33 -0
  30. package/esm/storage/opfs.d.ts +12 -0
  31. package/esm/types/oai-compat.d.ts +278 -0
  32. package/esm/types/types.d.ts +112 -0
  33. package/esm/utils.d.ts +119 -0
  34. package/esm/wasm/source-map.d.ts +1 -0
  35. package/esm/wasm/wllama.wasm +0 -0
  36. package/esm/wasm-from-cdn.d.ts +8 -0
  37. package/esm/wllama.d.ts +397 -0
  38. package/esm/worker.d.ts +92 -0
  39. package/esm/workers-code/generated.d.ts +4 -0
  40. package/guides/intro-v2.md +132 -0
  41. package/guides/intro-v3.1.md +40 -0
  42. package/guides/intro-v3.md +230 -0
  43. package/index.ts +1 -0
  44. package/package.json +71 -0
  45. package/scripts/bisect_test.sh +33 -0
  46. package/scripts/build_hf_space.sh +26 -0
  47. package/scripts/build_source_map.js +269 -0
  48. package/scripts/build_wasm.sh +19 -0
  49. package/scripts/build_worker.sh +38 -0
  50. package/scripts/check_debug_build.js +30 -0
  51. package/scripts/check_package_size.js +25 -0
  52. package/scripts/docker-compose.yml +76 -0
  53. package/scripts/generate_wasm_from_cdn.js +24 -0
  54. package/scripts/http_server.js +44 -0
  55. package/scripts/post_build.sh +32 -0
  56. package/src/cache-manager.ts +358 -0
  57. package/src/debug.ts +111 -0
  58. package/src/glue/glue.ts +291 -0
  59. package/src/glue/messages.ts +773 -0
  60. package/src/huggingface.ts +151 -0
  61. package/src/index.ts +8 -0
  62. package/src/mjs.test.ts +44 -0
  63. package/src/model-manager.test.ts +200 -0
  64. package/src/model-manager.ts +359 -0
  65. package/src/storage/cos.test.ts +83 -0
  66. package/src/storage/cos.ts +171 -0
  67. package/src/storage/index.ts +40 -0
  68. package/src/storage/opfs.ts +119 -0
  69. package/src/types/oai-compat.ts +342 -0
  70. package/src/types/types.ts +133 -0
  71. package/src/utils.test.ts +231 -0
  72. package/src/utils.ts +403 -0
  73. package/src/wasm/source-map.ts +7 -0
  74. package/src/wasm/wllama.js +1 -0
  75. package/src/wasm/wllama.wasm +0 -0
  76. package/src/wasm-from-cdn.ts +13 -0
  77. package/src/wllama.test.ts +392 -0
  78. package/src/wllama.ts +1138 -0
  79. package/src/wllama.wgpu.test.ts +62 -0
  80. package/src/worker.ts +443 -0
  81. package/src/workers-code/generated.ts +11 -0
  82. package/src/workers-code/llama-cpp.js +511 -0
  83. package/src/workers-code/opfs-utils.js +150 -0
  84. package/tsconfig.build.json +34 -0
  85. package/tsup.config.ts +23 -0
  86. package/vitest.config.ts +61 -0
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env node
2
+ // Usage: node scripts/build_source_map.js [--input name:buildDir] [--output out.ts]
3
+ // All flags are optional — defaults point to build/ and src/wasm/source-map.ts.
4
+ // Reads *.js.symbols from each build dir and produces cleaned function names.
5
+
6
+ import { readFileSync, writeFileSync, readdirSync } from 'fs';
7
+ import { resolve, join } from 'path';
8
+ import { gzipSync } from 'zlib';
9
+
10
+ const DEFAULT_INPUTS = [
11
+ { name: 'default', symbolsPath: 'build' },
12
+ { name: 'compat', symbolsPath: 'build-compat' },
13
+ ];
14
+ const DEFAULT_OUTPUT = 'src/wasm/source-map.ts';
15
+
16
+ const args = process.argv.slice(2);
17
+ const inputs = [];
18
+ let outputFile = null;
19
+
20
+ for (let i = 0; i < args.length; i++) {
21
+ if (args[i] === '--input') {
22
+ const next = args[++i];
23
+ if (!next) { console.error('--input must be name:buildDir'); process.exit(1); }
24
+ const [name, symbolsPath] = next.split(':');
25
+ if (!name || !symbolsPath) { console.error('--input must be name:buildDir'); process.exit(1); }
26
+ inputs.push({ name, symbolsPath: resolve(symbolsPath) });
27
+ } else if (args[i] === '--output') {
28
+ const next = args[++i];
29
+ if (!next) { console.error('--output requires a path'); process.exit(1); }
30
+ outputFile = resolve(next);
31
+ }
32
+ }
33
+
34
+ if (!inputs.length) {
35
+ for (const { name, symbolsPath } of DEFAULT_INPUTS) {
36
+ const resolved = resolve(symbolsPath);
37
+ try { readdirSync(resolved); inputs.push({ name, symbolsPath: resolved }); } catch { /* skip */ }
38
+ }
39
+ }
40
+ if (!outputFile) outputFile = resolve(DEFAULT_OUTPUT);
41
+ if (!inputs.length) { console.error('No build directories found.'); process.exit(1); }
42
+
43
+ // -- wasm binary: extract [firstFuncId, funcCount] ----------------------------
44
+
45
+ function readUleb(buf, pos) {
46
+ let result = 0, shift = 0;
47
+ while (true) {
48
+ const b = buf[pos++];
49
+ result |= (b & 0x7F) << shift;
50
+ if (!(b & 0x80)) break;
51
+ shift += 7;
52
+ }
53
+ return [result, pos];
54
+ }
55
+
56
+ function skipLimits(buf, pos) {
57
+ const kind = buf[pos++];
58
+ [, pos] = readUleb(buf, pos);
59
+ if (kind === 0x01 || kind === 0x03 || kind === 0x05 || kind === 0x07)
60
+ [, pos] = readUleb(buf, pos);
61
+ return pos;
62
+ }
63
+
64
+ function parseFuncIds(wasmBuf) {
65
+ let pos = 8, importCount = 0;
66
+ while (pos < wasmBuf.length) {
67
+ const sectionId = wasmBuf[pos++];
68
+ let sectionSize;
69
+ [sectionSize, pos] = readUleb(wasmBuf, pos);
70
+ const sectionEnd = pos + sectionSize;
71
+ if (sectionId === 2) {
72
+ let count; [count, pos] = readUleb(wasmBuf, pos);
73
+ for (let i = 0; i < count; i++) {
74
+ let nl;
75
+ [nl, pos] = readUleb(wasmBuf, pos); pos += nl;
76
+ [nl, pos] = readUleb(wasmBuf, pos); pos += nl;
77
+ const kind = wasmBuf[pos++];
78
+ if (kind === 0) { [, pos] = readUleb(wasmBuf, pos); importCount++; }
79
+ else if (kind === 1) { pos++; pos = skipLimits(wasmBuf, pos); }
80
+ else if (kind === 2) { pos = skipLimits(wasmBuf, pos); }
81
+ else if (kind === 3) { pos += 2; }
82
+ }
83
+ } else if (sectionId === 10) {
84
+ let funcCount; [funcCount, pos] = readUleb(wasmBuf, pos);
85
+ return [importCount, funcCount];
86
+ } else {
87
+ pos = sectionEnd;
88
+ }
89
+ }
90
+ throw new Error('No code section found');
91
+ }
92
+
93
+ // -- name simplification ------------------------------------------------------
94
+
95
+ function truncateTemplates(name, maxLen) {
96
+ let result = '', i = 0;
97
+ while (i < name.length) {
98
+ if (name[i] !== '<') { result += name[i++]; continue; }
99
+ // find matching >
100
+ let depth = 1, j = i + 1;
101
+ while (j < name.length && depth > 0) {
102
+ if (name[j] === '<') depth++;
103
+ else if (name[j] === '>') depth--;
104
+ j++;
105
+ }
106
+ const content = name.slice(i + 1, j - 1);
107
+ result += content.length > maxLen
108
+ ? '<' + content.slice(0, maxLen) + '...>'
109
+ : '<' + content + '>';
110
+ i = j;
111
+ }
112
+ return result;
113
+ }
114
+
115
+ function stripParams(name) {
116
+ // Find first top-level '(' (not inside <>) and strip from there.
117
+ // If params are empty "()", keep them; otherwise drop entirely.
118
+ let depth = 0;
119
+ for (let i = 0; i < name.length; i++) {
120
+ const c = name[i];
121
+ if (c === '<') { depth++; continue; }
122
+ if (c === '>') { depth--; continue; }
123
+ if (c === '(' && depth === 0) {
124
+ const base = name.slice(0, i);
125
+ return (name[i + 1] === ')') ? base + '()' : base;
126
+ }
127
+ }
128
+ return name;
129
+ }
130
+
131
+ const STD_HINT = 'std::...';
132
+
133
+ function simplifyName(raw) {
134
+ if (!raw) return null;
135
+ let name = raw;
136
+
137
+ // Rule 0: collapse all std:: into a hint
138
+ if (/^std::/.test(name)) return STD_HINT;
139
+
140
+ // Rule 1: lambda / closure types
141
+ // Handles ::$_N, ::'lambda'(), 'lambda'()
142
+ // Strategy: find the marker, take everything inside the nearest enclosing <..> before it
143
+ const lambdaRe = /::[$']_?\d*|::'lambda'/;
144
+ const lambdaMatch = lambdaRe.exec(name);
145
+ if (lambdaMatch) {
146
+ const before = name.slice(0, lambdaMatch.index);
147
+ // Extract the innermost meaningful context: look for the last '<' before the marker
148
+ const lastAngle = before.lastIndexOf('<');
149
+ let parent = lastAngle >= 0 ? before.slice(lastAngle + 1) : before;
150
+ // Strip trailing qualifiers
151
+ parent = parent.replace(/\s+(const|volatile|noexcept|&&?)\s*$/, '').trim();
152
+ name = parent;
153
+ // fall through to further cleanup below
154
+ }
155
+
156
+ // Rule 2: strip parameter list
157
+ name = stripParams(name);
158
+
159
+ // Rule 3: remove libc++ internal sub-namespaces (::__2::, ::__1::, etc.)
160
+ name = name.replace(/::__\d+::/g, '::');
161
+
162
+ // Rule 4: remove ABI tags
163
+ name = name.replace(/\[abi:[^\]]+\]/g, '');
164
+
165
+ // Rule 5: truncate template args to 10 chars
166
+ name = truncateTemplates(name, 10);
167
+
168
+ // Rule 6: final cleanup
169
+ name = name.replace(/::::/g, '::').replace(/\s+/g, ' ').trim();
170
+
171
+ return name || null;
172
+ }
173
+
174
+ // -- binary encoder -----------------------------------------------------------
175
+
176
+ function encodeNames(funcCount, firstId, symbols) {
177
+ // Build deduplicated name table
178
+ const nameToIdx = new Map(); // string -> u16 index
179
+ const nameTable = []; // array of Buffer
180
+ const indices = new Uint16Array(funcCount); // 0xFFFF = unknown
181
+ indices.fill(0xFFFF);
182
+
183
+ let mapped = 0;
184
+ for (let i = 0; i < funcCount; i++) {
185
+ const raw = symbols.get(firstId + i) ?? null;
186
+ const cleaned = raw ? simplifyName(raw) : null;
187
+ if (!cleaned) continue;
188
+ let idx = nameToIdx.get(cleaned);
189
+ if (idx === undefined) {
190
+ idx = nameTable.length;
191
+ const b = Buffer.from(cleaned.slice(0, 254));
192
+ nameTable.push(Buffer.concat([Buffer.from([b.length]), b]));
193
+ nameToIdx.set(cleaned, idx);
194
+ }
195
+ indices[i] = idx;
196
+ mapped++;
197
+ }
198
+
199
+ const numNames = nameTable.length;
200
+ process.stderr.write(` ${mapped}/${funcCount} named, ${numNames} unique names\n`);
201
+
202
+ // u32 numNames + name table + u16 index array
203
+ const header = Buffer.alloc(4);
204
+ header.writeUInt32LE(numNames, 0);
205
+ const indexBuf = Buffer.from(indices.buffer);
206
+ return Buffer.concat([header, ...nameTable, indexBuf]);
207
+ }
208
+
209
+ // -- resolve symbolsPath (dir or file) ----------------------------------------
210
+
211
+ function resolveSymbolsFile(symbolsPath) {
212
+ if (symbolsPath.endsWith('.js.symbols')) return symbolsPath;
213
+ for (const entry of readdirSync(symbolsPath))
214
+ if (entry.endsWith('.js.symbols')) return join(symbolsPath, entry);
215
+ throw new Error(`No .js.symbols file in ${symbolsPath}`);
216
+ }
217
+
218
+ // -- per-build processing -----------------------------------------------------
219
+
220
+ function processBuild(symbolsPath) {
221
+ const resolvedSymbols = resolveSymbolsFile(symbolsPath);
222
+ const wasmPath = resolvedSymbols.replace(/\.js\.symbols$/, '.wasm');
223
+
224
+ process.stderr.write(` Parsing wasm binary...\n`);
225
+ const [firstId, funcCount] = parseFuncIds(readFileSync(wasmPath));
226
+ process.stderr.write(` ${funcCount} functions starting at index ${firstId}\n`);
227
+
228
+ process.stderr.write(` Loading symbols...\n`);
229
+ const symbols = new Map();
230
+ for (const line of readFileSync(resolvedSymbols, 'utf8').split('\n')) {
231
+ const colon = line.indexOf(':');
232
+ if (colon < 0) continue;
233
+ const id = parseInt(line.slice(0, colon));
234
+ if (!isNaN(id)) symbols.set(id, line.slice(colon + 1).trim());
235
+ }
236
+ process.stderr.write(` ${symbols.size} raw symbols\n`);
237
+
238
+ const header = Buffer.alloc(8);
239
+ header.writeUInt32LE(firstId, 0);
240
+ header.writeUInt32LE(funcCount, 4);
241
+
242
+ const nameData = encodeNames(funcCount, firstId, symbols);
243
+ const binary = Buffer.concat([header, nameData]);
244
+ const compressed = gzipSync(binary);
245
+ process.stderr.write(` ${binary.length.toLocaleString()} bytes -> ${compressed.length.toLocaleString()} bytes gzipped\n`);
246
+
247
+ return compressed.toString('base64');
248
+ }
249
+
250
+ // -- main ---------------------------------------------------------------------
251
+
252
+ const entries_ts = [];
253
+ for (const { name, symbolsPath } of inputs) {
254
+ process.stderr.write(`\n[${name}] ${symbolsPath}\n`);
255
+ entries_ts.push(` "${name}": "${processBuild(symbolsPath)}"`);
256
+ }
257
+
258
+ const tsContent = [
259
+ `// Auto-generated by scripts/build_source_map.js — do not edit`,
260
+ `// Format: gzip-compressed binary name table, base64-encoded`,
261
+ `// Structure: u32 firstId, u32 funcCount, u32 numNames, then name table (u8 len + bytes each), then u16 index array (0xFFFF = unknown)`,
262
+ `export const WASM_SOURCE_MAP: Record<string, string> = {`,
263
+ entries_ts.join(',\n'),
264
+ `};`,
265
+ ``,
266
+ ].join('\n');
267
+
268
+ writeFileSync(outputFile, tsContent);
269
+ process.stderr.write(`\nWrote ${outputFile}\n`);
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+
3
+ #set -e
4
+
5
+ export EMSDK_IMAGE_TAG="4.0.20"
6
+
7
+ CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
8
+ cd $CURRENT_PATH
9
+
10
+ export D_UID=$UID
11
+ export D_GID=$GID
12
+
13
+ if [[ $(uname -m) == "arm64" ]]; then
14
+ echo "Running on ARM64 processor"
15
+ export DOCKER_DEFAULT_PLATFORM="linux/arm64"
16
+ export EMSDK_IMAGE_TAG="${EMSDK_IMAGE_TAG}-arm64"
17
+ fi
18
+
19
+ docker compose up llamacpp-wasm-builder --exit-code-from llamacpp-wasm-builder
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
6
+
7
+ # change to the llama.cpp directory
8
+ cd $CURRENT_PATH
9
+ cd ../llama.cpp
10
+ BUILD_NUMBER="$(git rev-list --count HEAD)"
11
+ SHORT_HASH="$(git rev-parse --short=7 HEAD)"
12
+
13
+ # change to the root of the project
14
+ cd $CURRENT_PATH
15
+ cd ..
16
+
17
+ echo "// This file is auto-generated" > ./src/workers-code/generated.ts
18
+ echo "// To re-generate it, run: npm run build:worker" >> ./src/workers-code/generated.ts
19
+ echo "" >> ./src/workers-code/generated.ts
20
+ echo "export const LIBLLAMA_VERSION = 'b${BUILD_NUMBER}-${SHORT_HASH}';" >> ./src/workers-code/generated.ts
21
+ echo "" >> ./src/workers-code/generated.ts
22
+
23
+ process_file() {
24
+ local file="$1"
25
+ local content
26
+ content=$(node -e "console.log(JSON.stringify(require('fs').readFileSync('$file', 'utf8').toString()))")
27
+ echo "export const $2 = $content;" >> ./src/workers-code/generated.ts
28
+ echo "" >> ./src/workers-code/generated.ts
29
+ }
30
+
31
+ process_file ./src/workers-code/llama-cpp.js LLAMA_CPP_WORKER_CODE
32
+ process_file ./src/workers-code/opfs-utils.js OPFS_UTILS_WORKER_CODE
33
+
34
+ # emscripten
35
+ process_file ./src/wasm/wllama.js WLLAMA_EMSCRIPTEN_CODE
36
+
37
+ # build CDN paths
38
+ node ./scripts/generate_wasm_from_cdn.js
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, existsSync } from 'fs';
4
+ import { resolve, dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const ROOT = resolve(__dirname, '..');
9
+ const WASM_PATHS = [
10
+ 'src/wasm/wllama.wasm',
11
+ 'compat/src/wasm/wllama.wasm',
12
+ ];
13
+ const DEBUG_MARKER = 'test-backend-ops.cpp';
14
+
15
+ export function checkDebugBuild() {
16
+ for (const relPath of WASM_PATHS) {
17
+ const absPath = resolve(ROOT, relPath);
18
+ if (!existsSync(absPath)) continue;
19
+ const contents = readFileSync(absPath);
20
+ if (contents.includes(DEBUG_MARKER)) {
21
+ console.error(`ERROR: ${relPath} contains "${DEBUG_MARKER}" - this is a debug build and cannot be merged to master or be published`);
22
+ process.exit(1);
23
+ }
24
+ }
25
+ }
26
+
27
+ // Run directly when invoked as a script
28
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
29
+ checkDebugBuild();
30
+ }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'child_process';
4
+ import { checkDebugBuild } from './check_debug_build.js';
5
+
6
+ checkDebugBuild();
7
+
8
+ const MAX_SIZE = 20 * 1024 * 1024; // 20 MB
9
+ const MAX_FILES = 90;
10
+
11
+ const result = JSON.parse(execSync('npm pack --dry-run --json 2>/dev/null'));
12
+ const { unpackedSize, entryCount } = result[0];
13
+
14
+ console.log(`Unpacked size: ${(unpackedSize / 1024 / 1024).toFixed(2)} MB`);
15
+ console.log(`Total files: ${entryCount}`);
16
+
17
+ if (unpackedSize > MAX_SIZE) {
18
+ console.error(`ERROR: Unpacked size exceeds 20 MB limit`);
19
+ process.exit(1);
20
+ }
21
+
22
+ if (entryCount > MAX_FILES) {
23
+ console.error(`ERROR: Total files (${entryCount}) exceeds limit of ${MAX_FILES}`);
24
+ process.exit(1);
25
+ }
@@ -0,0 +1,76 @@
1
+ services:
2
+ llamacpp-wasm-builder:
3
+ container_name: llamacpp-wasm-builder
4
+ build:
5
+ context: .
6
+ dockerfile_inline: |
7
+ ARG EMSDK_IMAGE_TAG=none # modify it in build_wasm.sh
8
+ FROM emscripten/emsdk:$${EMSDK_IMAGE_TAG}
9
+ RUN apt update && apt install -y git
10
+ args:
11
+ EMSDK_IMAGE_TAG: ${EMSDK_IMAGE_TAG}
12
+ user: "${D_UID}:${D_GID}"
13
+ environment:
14
+ - WLLAMA_TEST_BACKEND=${WLLAMA_TEST_BACKEND:-}
15
+ - SKIP_COMPAT=${SKIP_COMPAT:-}
16
+ volumes:
17
+ - ..:/source:Z
18
+ entrypoint: /bin/bash
19
+ command:
20
+ - -c
21
+ - |
22
+ set -e
23
+ cd /source
24
+
25
+ mkdir -p build
26
+ cd build
27
+ mkdir -p emdawn
28
+
29
+ DAWN_TAG=v20260317.182325
30
+ EMDAWN_PKG="emdawnwebgpu_pkg-$${DAWN_TAG}.zip"
31
+ EMDAWNWEBGPU_DIR="/source/build/emdawn/emdawnwebgpu_pkg"
32
+ echo "Downloading $${EMDAWN_PKG}"
33
+ curl -L -o emdawn.zip \
34
+ "https://github.com/google/dawn/releases/download/$${DAWN_TAG}/$${EMDAWN_PKG}"
35
+ python3 -c "import zipfile; zf=zipfile.ZipFile('emdawn.zip','r'); zf.extractall('/source/build/emdawn'); zf.close()"
36
+
37
+ CMAKE_EXTRA_FLAGS="-DWLLAMA_TEST_BACKEND=OFF"
38
+ if [ -n "${WLLAMA_TEST_BACKEND}" ]; then
39
+ CMAKE_EXTRA_FLAGS="-DWLLAMA_TEST_BACKEND=ON";
40
+ fi
41
+
42
+ emcmake cmake .. -DGGML_WEBGPU=ON -DGGML_WEBGPU_JSPI=ON -DEMDAWNWEBGPU_DIR="$${EMDAWNWEBGPU_DIR}" $${CMAKE_EXTRA_FLAGS}
43
+ emmake make wllama -j
44
+
45
+ # go back to root
46
+ cd ..
47
+
48
+ # copy output files
49
+ mkdir -p src/wasm
50
+ cp build/wllama.js src/wasm
51
+ cp build/wllama.wasm src/wasm
52
+
53
+ # build compat package (skip if SKIP_COMPAT=1)
54
+ if [ -z "${SKIP_COMPAT}" ]; then
55
+ mkdir -p build-compat
56
+ cd build-compat
57
+ emcmake cmake .. -DWLLAMA_COMPAT=ON -DLLAMA_WASM_MEM64=OFF -DGGML_WEBGPU=ON -DGGML_WEBGPU_JSPI=OFF -DEMDAWNWEBGPU_DIR="$${EMDAWNWEBGPU_DIR}" $${CMAKE_EXTRA_FLAGS}
58
+ emmake make wllama -j
59
+
60
+ # go back to root
61
+ cd ..
62
+
63
+ # copy output files (compat build)
64
+ mkdir -p compat/wasm
65
+ cp build-compat/wllama.js compat/wasm
66
+ cp build-compat/wllama.wasm compat/wasm
67
+ fi
68
+
69
+ # generate source map
70
+ node scripts/build_source_map.js
71
+
72
+ # list files (optional, for verification)
73
+ ls -lh build
74
+ if [ -d build-compat ]; then
75
+ ls -lh build-compat
76
+ fi
@@ -0,0 +1,24 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
5
+ const outputPath = './src/wasm-from-cdn.ts';
6
+
7
+ const version = packageJson.version;
8
+ const outputContent = `
9
+ // This file is generated by scripts/generate_wasm_from_cdn.js
10
+ // Do not edit this file directly
11
+
12
+ const WasmFromCDN = {
13
+ default: 'https://cdn.jsdelivr.net/npm/@wllama/wllama@${version}/src/wasm/wllama.wasm',
14
+ };
15
+
16
+ export const WasmCompatFromCDN = {
17
+ worker: 'https://cdn.jsdelivr.net/npm/@wllama/wllama-compat@${version}/wasm/wllama.js',
18
+ wasm: 'https://cdn.jsdelivr.net/npm/@wllama/wllama-compat@${version}/wasm/wllama.wasm',
19
+ };
20
+
21
+ export default WasmFromCDN;
22
+ `.trim();
23
+
24
+ fs.writeFileSync(outputPath, outputContent);
@@ -0,0 +1,44 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+ import mime from 'mime-types';
4
+ import { dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+
9
+ const app = express();
10
+
11
+ // Serve static files
12
+ app.use(express.static(path.join(__dirname, '..'), {
13
+ setHeaders: (res) => {
14
+ if (process.env.MULTITHREAD) {
15
+ // add required security header to enable SharedArrayBuffer, needed to run multithread
16
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements
17
+ res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
18
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
19
+ }
20
+ res.setHeader('Access-Control-Allow-Origin', '*');
21
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
22
+ res.setHeader('Pragma', 'no-cache');
23
+ res.setHeader('Expires', '0');
24
+ }
25
+ }));
26
+
27
+ if (!process.env.MULTITHREAD) {
28
+ console.log('WARN: Running server without MULTITHREAD=1, this will effectively disable multithreading');
29
+ }
30
+
31
+ app.get("/", (req, res) => {
32
+ res.send(`
33
+ Examples:<br/>
34
+ <a href="/examples/basic">Basic</a><br/>
35
+ <a href="/examples/embeddings">Embeddings</a><br/>
36
+ <a href="/examples/multimodal">Multimodal</a><br/>
37
+ <a href="/examples/tools">Tools</a><br/>
38
+ <a href="/examples/test-backend-ops">Test Backend Ops (FOR DEBUGGING ONLY)</a><br/>
39
+ `);
40
+ });
41
+
42
+ // Start the server
43
+ const PORT = 8080;
44
+ app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
6
+ cd $CURRENT_PATH/..
7
+
8
+ mkdir -p esm/wasm
9
+ cp src/wasm/wllama.wasm esm/wasm
10
+
11
+ # https://stackoverflow.com/questions/62619058/appending-js-extension-on-relative-import-statements-during-typescript-compilat
12
+
13
+ function patch_esm_import_js {
14
+ if [[ "$OSTYPE" == "darwin"* ]]; then
15
+ find $1 -name "*.js" -exec sed -i '' -E "s#export (.*) from '\.(.*)';#export \1 from '.\2\.js';#g" {} +;
16
+ find $1 -name "*.js" -exec sed -i '' -E "s#import (.*) from '\.(.*)';#import \1 from '.\2\.js';#g" {} +;
17
+ else
18
+ find $1 -name "*.js" -exec sed -i -E "s#export (.*) from '\.(.*)';#export \1 from '.\2\.js';#g" {} +;
19
+ find $1 -name "*.js" -exec sed -i -E "s#import (.*) from '\.(.*)';#import \1 from '.\2\.js';#g" {} +;
20
+ fi
21
+ }
22
+
23
+ patch_esm_import_js "./esm"
24
+
25
+ # sync compat/package.json version with root package.json
26
+ node -e "
27
+ const fs = require('fs');
28
+ const version = JSON.parse(fs.readFileSync('package.json', 'utf8')).version;
29
+ const compat = JSON.parse(fs.readFileSync('compat/package.json', 'utf8'));
30
+ compat.version = version;
31
+ fs.writeFileSync('compat/package.json', JSON.stringify(compat, null, 2) + '\n');
32
+ "