@fugood/node-whisper-wasm 1.0.20 → 1.0.22
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/README.md +21 -15
- package/index.d.ts +7 -0
- package/index.js +76 -28
- package/package.json +4 -2
- package/wasm/whisper-node.js +2 -2
- package/wasm/whisper-node.threads.js +2 -0
- package/wasm/whisper-node.threads.wasm +0 -0
- package/wasm/whisper-node.wasm +0 -0
- package/worker.js +1 -0
package/README.md
CHANGED
|
@@ -24,21 +24,25 @@ console.log(await promise)
|
|
|
24
24
|
await whisper.release()
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
The
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
The package ships both single-thread and pthread WASM artifacts. On
|
|
28
|
+
cross-origin isolated pages with `SharedArrayBuffer`, the loader uses the
|
|
29
|
+
pthread artifact; otherwise it falls back to the single-thread artifact
|
|
30
|
+
automatically. Whisper transcription defaults to up to 8 threads based on
|
|
31
|
+
browser hardware concurrency when pthreads are available; pass `maxThreads` to
|
|
32
|
+
override it. Browser WASM clamps `maxThreads` to the compiled pool limit of 8,
|
|
33
|
+
or 1 in the single-thread fallback. Browser pages run model loading,
|
|
34
|
+
transcription, benchmarks, and VAD in a dedicated module worker by default so
|
|
35
|
+
the UI thread can keep rendering. Use the main `whisper.node` package entrypoint
|
|
36
|
+
in browser code:
|
|
34
37
|
|
|
35
38
|
```js
|
|
36
39
|
import { configureWasm, initWhisper } from '@fugood/whisper.node'
|
|
37
40
|
```
|
|
38
41
|
|
|
39
42
|
Use `configureWasm({ worker: false })` only when you explicitly need the
|
|
40
|
-
in-thread runtime,
|
|
41
|
-
|
|
43
|
+
in-thread runtime, `configureWasm({ threads: false })` to force the
|
|
44
|
+
single-thread artifact, or pass `workerPath`, `jsPath`, and `wasmPath` when
|
|
45
|
+
serving the package files from custom URLs. The older `workerUrl` and
|
|
42
46
|
`runtimeScriptUrl` option names still work. Model downloads are cached in
|
|
43
47
|
browser Cache Storage by default. Pass `cacheModel: false` to disable persistent
|
|
44
48
|
caching, `modelCacheName` to isolate the cache namespace, or `modelCacheKey`
|
|
@@ -47,9 +51,11 @@ model. Set `useGpu: true` only with a package built using `GGML_WEBGPU=ON` and a
|
|
|
47
51
|
browser that exposes `navigator.gpu`. VAD currently falls back to CPU in the
|
|
48
52
|
browser package because the Silero VAD graph hits unsupported WebGPU ops.
|
|
49
53
|
|
|
50
|
-
The default build emits `wasm/whisper-node.js
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`whisper-node.worker.js` is
|
|
55
|
-
UI work off the main thread
|
|
54
|
+
The default build emits `wasm/whisper-node.js`, `wasm/whisper-node.wasm`,
|
|
55
|
+
`wasm/whisper-node.threads.js`, and `wasm/whisper-node.threads.wasm`. Use
|
|
56
|
+
`bash scripts/build-wasm.sh --single-file` only when you want the WASM binary
|
|
57
|
+
embedded into each generated JS file. Modern Emscripten embeds the pthread
|
|
58
|
+
worker bootstrap in the main JS file, so a separate `whisper-node.worker.js` is
|
|
59
|
+
not expected. The package module worker that keeps UI work off the main thread
|
|
60
|
+
is `worker.js`. Use `npm run build-wasm-docker` to build through Docker; it
|
|
61
|
+
selects a native arm64 Emscripten image on Apple Silicon hosts.
|
package/index.d.ts
CHANGED
|
@@ -148,6 +148,7 @@ export interface WasmRuntimeOptions {
|
|
|
148
148
|
runtimeScriptUrl?: string
|
|
149
149
|
jsPath?: string
|
|
150
150
|
wasmPath?: string
|
|
151
|
+
threads?: boolean
|
|
151
152
|
locateFileBaseUrl?: string
|
|
152
153
|
locateFile?: (path: string, prefix: string) => string
|
|
153
154
|
mainScriptUrlOrBlob?: string | Blob
|
|
@@ -166,10 +167,13 @@ export declare const WASM_CONFIG_PATHS: {
|
|
|
166
167
|
index: string
|
|
167
168
|
js: string
|
|
168
169
|
wasm: string
|
|
170
|
+
threadsJs: string
|
|
171
|
+
threadsWasm: string
|
|
169
172
|
worker: string
|
|
170
173
|
}
|
|
171
174
|
|
|
172
175
|
export declare function configureWasm(options: WasmRuntimeOptions): void
|
|
176
|
+
export declare function isWasmThreadsSupported(): boolean
|
|
173
177
|
export declare function loadWasmModule(): Promise<unknown>
|
|
174
178
|
export declare function loadWhisperModule(): Promise<Module>
|
|
175
179
|
export declare function initWhisper(
|
|
@@ -185,6 +189,7 @@ export declare function toggleNativeLog(
|
|
|
185
189
|
export declare function addNativeLogListener(
|
|
186
190
|
listener: (level: string, text: string) => void,
|
|
187
191
|
): { remove: () => void }
|
|
192
|
+
export declare function isNativeLogEnabled(): boolean
|
|
188
193
|
|
|
189
194
|
declare const _default: {
|
|
190
195
|
WhisperContext: typeof WhisperContext
|
|
@@ -196,6 +201,8 @@ declare const _default: {
|
|
|
196
201
|
initWhisperVad: typeof initWhisperVad
|
|
197
202
|
toggleNativeLog: typeof toggleNativeLog
|
|
198
203
|
addNativeLogListener: typeof addNativeLogListener
|
|
204
|
+
isNativeLogEnabled: typeof isNativeLogEnabled
|
|
205
|
+
isWasmThreadsSupported: typeof isWasmThreadsSupported
|
|
199
206
|
DEFAULT_WASM_MODEL_SIZE_LIMIT_BYTES: typeof DEFAULT_WASM_MODEL_SIZE_LIMIT_BYTES
|
|
200
207
|
MAX_WASM_THREADS: typeof MAX_WASM_THREADS
|
|
201
208
|
WASM_CONFIG_PATHS: typeof WASM_CONFIG_PATHS
|
package/index.js
CHANGED
|
@@ -11,6 +11,8 @@ export const WASM_CONFIG_PATHS = {
|
|
|
11
11
|
index: new URL('./index.js', import.meta.url).href,
|
|
12
12
|
js: new URL('./wasm/whisper-node.js', import.meta.url).href,
|
|
13
13
|
wasm: new URL('./wasm/whisper-node.wasm', import.meta.url).href,
|
|
14
|
+
threadsJs: new URL('./wasm/whisper-node.threads.js', import.meta.url).href,
|
|
15
|
+
threadsWasm: new URL('./wasm/whisper-node.threads.wasm', import.meta.url).href,
|
|
14
16
|
worker: new URL('./worker.js', import.meta.url).href,
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -68,20 +70,39 @@ const createWhisperNodeApi = function (root) {
|
|
|
68
70
|
return capturedScriptUrl
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
function
|
|
73
|
+
function isWasmThreadsSupported() {
|
|
74
|
+
if (!isBrowserLike()) {
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
return (
|
|
78
|
+
typeof root.SharedArrayBuffer !== 'undefined' &&
|
|
79
|
+
typeof root.Atomics !== 'undefined' &&
|
|
80
|
+
typeof root.crossOriginIsolated !== 'undefined' &&
|
|
81
|
+
root.crossOriginIsolated === true
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function shouldUseThreadedRuntime() {
|
|
86
|
+
if (typeof runtimeOptions.__resolvedThreads === 'boolean') {
|
|
87
|
+
return runtimeOptions.__resolvedThreads
|
|
88
|
+
}
|
|
89
|
+
return runtimeOptions.threads !== false && isWasmThreadsSupported()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getRuntimeScriptUrl(indexScriptUrl, useThreads) {
|
|
72
93
|
var configured = runtimeOptions.jsPath || runtimeOptions.runtimeScriptUrl
|
|
73
94
|
if (configured) {
|
|
74
95
|
return resolveUrl(configured, indexScriptUrl)
|
|
75
96
|
}
|
|
76
|
-
return WASM_CONFIG_PATHS.js
|
|
97
|
+
return useThreads ? WASM_CONFIG_PATHS.threadsJs : WASM_CONFIG_PATHS.js
|
|
77
98
|
}
|
|
78
99
|
|
|
79
|
-
function getWasmBinaryUrl(runtimeScriptUrl) {
|
|
100
|
+
function getWasmBinaryUrl(runtimeScriptUrl, useThreads) {
|
|
80
101
|
var configured = runtimeOptions.wasmPath
|
|
81
102
|
if (configured) {
|
|
82
103
|
return resolveUrl(configured, runtimeScriptUrl)
|
|
83
104
|
}
|
|
84
|
-
return WASM_CONFIG_PATHS.wasm
|
|
105
|
+
return useThreads ? WASM_CONFIG_PATHS.threadsWasm : WASM_CONFIG_PATHS.wasm
|
|
85
106
|
}
|
|
86
107
|
|
|
87
108
|
function getWorkerScriptUrl(indexScriptUrl) {
|
|
@@ -111,6 +132,8 @@ const createWhisperNodeApi = function (root) {
|
|
|
111
132
|
runtimeScriptUrl: true,
|
|
112
133
|
jsPath: true,
|
|
113
134
|
wasmPath: true,
|
|
135
|
+
threads: true,
|
|
136
|
+
__resolvedThreads: true,
|
|
114
137
|
locateFileBaseUrl: true,
|
|
115
138
|
locateFile: true,
|
|
116
139
|
moduleFactory: true,
|
|
@@ -129,34 +152,42 @@ const createWhisperNodeApi = function (root) {
|
|
|
129
152
|
return options
|
|
130
153
|
}
|
|
131
154
|
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
155
|
+
function normalizeLogLevel(level) {
|
|
156
|
+
var normalized = String(level || '').toLowerCase()
|
|
157
|
+
if (normalized === 'warning') {
|
|
158
|
+
return 'warn'
|
|
159
|
+
}
|
|
160
|
+
if (
|
|
161
|
+
normalized === 'error' ||
|
|
162
|
+
normalized === 'warn' ||
|
|
163
|
+
normalized === 'info' ||
|
|
164
|
+
normalized === 'debug'
|
|
165
|
+
) {
|
|
166
|
+
return normalized
|
|
137
167
|
}
|
|
168
|
+
return normalized || 'info'
|
|
138
169
|
}
|
|
139
170
|
|
|
140
171
|
function emitLog(level, text) {
|
|
141
172
|
if (!logEnabled) {
|
|
142
173
|
return
|
|
143
174
|
}
|
|
175
|
+
var normalizedLevel = normalizeLogLevel(level)
|
|
144
176
|
if (typeof nativeLogCallback === 'function') {
|
|
145
|
-
nativeLogCallback(
|
|
177
|
+
nativeLogCallback(normalizedLevel, text)
|
|
146
178
|
}
|
|
147
179
|
logListeners.slice().forEach(function (listener) {
|
|
148
|
-
listener(
|
|
180
|
+
listener(normalizedLevel, text)
|
|
149
181
|
})
|
|
150
182
|
}
|
|
151
183
|
|
|
152
184
|
function loadRuntime() {
|
|
153
185
|
if (!runtimePromise) {
|
|
154
|
-
assertThreadSupport()
|
|
155
|
-
|
|
156
186
|
runtimePromise = (async function () {
|
|
157
187
|
var indexScriptUrl = getIndexScriptUrl()
|
|
158
|
-
var
|
|
159
|
-
var
|
|
188
|
+
var useThreads = shouldUseThreadedRuntime()
|
|
189
|
+
var runtimeScriptUrl = getRuntimeScriptUrl(indexScriptUrl, useThreads)
|
|
190
|
+
var wasmUrl = getWasmBinaryUrl(runtimeScriptUrl, useThreads)
|
|
160
191
|
var moduleFactory = runtimeOptions.moduleFactory
|
|
161
192
|
|
|
162
193
|
if (!moduleFactory) {
|
|
@@ -168,7 +199,7 @@ const createWhisperNodeApi = function (root) {
|
|
|
168
199
|
}
|
|
169
200
|
if (typeof moduleFactory !== 'function') {
|
|
170
201
|
throw new Error(
|
|
171
|
-
'Failed to load whisper.node WASM runtime. Make sure wasm/whisper-node
|
|
202
|
+
'Failed to load whisper.node WASM runtime. Make sure the selected wasm/whisper-node*.js artifact is built and included in the package.',
|
|
172
203
|
)
|
|
173
204
|
}
|
|
174
205
|
|
|
@@ -193,6 +224,8 @@ const createWhisperNodeApi = function (root) {
|
|
|
193
224
|
delete options.runtimeScriptUrl
|
|
194
225
|
delete options.jsPath
|
|
195
226
|
delete options.wasmPath
|
|
227
|
+
delete options.threads
|
|
228
|
+
delete options.__resolvedThreads
|
|
196
229
|
delete options.locateFileBaseUrl
|
|
197
230
|
|
|
198
231
|
options.noInitialRun = true
|
|
@@ -219,7 +252,9 @@ const createWhisperNodeApi = function (root) {
|
|
|
219
252
|
}
|
|
220
253
|
}
|
|
221
254
|
|
|
222
|
-
|
|
255
|
+
var runtime = await moduleFactory(options)
|
|
256
|
+
runtime.__whisperNodeWasmThreads = useThreads
|
|
257
|
+
return runtime
|
|
223
258
|
})()
|
|
224
259
|
}
|
|
225
260
|
|
|
@@ -253,8 +288,9 @@ const createWhisperNodeApi = function (root) {
|
|
|
253
288
|
|
|
254
289
|
function createWorkerProxy() {
|
|
255
290
|
var indexScriptUrl = getIndexScriptUrl()
|
|
256
|
-
var
|
|
257
|
-
var
|
|
291
|
+
var useThreads = shouldUseThreadedRuntime()
|
|
292
|
+
var runtimeScriptUrl = getRuntimeScriptUrl(indexScriptUrl, useThreads)
|
|
293
|
+
var wasmUrl = getWasmBinaryUrl(runtimeScriptUrl, useThreads)
|
|
258
294
|
var workerScriptUrl = getWorkerScriptUrl(indexScriptUrl)
|
|
259
295
|
|
|
260
296
|
if (!indexScriptUrl || !runtimeScriptUrl || !workerScriptUrl) {
|
|
@@ -367,6 +403,7 @@ const createWhisperNodeApi = function (root) {
|
|
|
367
403
|
indexScriptUrl: indexScriptUrl,
|
|
368
404
|
runtimeScriptUrl: runtimeScriptUrl,
|
|
369
405
|
wasmPath: wasmUrl,
|
|
406
|
+
threads: useThreads,
|
|
370
407
|
runtimeOptions: getWorkerRuntimeOptions(),
|
|
371
408
|
locateFileBaseUrl:
|
|
372
409
|
runtimeOptions.locateFileBaseUrl || resolveUrl('.', runtimeScriptUrl),
|
|
@@ -743,17 +780,21 @@ const createWhisperNodeApi = function (root) {
|
|
|
743
780
|
return value
|
|
744
781
|
}
|
|
745
782
|
|
|
746
|
-
function normalizeMaxThreads(value) {
|
|
783
|
+
function normalizeMaxThreads(value, runtime) {
|
|
784
|
+
var maxThreads =
|
|
785
|
+
runtime && runtime.__whisperNodeWasmThreads !== true
|
|
786
|
+
? 1
|
|
787
|
+
: MAX_WASM_THREADS
|
|
747
788
|
var nThreads = Number(value)
|
|
748
789
|
if (!Number.isFinite(nThreads) || nThreads <= 0) {
|
|
749
790
|
return null
|
|
750
791
|
}
|
|
751
|
-
return Math.max(1, Math.min(
|
|
792
|
+
return Math.max(1, Math.min(maxThreads, Math.floor(nThreads)))
|
|
752
793
|
}
|
|
753
794
|
|
|
754
|
-
function normalizeThreadOptions(options) {
|
|
795
|
+
function normalizeThreadOptions(options, runtime) {
|
|
755
796
|
var normalized = Object.assign({}, options || {})
|
|
756
|
-
var maxThreads = normalizeMaxThreads(normalized.maxThreads)
|
|
797
|
+
var maxThreads = normalizeMaxThreads(normalized.maxThreads, runtime)
|
|
757
798
|
if (maxThreads) {
|
|
758
799
|
normalized.maxThreads = maxThreads
|
|
759
800
|
} else {
|
|
@@ -762,8 +803,8 @@ const createWhisperNodeApi = function (root) {
|
|
|
762
803
|
return normalized
|
|
763
804
|
}
|
|
764
805
|
|
|
765
|
-
function normalizeTranscribeOptions(options) {
|
|
766
|
-
var normalized = normalizeThreadOptions(options)
|
|
806
|
+
function normalizeTranscribeOptions(options, runtime) {
|
|
807
|
+
var normalized = normalizeThreadOptions(options, runtime)
|
|
767
808
|
if (typeof normalized.onProgress !== 'function') {
|
|
768
809
|
delete normalized.onProgress
|
|
769
810
|
}
|
|
@@ -1209,7 +1250,7 @@ const createWhisperNodeApi = function (root) {
|
|
|
1209
1250
|
await runtime.__wasm_transcribe(
|
|
1210
1251
|
id,
|
|
1211
1252
|
audio,
|
|
1212
|
-
normalizeTranscribeOptions(options),
|
|
1253
|
+
normalizeTranscribeOptions(options, runtime),
|
|
1213
1254
|
),
|
|
1214
1255
|
)
|
|
1215
1256
|
if (isCancelled && isCancelled()) {
|
|
@@ -1258,7 +1299,8 @@ const createWhisperNodeApi = function (root) {
|
|
|
1258
1299
|
|
|
1259
1300
|
WhisperContextInstance.prototype.bench = async function (nThreads) {
|
|
1260
1301
|
this._assertValid()
|
|
1261
|
-
|
|
1302
|
+
var threads = normalizeMaxThreads(nThreads || 1, this._runtime) || 1
|
|
1303
|
+
return unwrapWasmResult(await this._runtime.__wasm_bench(this._id, threads))
|
|
1262
1304
|
}
|
|
1263
1305
|
|
|
1264
1306
|
WhisperContextInstance.prototype.release = function () {
|
|
@@ -1352,7 +1394,7 @@ const createWhisperNodeApi = function (root) {
|
|
|
1352
1394
|
var modelSource = options.filePath || options.modelUrl
|
|
1353
1395
|
var runtime = await loadRuntime()
|
|
1354
1396
|
var useGpu = false
|
|
1355
|
-
var nThreads = options.nThreads || 1
|
|
1397
|
+
var nThreads = normalizeMaxThreads(options.nThreads || 1, runtime) || 1
|
|
1356
1398
|
|
|
1357
1399
|
if (options.useGpu === true) {
|
|
1358
1400
|
emitLog(
|
|
@@ -1448,6 +1490,10 @@ const createWhisperNodeApi = function (root) {
|
|
|
1448
1490
|
initWhisperVad: initWhisperVad,
|
|
1449
1491
|
toggleNativeLog: toggleNativeLog,
|
|
1450
1492
|
addNativeLogListener: addNativeLogListener,
|
|
1493
|
+
isNativeLogEnabled: function () {
|
|
1494
|
+
return logEnabled
|
|
1495
|
+
},
|
|
1496
|
+
isWasmThreadsSupported: isWasmThreadsSupported,
|
|
1451
1497
|
DEFAULT_WASM_MODEL_SIZE_LIMIT_BYTES: 1500 * MIB,
|
|
1452
1498
|
MAX_WASM_THREADS: MAX_WASM_THREADS,
|
|
1453
1499
|
WASM_CONFIG_PATHS: WASM_CONFIG_PATHS,
|
|
@@ -1472,6 +1518,8 @@ export const initWhisper = api.initWhisper
|
|
|
1472
1518
|
export const initWhisperVad = api.initWhisperVad
|
|
1473
1519
|
export const toggleNativeLog = api.toggleNativeLog
|
|
1474
1520
|
export const addNativeLogListener = api.addNativeLogListener
|
|
1521
|
+
export const isNativeLogEnabled = api.isNativeLogEnabled
|
|
1522
|
+
export const isWasmThreadsSupported = api.isWasmThreadsSupported
|
|
1475
1523
|
|
|
1476
1524
|
if (root) {
|
|
1477
1525
|
root.WhisperNodeWasm = api
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/node-whisper-wasm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "Browser WASM module for whisper.node",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
"README.md",
|
|
43
43
|
"worker.js",
|
|
44
44
|
"wasm/whisper-node.js",
|
|
45
|
-
"wasm/whisper-node.wasm"
|
|
45
|
+
"wasm/whisper-node.wasm",
|
|
46
|
+
"wasm/whisper-node.threads.js",
|
|
47
|
+
"wasm/whisper-node.threads.wasm"
|
|
46
48
|
]
|
|
47
49
|
}
|