@qvac/bci-whispercpp 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/LICENSE +179 -0
- package/NOTICE +108 -0
- package/README.md +378 -0
- package/addonLogging.d.ts +7 -0
- package/addonLogging.js +6 -0
- package/bci.js +370 -0
- package/binding.js +1 -0
- package/configChecker.js +91 -0
- package/index.d.ts +134 -0
- package/index.js +676 -0
- package/lib/error.js +112 -0
- package/lib/stream.js +207 -0
- package/lib/util.js +15 -0
- package/lib/wer.js +40 -0
- package/package.json +100 -1
- package/prebuilds/android-arm64/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/darwin-arm64/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/darwin-arm64/qvac__bci-whispercpp.bare.exports +1439 -0
- package/prebuilds/darwin-x64/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/darwin-x64/qvac__bci-whispercpp.bare.exports +1654 -0
- package/prebuilds/ios-arm64/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/ios-arm64/qvac__bci-whispercpp.bare.exports +1439 -0
- package/prebuilds/ios-arm64-simulator/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/ios-arm64-simulator/qvac__bci-whispercpp.bare.exports +1439 -0
- package/prebuilds/ios-x64-simulator/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/ios-x64-simulator/qvac__bci-whispercpp.bare.exports +1654 -0
- package/prebuilds/linux-arm64/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/linux-x64/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/win32-x64/qvac__bci-whispercpp.bare +0 -0
- package/prebuilds/win32-x64/qvac__bci-whispercpp.bare.exports +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
# @qvac/bci-whispercpp
|
|
2
|
+
|
|
3
|
+
Brain-Computer Interface (BCI) neural signal transcription addon for qvac, powered by the [tetherto/qvac-ext-lib-whisper.cpp](https://github.com/tetherto/qvac-ext-lib-whisper.cpp) fork of whisper.cpp.
|
|
4
|
+
|
|
5
|
+
Transcribes multi-channel neural signals (e.g., 512-channel microelectrode array recordings) into text using a BCI-trained whisper model running natively via GGML. Output matches the Python BrainWhisperer reference model exactly.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Architecture](#architecture)
|
|
10
|
+
- [Results](#results)
|
|
11
|
+
- [Neural Signal Format](#neural-signal-format)
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Model Conversion](#model-conversion)
|
|
14
|
+
- [Usage](#usage)
|
|
15
|
+
- [Configuration](#configuration)
|
|
16
|
+
- [Tests](#tests)
|
|
17
|
+
- [Error Range](#error-range)
|
|
18
|
+
- [whisper.cpp Patches](#whispercpp-patches)
|
|
19
|
+
- [Resources](#resources)
|
|
20
|
+
- [Glossary](#glossary)
|
|
21
|
+
- [License](#license)
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Neural Signal (512ch, 20ms bins)
|
|
27
|
+
│
|
|
28
|
+
▼
|
|
29
|
+
┌──────────────────────────────┐
|
|
30
|
+
│ NeuralProcessor (C++) │
|
|
31
|
+
│ - Gaussian smoothing │ std=2, kernel=100
|
|
32
|
+
│ - Day-specific projection │ low-rank (A·B) + month + softsign
|
|
33
|
+
│ - Pad to 3000 frames │ mel-major layout for whisper.cpp
|
|
34
|
+
└──────────────┬───────────────┘
|
|
35
|
+
│ mel features (512 × 3000)
|
|
36
|
+
▼
|
|
37
|
+
┌──────────────────────────────┐
|
|
38
|
+
│ whisper.cpp (patched) │
|
|
39
|
+
│ - conv1 (k=7, 512→384) │ BCI-trained embedder weights
|
|
40
|
+
│ - conv2 (k=3, stride=2) │
|
|
41
|
+
│ - Positional encoding │ learned time PE + sinusoidal day PE
|
|
42
|
+
│ - 6-layer encoder │ windowed attention (w=57) on layers 0–3
|
|
43
|
+
│ - 4-layer decoder (LoRA) │ beam search, length_penalty=0.14
|
|
44
|
+
└──────────────┬───────────────┘
|
|
45
|
+
│
|
|
46
|
+
▼
|
|
47
|
+
Text output
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Results
|
|
51
|
+
|
|
52
|
+
Native GGML inference matches the Python BrainWhisperer reference on all test samples:
|
|
53
|
+
|
|
54
|
+
| Sample | Ground Truth | GGML Native Output | WER |
|
|
55
|
+
|--------|-------------|-------------------|-----|
|
|
56
|
+
| 0 | "You can see the code at this point as well." | "You can see the good at this point as well." | 10.0% |
|
|
57
|
+
| 1 | "How does it keep the cost down?" | "How does it keep the cost down?" | 0.0% |
|
|
58
|
+
| 2 | "Not too controversial." | "Not too controversial." | 0.0% |
|
|
59
|
+
| 3 | "The jury and a judge work together on it." | "The jury and a judge work together on it." | 0.0% |
|
|
60
|
+
| 4 | "Were quite vocal about it." | "We're quite vocal about it." | 20.0% |
|
|
61
|
+
| **Average** | | | **6.0%** |
|
|
62
|
+
|
|
63
|
+
## Neural Signal Format
|
|
64
|
+
|
|
65
|
+
Binary files with the following layout:
|
|
66
|
+
|
|
67
|
+
| Offset | Type | Description |
|
|
68
|
+
|--------|-----------|------------------------------------------------------|
|
|
69
|
+
| 0 | uint32 | Number of timesteps |
|
|
70
|
+
| 4 | uint32 | Number of channels |
|
|
71
|
+
| 8 | float32[] | Feature data (row-major: `features[t * channels + c]`) |
|
|
72
|
+
|
|
73
|
+
Each timestep represents a 20ms bin of neural activity. Channels correspond to individual electrodes in a microelectrode array (typically 512 channels).
|
|
74
|
+
|
|
75
|
+
## Installation
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
cd packages/bci-whispercpp
|
|
79
|
+
npm install
|
|
80
|
+
VCPKG_ROOT=/path/to/vcpkg npm run build
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Prerequisites
|
|
84
|
+
|
|
85
|
+
- **Bare runtime** >= 1.24.0
|
|
86
|
+
- **CMake** >= 3.25
|
|
87
|
+
- **vcpkg** with `VCPKG_ROOT` environment variable set
|
|
88
|
+
|
|
89
|
+
### Model Conversion Prerequisites
|
|
90
|
+
|
|
91
|
+
- **Python 3** with `numpy`, `torch`, and `transformers` (`pip install numpy torch transformers`)
|
|
92
|
+
|
|
93
|
+
### Model Conversion
|
|
94
|
+
|
|
95
|
+
Convert a trained BrainWhisperer checkpoint. This produces **two files**, both required for inference:
|
|
96
|
+
|
|
97
|
+
| File | Size | Description |
|
|
98
|
+
|------|------|-------------|
|
|
99
|
+
| `ggml-bci-windowed.bin` | ~84 MB | GGML model: whisper encoder/decoder (LoRA-merged), tokenizer, positional embedding, windowed attention header |
|
|
100
|
+
| `bci-embedder.bin` | ~24 MB | Day projection weights: low-rank A·B matrices per recording day, month projections, session-to-day mapping |
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
python3 scripts/convert-model.py \
|
|
104
|
+
--checkpoint /path/to/epoch=93-val_wer=0.0910.ckpt
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Both files are written to `models/` by default. All flags are optional:
|
|
108
|
+
|
|
109
|
+
| Flag | Default | Description |
|
|
110
|
+
|------|---------|-------------|
|
|
111
|
+
| `--output` | `models/ggml-bci-windowed.bin` | GGML model output path |
|
|
112
|
+
| `--embedder-output` | `models/bci-embedder.bin` | Embedder weights output path |
|
|
113
|
+
| `--day-idx` | `1` | Day index for baked positional embedding |
|
|
114
|
+
| `--window-size` | `57` | Windowed attention size (0 to disable) |
|
|
115
|
+
| `--last-window-layer` | `3` | Last encoder layer with windowed attention |
|
|
116
|
+
| `--f32` | off | Use f32 for all tensors (avoids f16 precision loss, ~2x larger) |
|
|
117
|
+
|
|
118
|
+
**Important:** Both files must be in the same directory at runtime. The C++ addon looks for `bci-embedder.bin` next to the GGML model file and will fail if it is missing.
|
|
119
|
+
|
|
120
|
+
## Usage
|
|
121
|
+
|
|
122
|
+
The package's default export is the high-level `BCIWhispercpp` class. It owns model lifecycle, an inference queue, and a sliding-window streaming driver on top of the native addon.
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
const BCIWhispercpp = require('@qvac/bci-whispercpp')
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 1. Construct an instance
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
const bci = new BCIWhispercpp({
|
|
132
|
+
files: { model: './models/ggml-bci-windowed.bin' },
|
|
133
|
+
opts: { stats: true } // optional — surfaces runtime stats on response.stats
|
|
134
|
+
}, {
|
|
135
|
+
whisperConfig: { language: 'en', temperature: 0.0 },
|
|
136
|
+
bciConfig: { day_idx: 1 }, // session day index for day-specific projection
|
|
137
|
+
miscConfig: { caption_enabled: false }
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> The companion `bci-embedder.bin` must sit next to `files.model`. The native addon resolves it by path and will fail to load otherwise.
|
|
142
|
+
|
|
143
|
+
### 2. Load the model
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
await bci.load()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`load()` is idempotent — calling it again unloads the existing model and re-initialises with the current config. There is no progress callback today.
|
|
150
|
+
|
|
151
|
+
### 3. Transcribe (batch mode)
|
|
152
|
+
|
|
153
|
+
Use this when you have the full neural signal up-front. `transcribe()` accepts the raw bytes (header + body); `transcribeFile()` is a convenience wrapper that reads the file for you.
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
const fs = require('bare-fs')
|
|
157
|
+
|
|
158
|
+
const response = await bci.transcribeFile('./signal.bin')
|
|
159
|
+
// or: const response = await bci.transcribe(new Uint8Array(fs.readFileSync('./signal.bin')))
|
|
160
|
+
|
|
161
|
+
const segments = await response.await()
|
|
162
|
+
const text = segments.map(s => s.text).join('').trim()
|
|
163
|
+
console.log(text)
|
|
164
|
+
|
|
165
|
+
if (response.stats) console.log(response.stats) // when opts.stats: true
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Concurrent calls are serialised — a second `transcribe()` waits for the first to settle.
|
|
169
|
+
|
|
170
|
+
### 4. Transcribe (streaming mode)
|
|
171
|
+
|
|
172
|
+
`transcribeStream()` consumes a stream of bytes (async iterable, sync iterable, `Uint8Array`, or array of chunks) and decodes a sliding window over the body as data arrives. The first 8 bytes of the stream must be the standard `[T u32 LE, C u32 LE]` header (`T` is ignored in stream mode; `C` must be non-zero).
|
|
173
|
+
|
|
174
|
+
```js
|
|
175
|
+
const response = await bci.transcribeStream(chunkIterable, {
|
|
176
|
+
windowTimesteps: 1500, // default
|
|
177
|
+
hopTimesteps: 500, // default — must be < windowTimesteps
|
|
178
|
+
emit: 'delta' // 'delta' (default) | 'full'
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
response.onUpdate(segments => {
|
|
182
|
+
// emit: 'delta' — newly-discovered tail segments since the last window.
|
|
183
|
+
// Each segment carries native fields (text, t0, t1, ...) plus
|
|
184
|
+
// windowStartTimestep so you can map back to the stream timeline.
|
|
185
|
+
// emit: 'full' — single { text } entry with the full running transcript.
|
|
186
|
+
for (const s of segments) process.stdout.write(s.text)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
await response.await() // resolves when the stream ends and the final window decodes
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Streaming constraints:
|
|
193
|
+
|
|
194
|
+
| Option | Constraint |
|
|
195
|
+
|--------|------------|
|
|
196
|
+
| `windowTimesteps` | positive integer, ≤ `2900` (`MAX_WINDOW_TIMESTEPS`) |
|
|
197
|
+
| `hopTimesteps` | positive integer, `< windowTimesteps` |
|
|
198
|
+
| `emit` | `'delta'` or `'full'` |
|
|
199
|
+
|
|
200
|
+
Only one stream may be active at a time. `response.stats` is **not** populated for streams.
|
|
201
|
+
|
|
202
|
+
### 5. Cancel / unload / destroy
|
|
203
|
+
|
|
204
|
+
```js
|
|
205
|
+
await bci.cancel() // abort an in-flight job or stream; instance remains usable
|
|
206
|
+
await bci.unload() // release native resources; bci.load() can be called again
|
|
207
|
+
await bci.destroy() // permanent — instance cannot be reused
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 6. Word Error Rate helper
|
|
211
|
+
|
|
212
|
+
The package re-exports `computeWER(hypothesis, reference)` for evaluation:
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
const { computeWER } = require('@qvac/bci-whispercpp')
|
|
216
|
+
const wer = computeWER('how does it keep the cost down', 'how does it keep the cost down?')
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Output shape
|
|
220
|
+
|
|
221
|
+
`response.await()` resolves to an array of segments; `response.onUpdate(cb)` receives the same shape per emission:
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
[
|
|
225
|
+
{ text: ' How does it keep the cost down?', t0: 0, t1: 280, /* ... */ }
|
|
226
|
+
]
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
In streaming `delta` mode each segment is annotated with `windowStartTimestep`. In `full` mode the array contains a single `{ text }` entry.
|
|
230
|
+
|
|
231
|
+
## Tests
|
|
232
|
+
|
|
233
|
+
| Script | Purpose |
|
|
234
|
+
|--------|---------|
|
|
235
|
+
| `npm run test:unit` | JS unit tests (`brittle-bare test/unit/*.test.js`) — no model required |
|
|
236
|
+
| `npm run test:integration` | JS integration tests against the native addon — requires `WHISPER_MODEL_PATH` |
|
|
237
|
+
| `npm run test:cpp` | C++ unit tests (GoogleTest); `bare-make` rebuilds the addon with `BUILD_TESTING=ON` |
|
|
238
|
+
| `npm run test:dts` | Type-checks the published `index.d.ts` |
|
|
239
|
+
| `npm test` | Runs `test:unit` + `test:integration` |
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# JS unit tests
|
|
243
|
+
npm run test:unit
|
|
244
|
+
|
|
245
|
+
# JS integration tests
|
|
246
|
+
WHISPER_MODEL_PATH=./models/ggml-bci-windowed.bin npm run test:integration
|
|
247
|
+
|
|
248
|
+
# C++ unit tests
|
|
249
|
+
VCPKG_ROOT=/path/to/vcpkg npm run test:cpp
|
|
250
|
+
|
|
251
|
+
# .d.ts typecheck
|
|
252
|
+
npm run test:dts
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Integration tests require both `ggml-bci-windowed.bin` and `bci-embedder.bin` to be present in the same directory. See [Model Conversion](#model-conversion).
|
|
256
|
+
|
|
257
|
+
## Configuration
|
|
258
|
+
|
|
259
|
+
`BCIWhispercpp` accepts two arguments:
|
|
260
|
+
|
|
261
|
+
```js
|
|
262
|
+
new BCIWhispercpp(args, config)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### args
|
|
266
|
+
|
|
267
|
+
| Field | Type | Description |
|
|
268
|
+
|-------|------|-------------|
|
|
269
|
+
| `files.model` | string | **Required.** Path to BCI GGML model file (`bci-embedder.bin` must sit alongside it). |
|
|
270
|
+
| `logger` | object | Optional logger; wrapped in `@qvac/logging`. Defaults to a noop logger. |
|
|
271
|
+
| `opts.stats` | boolean | When `true`, runtime stats are surfaced on `response.stats` for batch jobs. Default `false`. |
|
|
272
|
+
|
|
273
|
+
### config.whisperConfig
|
|
274
|
+
|
|
275
|
+
The convenience defaults below are surfaced explicitly. **Any other `whisper_full_params` key is forwarded untouched** to whisper.cpp — see [Advanced configuration](#advanced-configuration).
|
|
276
|
+
|
|
277
|
+
| Parameter | Type | Default | Description |
|
|
278
|
+
|-----------|------|---------|-------------|
|
|
279
|
+
| `language` | string | `"en"` | Language code |
|
|
280
|
+
| `temperature` | number | `0.0` | Sampling temperature |
|
|
281
|
+
| `n_threads` | number | `0` (auto) | Number of threads |
|
|
282
|
+
|
|
283
|
+
### config.bciConfig
|
|
284
|
+
|
|
285
|
+
| Parameter | Type | Default | Description |
|
|
286
|
+
|-----------|------|---------|-------------|
|
|
287
|
+
| `day_idx` | number | `0` | Session day index for the day-specific low-rank projection at runtime. Distinct from the conversion-time `--day-idx` flag, which bakes a positional embedding into `ggml-bci-windowed.bin`. |
|
|
288
|
+
|
|
289
|
+
### config.contextParams
|
|
290
|
+
|
|
291
|
+
These keys back the `whisper_context`. Changing any of them between jobs forces a full model reload (unload → re-init → warmup), which can take several seconds.
|
|
292
|
+
|
|
293
|
+
| Parameter | Type | Description |
|
|
294
|
+
|-----------|------|-------------|
|
|
295
|
+
| `model` | string | Optional override; usually set via `args.files.model`. |
|
|
296
|
+
| `use_gpu` | boolean | Enable GPU acceleration (Metal on macOS by default). |
|
|
297
|
+
| `flash_attn` | boolean | Enable flash attention. |
|
|
298
|
+
| `gpu_device` | number | Select a non-default GPU device. |
|
|
299
|
+
|
|
300
|
+
### config.miscConfig
|
|
301
|
+
|
|
302
|
+
| Parameter | Type | Default | Description |
|
|
303
|
+
|-----------|------|---------|-------------|
|
|
304
|
+
| `caption_enabled` | boolean | `false` | Format segments with `<\|start\|>..<\|end\|>` markers. |
|
|
305
|
+
|
|
306
|
+
### streamOpts (passed to `transcribeStream()`)
|
|
307
|
+
|
|
308
|
+
| Parameter | Type | Default | Constraint | Description |
|
|
309
|
+
|-----------|------|---------|------------|-------------|
|
|
310
|
+
| `windowTimesteps` | number | `1500` | positive integer, ≤ `2900` (`MAX_WINDOW_TIMESTEPS`) | Decode window size in 20 ms timesteps. |
|
|
311
|
+
| `hopTimesteps` | number | `500` | positive integer, `< windowTimesteps` | How far the window advances between decodes (~33% overlap by default). |
|
|
312
|
+
| `emit` | string | `'delta'` | `'delta'` or `'full'` | `'delta'` emits the newly-discovered tail per window with native segment fields plus `windowStartTimestep`. `'full'` emits a single `{ text }` entry with the running transcript. |
|
|
313
|
+
|
|
314
|
+
The encoder accepts up to ~3000 timesteps per forward pass; `MAX_WINDOW_TIMESTEPS = 2900` keeps a safety margin so partial flush windows always fit.
|
|
315
|
+
|
|
316
|
+
### Advanced configuration
|
|
317
|
+
|
|
318
|
+
`whisperConfig` is a thin pass-through to whisper.cpp's `whisper_full_params`. For the full surface (decoding strategy, beam search, VAD, suppression, callbacks, etc.) refer to the upstream reference:
|
|
319
|
+
|
|
320
|
+
- [`whisper_full_params` in whisper.cpp](https://github.com/ggerganov/whisper.cpp/blob/master/include/whisper.h)
|
|
321
|
+
- Concrete shapes used in production: see the [examples](examples) directory and [`@qvac/transcription-whispercpp`](https://github.com/tetherto/qvac/tree/main/packages/qvac-lib-infer-whispercpp) for richer usage patterns (VAD, chunking, live streaming).
|
|
322
|
+
|
|
323
|
+
## whisper.cpp Patches
|
|
324
|
+
|
|
325
|
+
The BCI patches live in the `tetherto/qvac-ext-lib-whisper.cpp` fork (v1.8.4.2) and are consumed via the `qvac-registry-vcpkg` port:
|
|
326
|
+
|
|
327
|
+
| Feature | Description |
|
|
328
|
+
|---------|-------------|
|
|
329
|
+
| Variable conv1 kernel | Read `n_audio_conv1_kernel` from model header (k=7 for 512ch BCI vs k=3 for audio) |
|
|
330
|
+
| Windowed attention | Attention mask with configurable window size/layer params in header |
|
|
331
|
+
| BCI SOS tokens | BCI-specific start-of-sequence token handling |
|
|
332
|
+
| Graph placement fix | Correct encoder-graph mask population for the encoder graph refactor |
|
|
333
|
+
|
|
334
|
+
## Error Range
|
|
335
|
+
|
|
336
|
+
All errors thrown by this package are `QvacErrorAddonBCI` instances (extending `QvacErrorBase` from `@qvac/error`) and use codes in the range **26001–27000**.
|
|
337
|
+
|
|
338
|
+
| Code | Name | When |
|
|
339
|
+
|------|------|------|
|
|
340
|
+
| `26001` | `FAILED_TO_LOAD_WEIGHTS` | Native addon failed to load the GGML model |
|
|
341
|
+
| `26002` | `FAILED_TO_CANCEL` | `cancel()` failed at the addon layer |
|
|
342
|
+
| `26003` | `FAILED_TO_APPEND` | Append to processing queue failed |
|
|
343
|
+
| `26004` | `FAILED_TO_DESTROY` | `destroy()` failed at the addon layer |
|
|
344
|
+
| `26005` | `FAILED_TO_ACTIVATE` | `addon.activate()` failed during `load()` |
|
|
345
|
+
| `26006` | `INVALID_NEURAL_INPUT` | Batch input rejected by the addon |
|
|
346
|
+
| `26007` | `JOB_ALREADY_RUNNING` | `transcribe()` called while a job is in flight |
|
|
347
|
+
| `26008` | `MODEL_NOT_LOADED` | Inference called before `load()` or after `destroy()` |
|
|
348
|
+
| `26009` | `MODEL_FILE_NOT_FOUND` | `files.model` missing or unreadable |
|
|
349
|
+
| `26010` | `BUFFER_LIMIT_EXCEEDED` | Neural signal buffer exceeded the addon limit |
|
|
350
|
+
| `26011` | `FAILED_TO_START_JOB` | Addon refused to start the job |
|
|
351
|
+
| `26012` | `INVALID_CONFIG` | Constructor / context configuration rejected |
|
|
352
|
+
| `26013` | `EMBEDDER_WEIGHTS_INVALID` | `bci-embedder.bin` failed validation |
|
|
353
|
+
| `26014` | `STREAM_ALREADY_ACTIVE` | `transcribeStream()` called while one is already active |
|
|
354
|
+
| `26015` | `INVALID_STREAM_INPUT` | Bad stream input type or `streamOpts` |
|
|
355
|
+
| `26016` | `INVALID_STREAM_HEADER` | Stream `[T u32, C u32]` header malformed (e.g. `C == 0`) |
|
|
356
|
+
| `26017` | `WINDOW_TOO_LARGE` | `windowTimesteps` exceeds `MAX_WINDOW_TIMESTEPS` (2900) |
|
|
357
|
+
|
|
358
|
+
Codes are also re-exported via `require('@qvac/bci-whispercpp/lib/error').ERR_CODES` for programmatic matching.
|
|
359
|
+
|
|
360
|
+
## Resources
|
|
361
|
+
|
|
362
|
+
- whisper.cpp fork (Tether): [`tetherto/qvac-ext-lib-whisper.cpp`](https://github.com/tetherto/qvac-ext-lib-whisper.cpp)
|
|
363
|
+
- Sibling package — audio transcription: [`@qvac/transcription-whispercpp`](https://github.com/tetherto/qvac/tree/main/packages/qvac-lib-infer-whispercpp)
|
|
364
|
+
- vcpkg registry: [`qvac-registry-vcpkg`](https://github.com/tetherto/qvac-registry-vcpkg)
|
|
365
|
+
- BrainWhisperer reference (Python): the model checkpoints converted by `scripts/convert-model.py`
|
|
366
|
+
|
|
367
|
+
## Glossary
|
|
368
|
+
|
|
369
|
+
- **Bare** — small modular JavaScript runtime for desktop and mobile. [Learn more](https://docs.pears.com/bare-reference/overview).
|
|
370
|
+
- **QVAC** — Tether's open-source SDK for building decentralized, local-first AI applications.
|
|
371
|
+
- **GGML** — tensor library / file format used by whisper.cpp for native inference.
|
|
372
|
+
- **BCI** — Brain-Computer Interface; here, microelectrode-array recordings of neural activity decoded into text.
|
|
373
|
+
- **Day index (`day_idx`)** — selects the day-specific low-rank projection (A·B) baked into `bci-embedder.bin`. Sessions recorded on different days use different projections.
|
|
374
|
+
- **Windowed attention** — encoder attention mask restricted to a local window (`w=57` over layers 0–3 by default), configured at model conversion time.
|
|
375
|
+
|
|
376
|
+
## License
|
|
377
|
+
|
|
378
|
+
Apache-2.0
|