@qvac/sdk 0.12.2 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +55 -1225
- package/README.md +12 -0
- package/dist/client/api/bci-transcribe.d.ts +57 -0
- package/dist/client/api/bci-transcribe.js +172 -0
- package/dist/client/api/get-model-info.d.ts +1 -1
- package/dist/client/api/index.d.ts +1 -0
- package/dist/client/api/index.js +1 -0
- package/dist/client/api/video.d.ts +26 -6
- package/dist/client/api/video.js +30 -7
- package/dist/client/api/vla.d.ts +11 -7
- package/dist/client/api/vla.js +11 -7
- package/dist/client/rpc/bare-client.js +2 -2
- package/dist/commands/bundle/constants.js +2 -0
- package/dist/examples/bci/bci-filesystem-streaming.d.ts +2 -0
- package/dist/examples/bci/bci-filesystem-streaming.js +77 -0
- package/dist/examples/bci/bci-filesystem.d.ts +2 -0
- package/dist/examples/bci/bci-filesystem.js +63 -0
- package/dist/examples/cache-management.js +0 -1
- package/dist/examples/cancel-by-request-id.js +0 -1
- package/dist/examples/classification/classify-image.js +1 -1
- package/dist/examples/completion-events.js +6 -2
- package/dist/examples/completion-stop-reason.d.ts +25 -0
- package/dist/examples/completion-stop-reason.js +65 -0
- package/dist/examples/concurrent-completion-collision.d.ts +2 -0
- package/dist/examples/concurrent-completion-collision.js +98 -0
- package/dist/examples/config-reload.js +1 -2
- package/dist/examples/default-config-usage.js +0 -1
- package/dist/examples/delegated-inference/composite.js +0 -1
- package/dist/examples/delegated-inference/consumer-profiled.js +0 -1
- package/dist/examples/delegated-inference/consumer.js +0 -1
- package/dist/examples/diffusion-esrgan-upscale.js +1 -3
- package/dist/examples/diffusion-flux2-klein-img2img.js +1 -1
- package/dist/examples/diffusion-flux2-klein.js +2 -2
- package/dist/examples/diffusion-img2img.js +1 -1
- package/dist/examples/diffusion-img2vid.d.ts +2 -0
- package/dist/examples/diffusion-img2vid.js +78 -0
- package/dist/examples/diffusion-simple.js +1 -1
- package/dist/examples/diffusion-txt2img.js +8 -3
- package/dist/examples/diffusion-txt2vid.js +2 -3
- package/dist/examples/download-with-blind-relays.js +0 -1
- package/dist/examples/embed-multi-gpu.js +1 -2
- package/dist/examples/embed-p2p.js +4 -2
- package/dist/examples/finetune/llamacpp-finetune.js +5 -3
- package/dist/examples/kv-cache-cleanup.js +0 -1
- package/dist/examples/kv-cache-custom-key.js +0 -1
- package/dist/examples/kv-cache-example.js +4 -2
- package/dist/examples/llamacpp-cache.js +0 -1
- package/dist/examples/llamacpp-dynamic-tools.js +4 -2
- package/dist/examples/llamacpp-filesystem.js +1 -1
- package/dist/examples/llamacpp-http.js +1 -1
- package/dist/examples/llamacpp-multi-gpu.js +2 -2
- package/dist/examples/llamacpp-multimodal.js +0 -1
- package/dist/examples/llamacpp-p2p.js +0 -1
- package/dist/examples/llamacpp-sharded.js +1 -1
- package/dist/examples/llamacpp-structured-output.js +0 -1
- package/dist/examples/logging-file-transport.js +0 -1
- package/dist/examples/logging-streaming.js +0 -2
- package/dist/examples/mcp-websearch.js +0 -1
- package/dist/examples/multi-model-demo.js +0 -2
- package/dist/examples/ocr-fasttext.js +0 -1
- package/dist/examples/plugins.js +1 -4
- package/dist/examples/profiling/basic.js +3 -2
- package/dist/examples/profiling/per-call.js +0 -1
- package/dist/examples/quickstart.js +0 -1
- package/dist/examples/rag/rag-chromadb.js +0 -1
- package/dist/examples/rag/rag-hyperdb/cancellation.js +0 -1
- package/dist/examples/rag/rag-hyperdb/ingest.js +0 -1
- package/dist/examples/rag/rag-hyperdb/pipeline.js +0 -1
- package/dist/examples/rag/rag-hyperdb/workspaces.js +0 -1
- package/dist/examples/rag/rag-lancedb.js +0 -1
- package/dist/examples/rag/rag-sqlite.js +0 -1
- package/dist/examples/suspend-resume.js +0 -1
- package/dist/examples/tools/llamacpp-native-tools.js +0 -1
- package/dist/examples/tools/llamacpp-tools-gemma4.js +1 -1
- package/dist/examples/tools/llamacpp-tools-pythonic.js +2 -2
- package/dist/examples/tools/llamacpp-tools-qwen35.js +1 -1
- package/dist/examples/transcription/parakeet-ctc-filesystem.js +1 -1
- package/dist/examples/transcription/parakeet-microphone-record.js +0 -1
- package/dist/examples/transcription/parakeet-microphone-stream.js +0 -1
- package/dist/examples/transcription/parakeet-sortformer.js +1 -2
- package/dist/examples/transcription/parakeet-tdt-filesystem.js +1 -1
- package/dist/examples/transcription/whispercpp-filesystem-streaming.js +10 -6
- package/dist/examples/transcription/whispercpp-filesystem.js +4 -2
- package/dist/examples/transcription/whispercpp-microphone-conversation.js +0 -1
- package/dist/examples/transcription/whispercpp-microphone-record.js +0 -1
- package/dist/examples/transcription/whispercpp-prompt.js +0 -1
- package/dist/examples/translation/translation-bergamot-batch.js +1 -2
- package/dist/examples/translation/translation-bergamot-pivot.js +3 -4
- package/dist/examples/translation/translation-bergamot.js +1 -2
- package/dist/examples/translation/translation-indic.js +1 -2
- package/dist/examples/translation/translation-llm-afriquegemma.js +3 -4
- package/dist/examples/translation/translation-llm-context.js +1 -2
- package/dist/examples/translation/translation-llm.js +2 -3
- package/dist/examples/translation/translation-stream.js +2 -3
- package/dist/examples/tts/chatterbox.js +1 -2
- package/dist/examples/tts/llm-to-tts-streaming.js +2 -4
- package/dist/examples/tts/supertonic-multilingual.js +1 -2
- package/dist/examples/tts/supertonic.js +1 -2
- package/dist/examples/vla-pi05.d.ts +2 -0
- package/dist/examples/vla-pi05.js +84 -0
- package/dist/examples/vla-smolvla.js +1 -1
- package/dist/examples/voice-assistant/voice-assistant.js +1 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9 -7
- package/dist/models/registry/models.d.ts +869 -186
- package/dist/models/registry/models.js +3160 -2453
- package/dist/models/update-models/companions.d.ts +7 -0
- package/dist/models/update-models/companions.js +56 -0
- package/dist/models/update-models/naming.js +10 -0
- package/dist/pear/pre.js +2 -0
- package/dist/schemas/bci-config.d.ts +36 -0
- package/dist/schemas/bci-config.js +56 -0
- package/dist/schemas/bci.d.ts +177 -0
- package/dist/schemas/bci.js +95 -0
- package/dist/schemas/common.d.ts +395 -51
- package/dist/schemas/common.js +5 -0
- package/dist/schemas/completion-event.d.ts +1 -0
- package/dist/schemas/download-asset.d.ts +7 -3
- package/dist/schemas/embed.d.ts +2 -0
- package/dist/schemas/embed.js +1 -0
- package/dist/schemas/engine-addon-map.d.ts +1 -0
- package/dist/schemas/engine-addon-map.js +4 -1
- package/dist/schemas/get-model-info.d.ts +2 -0
- package/dist/schemas/get-model-info.js +1 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/llamacpp-config.d.ts +6 -2
- package/dist/schemas/load-model.d.ts +1841 -267
- package/dist/schemas/load-model.js +34 -1
- package/dist/schemas/model-src-utils.d.ts +10 -2
- package/dist/schemas/model-types.d.ts +19 -1
- package/dist/schemas/model-types.js +11 -0
- package/dist/schemas/ocr.d.ts +2 -0
- package/dist/schemas/plugin.d.ts +13 -5
- package/dist/schemas/plugin.js +13 -4
- package/dist/schemas/registry.d.ts +18 -0
- package/dist/schemas/registry.js +2 -0
- package/dist/schemas/reload-config.d.ts +10 -2
- package/dist/schemas/sdcpp-config.d.ts +75 -8
- package/dist/schemas/sdcpp-config.js +56 -10
- package/dist/schemas/sdk-config.d.ts +342 -0
- package/dist/schemas/text-to-speech.d.ts +8 -0
- package/dist/schemas/transcription-config.d.ts +2 -0
- package/dist/schemas/transcription.d.ts +12 -0
- package/dist/schemas/transcription.js +8 -0
- package/dist/schemas/translation-config.d.ts +44 -16
- package/dist/schemas/translation-config.js +2 -0
- package/dist/schemas/vla.d.ts +14 -0
- package/dist/schemas/vla.js +26 -3
- package/dist/server/bare/delegate-connect-diagnostics.d.ts +9 -0
- package/dist/server/bare/delegate-connect-diagnostics.js +44 -0
- package/dist/server/bare/delegate-rpc-client.js +10 -12
- package/dist/server/bare/hyperswarm.js +1 -0
- package/dist/server/bare/ops/bci-transcribe.d.ts +13 -0
- package/dist/server/bare/ops/bci-transcribe.js +248 -0
- package/dist/server/bare/ops/embed.js +2 -1
- package/dist/server/bare/ops/kv-cache-utils.js +4 -3
- package/dist/server/bare/ops/transcribe.js +6 -2
- package/dist/server/bare/ops/translate.js +1 -1
- package/dist/server/bare/plugins/bci-whispercpp-transcription/plugin.d.ts +130 -0
- package/dist/server/bare/plugins/bci-whispercpp-transcription/plugin.js +116 -0
- package/dist/server/bare/plugins/ggml-vla/ops/vla-run.js +4 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.d.ts +7 -0
- package/dist/server/bare/plugins/ggml-vla/plugin.js +1 -1
- package/dist/server/bare/plugins/index.d.ts +1 -0
- package/dist/server/bare/plugins/index.js +1 -0
- package/dist/server/bare/plugins/llamacpp-completion/ops/finetune.js +22 -29
- package/dist/server/bare/plugins/llamacpp-completion/plugin.d.ts +2 -0
- package/dist/server/bare/plugins/llamacpp-completion/plugin.js +38 -7
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.d.ts +1 -0
- package/dist/server/bare/plugins/llamacpp-embedding/plugin.js +9 -1
- package/dist/server/bare/plugins/nmtcpp-translation/plugin.d.ts +14 -0
- package/dist/server/bare/plugins/onnx-ocr/plugin.d.ts +2 -0
- package/dist/server/bare/plugins/parakeet-transcription/plugin.d.ts +8 -0
- package/dist/server/bare/plugins/sdcpp-generation/ops/video.js +7 -1
- package/dist/server/bare/plugins/sdcpp-generation/plugin.d.ts +58 -4
- package/dist/server/bare/plugins/sdcpp-generation/plugin.js +9 -4
- package/dist/server/bare/plugins/whispercpp-transcription/plugin.d.ts +10 -0
- package/dist/server/bare/registry/model-config-utils.js +3 -0
- package/dist/server/bare/runtime/request-registry-singleton.js +11 -4
- package/dist/server/bare/runtime/request-registry.d.ts +60 -16
- package/dist/server/bare/runtime/request-registry.js +258 -15
- package/dist/server/bare/runtime/with-request-context.d.ts +1 -1
- package/dist/server/bare/runtime/with-request-context.js +1 -1
- package/dist/server/bare/types/addon-responses.d.ts +5 -0
- package/dist/server/rpc/handler-registry.js +4 -0
- package/dist/server/rpc/handlers/bci-transcribe-stream.d.ts +3 -0
- package/dist/server/rpc/handlers/bci-transcribe-stream.js +4 -0
- package/dist/server/rpc/handlers/bci-transcribe.d.ts +3 -0
- package/dist/server/rpc/handlers/bci-transcribe.js +4 -0
- package/dist/server/rpc/handlers/download-asset.js +1 -1
- package/dist/server/rpc/handlers/get-model-info.js +3 -3
- package/dist/server/rpc/handlers/index.d.ts +4 -0
- package/dist/server/rpc/handlers/index.js +4 -0
- package/dist/server/rpc/handlers/load-model/download-manager.d.ts +1 -1
- package/dist/server/rpc/handlers/load-model/handler.js +1 -1
- package/dist/server/rpc/handlers/load-model/http.js +6 -6
- package/dist/server/rpc/handlers/load-model/hyperdrive.js +1 -0
- package/dist/server/rpc/handlers/load-model/registry-companion-set.d.ts +1 -0
- package/dist/server/rpc/handlers/load-model/registry-companion-set.js +2 -2
- package/dist/server/rpc/handlers/load-model/registry.js +1 -0
- package/dist/server/rpc/handlers/load-model/resolve-session.d.ts +1 -1
- package/dist/server/rpc/handlers/rag.js +5 -5
- package/dist/server/utils/audio/decoder.js +1 -1
- package/dist/server/utils/checksum.js +2 -1
- package/dist/server/utils/formatting.js +2 -1
- package/dist/server/utils/multi-gpu-mobile.d.ts +5 -0
- package/dist/server/utils/multi-gpu-mobile.js +10 -0
- package/dist/server/worker.js +2 -1
- package/dist/utils/aggregate-events.d.ts +2 -1
- package/dist/utils/aggregate-events.js +11 -3
- package/dist/utils/errors-server.d.ts +6 -4
- package/dist/utils/errors-server.js +6 -4
- package/electron-forge/index.cjs +820 -0
- package/package.json +70 -37
- package/dist/scripts/run-security-tests-bare.d.ts +0 -2
- package/dist/scripts/run-security-tests-bare.js +0 -78
package/README.md
CHANGED
|
@@ -117,6 +117,18 @@ This outputs a tarball under `dist/sdk-{version}.tgz` that you can install in yo
|
|
|
117
117
|
npm i path/to/sdk-0.3.0.tgz
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
## Testing
|
|
121
|
+
|
|
122
|
+
The SDK test suite is organized into three buckets by runtime:
|
|
123
|
+
|
|
124
|
+
| Bucket | Runtime | Location | Command |
|
|
125
|
+
|--------|---------|----------|---------|
|
|
126
|
+
| Unit | Bun / Node | `test/unit/` | `bun run test:unit` |
|
|
127
|
+
| Server (Bare) | Bare | `test/bare/` | `bun run test:bare` |
|
|
128
|
+
| Client (consumer) | Node / RN | `e2e/` | See [`e2e/README.md`](./e2e/README.md) |
|
|
129
|
+
|
|
130
|
+
See [`TESTING.md`](./TESTING.md) for the full decision tree on where new tests should land.
|
|
131
|
+
|
|
120
132
|
## Contributing
|
|
121
133
|
|
|
122
134
|
### Commit Message and PR Title Format
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type BciTranscribeClientParams, type BciTranscribeStreamClientParams, type BciTranscribeStreamSession, type BciTranscribeStreamMetadataSession, type RPCOptions, type TranscribeSegment } from "../../schemas/index";
|
|
2
|
+
/**
|
|
3
|
+
* Transcribe a neural-signal buffer with a loaded BCI model and return the
|
|
4
|
+
* complete text. Accepts either a `.bin` file path or a raw neural buffer.
|
|
5
|
+
*
|
|
6
|
+
* @param params - BCI transcription parameters.
|
|
7
|
+
* @param params.modelId - The identifier of the loaded BCI model to use.
|
|
8
|
+
* @param params.neuralData - Neural signal as either a file path (string) or
|
|
9
|
+
* a binary buffer (`Uint8Array`).
|
|
10
|
+
* @param params.metadata - When true, resolves to an array of transcript
|
|
11
|
+
* segments (`{ text, startMs, endMs, append, id }`)
|
|
12
|
+
* instead of joined text.
|
|
13
|
+
* @param options - Optional RPC options including per-call profiling.
|
|
14
|
+
* @returns A promise (decorated with `requestId`) resolving to the complete
|
|
15
|
+
* transcribed text, or — when `metadata` is true — the list of
|
|
16
|
+
* transcript segments in emission order. The `requestId` is
|
|
17
|
+
* reachable synchronously so callers can target this in-flight
|
|
18
|
+
* transcription with `cancel({ requestId })` before `await` resolves.
|
|
19
|
+
*/
|
|
20
|
+
export declare function bciTranscribe(params: BciTranscribeClientParams & {
|
|
21
|
+
metadata: true;
|
|
22
|
+
}, options?: RPCOptions): Promise<TranscribeSegment[]> & {
|
|
23
|
+
requestId: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function bciTranscribe(params: BciTranscribeClientParams, options?: RPCOptions): Promise<string> & {
|
|
26
|
+
requestId: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Opens a bidirectional streaming BCI transcription session. Neural-signal
|
|
30
|
+
* chunks are streamed in via `write()`, and transcription is yielded as the
|
|
31
|
+
* addon's sliding window decodes successive windows.
|
|
32
|
+
*
|
|
33
|
+
* The returned session is single-use. Attempting to iterate a second time
|
|
34
|
+
* will throw a `TranscriptionFailedError`.
|
|
35
|
+
*
|
|
36
|
+
* @param params - Streaming BCI transcription parameters.
|
|
37
|
+
* @param params.modelId - The loaded BCI model to use.
|
|
38
|
+
* @param params.metadata - When true, the session yields transcript segment
|
|
39
|
+
* objects (`{ text, startMs, endMs, append, id }`)
|
|
40
|
+
* instead of plain text.
|
|
41
|
+
* @param params.windowTimesteps - Decode window size in timesteps.
|
|
42
|
+
* @param params.hopTimesteps - How far the window advances between decodes.
|
|
43
|
+
* @param params.emit - `'delta'` (default) yields only the newly-discovered
|
|
44
|
+
* tail per window; `'full'` yields the full running
|
|
45
|
+
* transcript each update.
|
|
46
|
+
* @param options - Optional RPC options including per-call profiling.
|
|
47
|
+
* @returns A session object: call `write(neuralChunk)` with a `Uint8Array`
|
|
48
|
+
* to feed neural-signal bytes, iterate with `for await (...)` to
|
|
49
|
+
* receive transcription, and `end()` to signal end of input. The
|
|
50
|
+
* session exposes `requestId` synchronously for targeted
|
|
51
|
+
* `cancel({ requestId })`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function bciTranscribeStream(params: BciTranscribeStreamClientParams & {
|
|
54
|
+
metadata: true;
|
|
55
|
+
}, options?: RPCOptions): Promise<BciTranscribeStreamMetadataSession>;
|
|
56
|
+
export declare function bciTranscribeStream(params: BciTranscribeStreamClientParams, options?: RPCOptions): Promise<BciTranscribeStreamSession>;
|
|
57
|
+
//# sourceMappingURL=bci-transcribe.d.ts.map
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { bciTranscribeResponseSchema, bciTranscribeClientParamsSchema, bciTranscribeStreamResponseSchema, } from "../../schemas/index.js";
|
|
2
|
+
import { stream, duplex } from "../rpc/rpc-client.js";
|
|
3
|
+
import { getClientLogger } from "../../logging/index.js";
|
|
4
|
+
import { TranscriptionFailedError } from "../../utils/errors-client.js";
|
|
5
|
+
import { decoratePromise } from "../../utils/decorate-promise.js";
|
|
6
|
+
import { generateClientRequestId } from "./client-request-id.js";
|
|
7
|
+
const logger = getClientLogger();
|
|
8
|
+
function buildBciTranscribeRequest(params, requestId) {
|
|
9
|
+
return {
|
|
10
|
+
type: "bciTranscribe",
|
|
11
|
+
modelId: params.modelId,
|
|
12
|
+
neuralData: typeof params.neuralData === "string"
|
|
13
|
+
? { type: "filePath", value: params.neuralData }
|
|
14
|
+
: {
|
|
15
|
+
type: "base64",
|
|
16
|
+
value: Buffer.from(params.neuralData).toString("base64"),
|
|
17
|
+
},
|
|
18
|
+
...(params.metadata === true && { metadata: true }),
|
|
19
|
+
requestId,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function bciTranscribe(params, options) {
|
|
23
|
+
const parsed = bciTranscribeClientParamsSchema.parse(params);
|
|
24
|
+
const requestId = generateClientRequestId();
|
|
25
|
+
const inner = runBciTranscribe(parsed, requestId, options);
|
|
26
|
+
return decoratePromise(inner, { requestId });
|
|
27
|
+
}
|
|
28
|
+
async function runBciTranscribe(params, requestId, options) {
|
|
29
|
+
const request = buildBciTranscribeRequest(params, requestId);
|
|
30
|
+
if (params.metadata === true) {
|
|
31
|
+
const segments = [];
|
|
32
|
+
for await (const response of stream(request, options)) {
|
|
33
|
+
if (response.type === "bciTranscribe") {
|
|
34
|
+
const parsed = bciTranscribeResponseSchema.parse(response);
|
|
35
|
+
if (parsed.segment) {
|
|
36
|
+
segments.push(parsed.segment);
|
|
37
|
+
}
|
|
38
|
+
if (parsed.done) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return segments;
|
|
44
|
+
}
|
|
45
|
+
let fullText = "";
|
|
46
|
+
for await (const response of stream(request, options)) {
|
|
47
|
+
if (response.type === "bciTranscribe") {
|
|
48
|
+
const parsed = bciTranscribeResponseSchema.parse(response);
|
|
49
|
+
if (parsed.text) {
|
|
50
|
+
fullText += parsed.text;
|
|
51
|
+
}
|
|
52
|
+
if (parsed.done) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return fullText;
|
|
58
|
+
}
|
|
59
|
+
function buildBciTranscribeStreamRequest(params, requestId) {
|
|
60
|
+
const streamOpts = {
|
|
61
|
+
...(params.windowTimesteps !== undefined && {
|
|
62
|
+
windowTimesteps: params.windowTimesteps,
|
|
63
|
+
}),
|
|
64
|
+
...(params.hopTimesteps !== undefined && {
|
|
65
|
+
hopTimesteps: params.hopTimesteps,
|
|
66
|
+
}),
|
|
67
|
+
...(params.emit !== undefined && { emit: params.emit }),
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
type: "bciTranscribeStream",
|
|
71
|
+
modelId: params.modelId,
|
|
72
|
+
...(params.metadata === true && { metadata: true }),
|
|
73
|
+
...(Object.keys(streamOpts).length > 0 && { streamOpts }),
|
|
74
|
+
requestId,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function bciTranscribeStream(params, options) {
|
|
78
|
+
if (params.metadata === true) {
|
|
79
|
+
return createBciStreamSession(params, options, processLineMetadata, "BciTranscribeStreamMetadataSession");
|
|
80
|
+
}
|
|
81
|
+
return createBciStreamSession(params, options, processLine, "BciTranscribeStreamSession");
|
|
82
|
+
}
|
|
83
|
+
async function createBciStreamSession(params, options, process, sessionName) {
|
|
84
|
+
const requestId = generateClientRequestId();
|
|
85
|
+
const request = buildBciTranscribeStreamRequest(params, requestId);
|
|
86
|
+
const { requestStream, responseStream } = await duplex(request, options);
|
|
87
|
+
const responses = parseLines(responseStream, process);
|
|
88
|
+
let consumed = false;
|
|
89
|
+
return {
|
|
90
|
+
requestId,
|
|
91
|
+
write(neuralChunk) {
|
|
92
|
+
requestStream.write(neuralChunk);
|
|
93
|
+
},
|
|
94
|
+
end() {
|
|
95
|
+
requestStream.end();
|
|
96
|
+
},
|
|
97
|
+
destroy() {
|
|
98
|
+
requestStream.destroy();
|
|
99
|
+
responseStream.destroy();
|
|
100
|
+
},
|
|
101
|
+
[Symbol.asyncIterator]() {
|
|
102
|
+
if (consumed) {
|
|
103
|
+
throw new TranscriptionFailedError(`${sessionName} can only be iterated once`);
|
|
104
|
+
}
|
|
105
|
+
consumed = true;
|
|
106
|
+
return responses;
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Line-delimited parser: reads newline-separated frames from a duplex
|
|
112
|
+
* response stream, passes each non-empty line through `process`, and yields
|
|
113
|
+
* whatever values it returns. `null` from `process` terminates the stream.
|
|
114
|
+
*/
|
|
115
|
+
async function* parseLines(responseStream, process) {
|
|
116
|
+
let buf = "";
|
|
117
|
+
for await (const chunk of responseStream) {
|
|
118
|
+
buf += chunk.toString();
|
|
119
|
+
const lines = buf.split("\n");
|
|
120
|
+
buf = lines.pop() || "";
|
|
121
|
+
for (const line of lines) {
|
|
122
|
+
const result = process(line);
|
|
123
|
+
if (result === null)
|
|
124
|
+
return;
|
|
125
|
+
if (result !== undefined)
|
|
126
|
+
yield result;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (buf.trim()) {
|
|
130
|
+
const result = process(buf);
|
|
131
|
+
if (result !== null && result !== undefined)
|
|
132
|
+
yield result;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function parseResponseLine(line) {
|
|
136
|
+
if (!line.trim())
|
|
137
|
+
return null;
|
|
138
|
+
let parsed;
|
|
139
|
+
try {
|
|
140
|
+
parsed = JSON.parse(line);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
logger.warn("bciTranscribeStream: malformed JSON from server:", line);
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const obj = parsed;
|
|
147
|
+
if (obj["type"] === "error") {
|
|
148
|
+
throw new TranscriptionFailedError(obj["message"] ?? "Unknown server error");
|
|
149
|
+
}
|
|
150
|
+
return bciTranscribeStreamResponseSchema.parse(parsed);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Shared wire-frame decoder. Returns `null` for the terminal `done` frame,
|
|
154
|
+
* `undefined` for frames the caller should skip, or the value extracted from
|
|
155
|
+
* the frame.
|
|
156
|
+
*/
|
|
157
|
+
function processWith(line, extract) {
|
|
158
|
+
const response = parseResponseLine(line);
|
|
159
|
+
if (response === null)
|
|
160
|
+
return undefined;
|
|
161
|
+
if (response.error)
|
|
162
|
+
throw new TranscriptionFailedError(response.error);
|
|
163
|
+
if (response.done)
|
|
164
|
+
return null;
|
|
165
|
+
return extract(response);
|
|
166
|
+
}
|
|
167
|
+
function processLine(line) {
|
|
168
|
+
return processWith(line, (response) => response.text?.trim() ? response.text : undefined);
|
|
169
|
+
}
|
|
170
|
+
function processLineMetadata(line) {
|
|
171
|
+
return processWith(line, (response) => response.segment);
|
|
172
|
+
}
|
|
@@ -12,7 +12,7 @@ export declare function getModelInfo(params: GetModelInfoParams): Promise<{
|
|
|
12
12
|
modelId: string;
|
|
13
13
|
expectedSize: number;
|
|
14
14
|
sha256Checksum: string;
|
|
15
|
-
addon: "embeddings" | "diffusion" | "tts" | "ocr" | "vla" | "llm" | "whisper" | "nmt" | "parakeet" | "classification" | "vad" | "other";
|
|
15
|
+
addon: "embeddings" | "diffusion" | "tts" | "ocr" | "vla" | "llm" | "whisper" | "bci" | "nmt" | "parakeet" | "classification" | "vad" | "other";
|
|
16
16
|
isCached: boolean;
|
|
17
17
|
isLoaded: boolean;
|
|
18
18
|
cacheFiles: {
|
|
@@ -6,6 +6,7 @@ export { unloadModel } from "./unload-model";
|
|
|
6
6
|
export { loggingStream } from "./logging-stream";
|
|
7
7
|
export { heartbeat } from "./heartbeat";
|
|
8
8
|
export { transcribe, transcribeStream } from "./transcribe";
|
|
9
|
+
export { bciTranscribe, bciTranscribeStream } from "./bci-transcribe";
|
|
9
10
|
export { embed } from "./embed";
|
|
10
11
|
export { finetune, type FinetuneHandle } from "./finetune";
|
|
11
12
|
export { translate } from "./translate";
|
package/dist/client/api/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { unloadModel } from "./unload-model.js";
|
|
|
6
6
|
export { loggingStream } from "./logging-stream.js";
|
|
7
7
|
export { heartbeat } from "./heartbeat.js";
|
|
8
8
|
export { transcribe, transcribeStream } from "./transcribe.js";
|
|
9
|
+
export { bciTranscribe, bciTranscribeStream } from "./bci-transcribe.js";
|
|
9
10
|
export { embed } from "./embed.js";
|
|
10
11
|
export { finetune } from "./finetune.js";
|
|
11
12
|
export { translate } from "./translate.js";
|
|
@@ -16,11 +16,13 @@ export interface VideoResult {
|
|
|
16
16
|
* @param params - Video request parameters (model, prompt, dimensions, frame count, fps, sampler, seed, etc.).
|
|
17
17
|
* @returns A result object exposing `requestId` (stable identifier for this in-flight generation), `progressStream` (async iterator of `{ step, totalSteps, elapsedMs }`), `outputs` (promise of the generated video buffers, typically a single AVI file), and `stats` (promise of generation statistics).
|
|
18
18
|
*
|
|
19
|
-
*
|
|
19
|
+
* Supports `txt2vid` (text-to-video) and `img2vid` (image-to-video). For `img2vid`,
|
|
20
|
+
* load the Wan pipeline with `modelConfig.clipVisionModelSrc` set to
|
|
21
|
+
* `clip_vision_h.safetensors`. On React Native, prefer a `modelId` loaded with a
|
|
22
|
+
* `delegate` since the bundled video diffusion models are too large for typical mobile devices.
|
|
20
23
|
*
|
|
21
|
-
* @example
|
|
24
|
+
* @example Basic txt2vid generation
|
|
22
25
|
* ```typescript
|
|
23
|
-
* // Basic txt2vid generation
|
|
24
26
|
* const { outputs, stats } = video({
|
|
25
27
|
* modelId,
|
|
26
28
|
* mode: "txt2vid",
|
|
@@ -32,8 +34,10 @@ export interface VideoResult {
|
|
|
32
34
|
* });
|
|
33
35
|
* const buffers = await outputs;
|
|
34
36
|
* fs.writeFileSync("output.avi", buffers[0]);
|
|
37
|
+
* ```
|
|
35
38
|
*
|
|
36
|
-
*
|
|
39
|
+
* @example With progress tracking
|
|
40
|
+
* ```typescript
|
|
37
41
|
* const { progressStream, outputs } = video({
|
|
38
42
|
* modelId,
|
|
39
43
|
* mode: "txt2vid",
|
|
@@ -43,8 +47,22 @@ export interface VideoResult {
|
|
|
43
47
|
* console.log(`${step}/${totalSteps}`);
|
|
44
48
|
* }
|
|
45
49
|
* const buffers = await outputs;
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example Image-to-video (first frame + motion prompt)
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const firstFrame = fs.readFileSync("portrait.png");
|
|
55
|
+
* const { outputs } = video({
|
|
56
|
+
* modelId,
|
|
57
|
+
* mode: "img2vid",
|
|
58
|
+
* prompt: "the subject slowly turns and smiles, cinematic lighting",
|
|
59
|
+
* init_image: firstFrame,
|
|
60
|
+
* strength: 0.85,
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
46
63
|
*
|
|
47
|
-
*
|
|
64
|
+
* @example With control frames (e.g. for guided generation)
|
|
65
|
+
* ```typescript
|
|
48
66
|
* const frameA = fs.readFileSync("frame-a.png");
|
|
49
67
|
* const frameB = fs.readFileSync("frame-b.png");
|
|
50
68
|
* const { outputs } = video({
|
|
@@ -53,8 +71,10 @@ export interface VideoResult {
|
|
|
53
71
|
* prompt: "smooth transition between scenes",
|
|
54
72
|
* control_frames: [frameA, frameB],
|
|
55
73
|
* });
|
|
74
|
+
* ```
|
|
56
75
|
*
|
|
57
|
-
*
|
|
76
|
+
* @example Cancellation via requestId
|
|
77
|
+
* ```typescript
|
|
58
78
|
* const { requestId, outputs } = video({ modelId, mode: "txt2vid", prompt: "..." });
|
|
59
79
|
* // ...later
|
|
60
80
|
* await cancel(requestId);
|
package/dist/client/api/video.js
CHANGED
|
@@ -8,11 +8,13 @@ import { decodeBase64, encodeBase64 } from "../../utils/encoding.js";
|
|
|
8
8
|
* @param params - Video request parameters (model, prompt, dimensions, frame count, fps, sampler, seed, etc.).
|
|
9
9
|
* @returns A result object exposing `requestId` (stable identifier for this in-flight generation), `progressStream` (async iterator of `{ step, totalSteps, elapsedMs }`), `outputs` (promise of the generated video buffers, typically a single AVI file), and `stats` (promise of generation statistics).
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Supports `txt2vid` (text-to-video) and `img2vid` (image-to-video). For `img2vid`,
|
|
12
|
+
* load the Wan pipeline with `modelConfig.clipVisionModelSrc` set to
|
|
13
|
+
* `clip_vision_h.safetensors`. On React Native, prefer a `modelId` loaded with a
|
|
14
|
+
* `delegate` since the bundled video diffusion models are too large for typical mobile devices.
|
|
12
15
|
*
|
|
13
|
-
* @example
|
|
16
|
+
* @example Basic txt2vid generation
|
|
14
17
|
* ```typescript
|
|
15
|
-
* // Basic txt2vid generation
|
|
16
18
|
* const { outputs, stats } = video({
|
|
17
19
|
* modelId,
|
|
18
20
|
* mode: "txt2vid",
|
|
@@ -24,8 +26,10 @@ import { decodeBase64, encodeBase64 } from "../../utils/encoding.js";
|
|
|
24
26
|
* });
|
|
25
27
|
* const buffers = await outputs;
|
|
26
28
|
* fs.writeFileSync("output.avi", buffers[0]);
|
|
29
|
+
* ```
|
|
27
30
|
*
|
|
28
|
-
*
|
|
31
|
+
* @example With progress tracking
|
|
32
|
+
* ```typescript
|
|
29
33
|
* const { progressStream, outputs } = video({
|
|
30
34
|
* modelId,
|
|
31
35
|
* mode: "txt2vid",
|
|
@@ -35,8 +39,22 @@ import { decodeBase64, encodeBase64 } from "../../utils/encoding.js";
|
|
|
35
39
|
* console.log(`${step}/${totalSteps}`);
|
|
36
40
|
* }
|
|
37
41
|
* const buffers = await outputs;
|
|
42
|
+
* ```
|
|
38
43
|
*
|
|
39
|
-
*
|
|
44
|
+
* @example Image-to-video (first frame + motion prompt)
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const firstFrame = fs.readFileSync("portrait.png");
|
|
47
|
+
* const { outputs } = video({
|
|
48
|
+
* modelId,
|
|
49
|
+
* mode: "img2vid",
|
|
50
|
+
* prompt: "the subject slowly turns and smiles, cinematic lighting",
|
|
51
|
+
* init_image: firstFrame,
|
|
52
|
+
* strength: 0.85,
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example With control frames (e.g. for guided generation)
|
|
57
|
+
* ```typescript
|
|
40
58
|
* const frameA = fs.readFileSync("frame-a.png");
|
|
41
59
|
* const frameB = fs.readFileSync("frame-b.png");
|
|
42
60
|
* const { outputs } = video({
|
|
@@ -45,21 +63,26 @@ import { decodeBase64, encodeBase64 } from "../../utils/encoding.js";
|
|
|
45
63
|
* prompt: "smooth transition between scenes",
|
|
46
64
|
* control_frames: [frameA, frameB],
|
|
47
65
|
* });
|
|
66
|
+
* ```
|
|
48
67
|
*
|
|
49
|
-
*
|
|
68
|
+
* @example Cancellation via requestId
|
|
69
|
+
* ```typescript
|
|
50
70
|
* const { requestId, outputs } = video({ modelId, mode: "txt2vid", prompt: "..." });
|
|
51
71
|
* // ...later
|
|
52
72
|
* await cancel(requestId);
|
|
53
73
|
* ```
|
|
54
74
|
*/
|
|
55
75
|
export function video(params) {
|
|
56
|
-
const { control_frames, ...rest } = params;
|
|
57
76
|
const requestId = generateClientRequestId();
|
|
77
|
+
const { control_frames, init_image, ...rest } = params;
|
|
58
78
|
const request = {
|
|
59
79
|
...rest,
|
|
60
80
|
...(control_frames !== undefined && {
|
|
61
81
|
control_frames: control_frames.map(encodeBase64),
|
|
62
82
|
}),
|
|
83
|
+
...(init_image !== undefined && {
|
|
84
|
+
init_image: encodeBase64(init_image),
|
|
85
|
+
}),
|
|
63
86
|
type: "videoStream",
|
|
64
87
|
requestId,
|
|
65
88
|
};
|
package/dist/client/api/vla.d.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
import { type VlaClientRunParams, type VlaClientRunResult, type VlaHparams } from "../../schemas/index";
|
|
2
2
|
/**
|
|
3
|
-
* Run
|
|
4
|
-
* chunk plus per-stage timings.
|
|
3
|
+
* Run VLA inference on a loaded model (SmolVLA or π₀.₅) and return the
|
|
4
|
+
* produced action chunk plus per-stage timings.
|
|
5
5
|
*
|
|
6
6
|
* @param params - Inference inputs.
|
|
7
7
|
* @param params.modelId - Identifier of the loaded VLA model (returned by
|
|
8
8
|
* `loadModel({ modelType: "vla", ... })`).
|
|
9
|
-
* @param params.images -
|
|
9
|
+
* @param params.images - The preprocessed camera frames; each is a
|
|
10
10
|
* `Float32Array` of length `3 * imgWidth * imgHeight` in CHW layout, range
|
|
11
|
-
* `[-1, 1]`.
|
|
11
|
+
* `[-1, 1]`. Pass exactly `hparams.numCameras` frames (2 for SmolVLA, 3
|
|
12
|
+
* for π₀.₅). Use the addon's `preprocessImage()` (re-exported as
|
|
12
13
|
* `vlaPreprocessImage`) to produce them.
|
|
13
14
|
* @param params.imgWidth - Width of each preprocessed image; must equal
|
|
14
15
|
* `hparams.visionImageSize`.
|
|
15
16
|
* @param params.imgHeight - Height of each preprocessed image; must equal
|
|
16
17
|
* `hparams.visionImageSize`.
|
|
17
|
-
* @param params.state - Robot end-effector / gripper state
|
|
18
|
-
* `hparams.maxStateDim`
|
|
18
|
+
* @param params.state - Robot end-effector / gripper state. For
|
|
19
|
+
* continuous-state models (SmolVLA) pad to `hparams.maxStateDim` with
|
|
20
|
+
* `vlaPadState`. For discrete-state models (π₀.₅,
|
|
21
|
+
* `hparams.stateInputMode === 'discrete'`) the state is tokenised into the
|
|
22
|
+
* prompt and this buffer is ignored — pass an empty `Float32Array(0)`.
|
|
19
23
|
* @param params.tokens - Tokenized instruction (`Int32Array` of length
|
|
20
24
|
* `hparams.tokenizerMaxLength`). Tokenize on the consumer side with the
|
|
21
|
-
* SmolVLM2
|
|
25
|
+
* model's tokenizer (SmolVLM2 for SmolVLA, PaliGemma/Gemma for π₀.₅).
|
|
22
26
|
* @param params.mask - Token attention mask (`Uint8Array` matching `tokens`).
|
|
23
27
|
* @param params.noise - Optional seeded noise prior
|
|
24
28
|
* (`Float32Array` of length `hparams.chunkSize * hparams.maxActionDim`).
|
package/dist/client/api/vla.js
CHANGED
|
@@ -13,25 +13,29 @@ function f32FromBase64(b64) {
|
|
|
13
13
|
return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* Run
|
|
17
|
-
* chunk plus per-stage timings.
|
|
16
|
+
* Run VLA inference on a loaded model (SmolVLA or π₀.₅) and return the
|
|
17
|
+
* produced action chunk plus per-stage timings.
|
|
18
18
|
*
|
|
19
19
|
* @param params - Inference inputs.
|
|
20
20
|
* @param params.modelId - Identifier of the loaded VLA model (returned by
|
|
21
21
|
* `loadModel({ modelType: "vla", ... })`).
|
|
22
|
-
* @param params.images -
|
|
22
|
+
* @param params.images - The preprocessed camera frames; each is a
|
|
23
23
|
* `Float32Array` of length `3 * imgWidth * imgHeight` in CHW layout, range
|
|
24
|
-
* `[-1, 1]`.
|
|
24
|
+
* `[-1, 1]`. Pass exactly `hparams.numCameras` frames (2 for SmolVLA, 3
|
|
25
|
+
* for π₀.₅). Use the addon's `preprocessImage()` (re-exported as
|
|
25
26
|
* `vlaPreprocessImage`) to produce them.
|
|
26
27
|
* @param params.imgWidth - Width of each preprocessed image; must equal
|
|
27
28
|
* `hparams.visionImageSize`.
|
|
28
29
|
* @param params.imgHeight - Height of each preprocessed image; must equal
|
|
29
30
|
* `hparams.visionImageSize`.
|
|
30
|
-
* @param params.state - Robot end-effector / gripper state
|
|
31
|
-
* `hparams.maxStateDim`
|
|
31
|
+
* @param params.state - Robot end-effector / gripper state. For
|
|
32
|
+
* continuous-state models (SmolVLA) pad to `hparams.maxStateDim` with
|
|
33
|
+
* `vlaPadState`. For discrete-state models (π₀.₅,
|
|
34
|
+
* `hparams.stateInputMode === 'discrete'`) the state is tokenised into the
|
|
35
|
+
* prompt and this buffer is ignored — pass an empty `Float32Array(0)`.
|
|
32
36
|
* @param params.tokens - Tokenized instruction (`Int32Array` of length
|
|
33
37
|
* `hparams.tokenizerMaxLength`). Tokenize on the consumer side with the
|
|
34
|
-
* SmolVLM2
|
|
38
|
+
* model's tokenizer (SmolVLM2 for SmolVLA, PaliGemma/Gemma for π₀.₅).
|
|
35
39
|
* @param params.mask - Token attention mask (`Uint8Array` matching `tokens`).
|
|
36
40
|
* @param params.noise - Optional seeded noise prior
|
|
37
41
|
* (`Float32Array` of length `hparams.chunkSize * hparams.maxActionDim`).
|
|
@@ -9,7 +9,7 @@ import { setSDKConfig } from "../../server/bare/registry/config-registry.js";
|
|
|
9
9
|
import { setRuntimeContext } from "../../server/bare/registry/runtime-context-registry.js";
|
|
10
10
|
import { resolveModelConfig } from "../../server/bare/registry/model-config-registry.js";
|
|
11
11
|
import { resolveConfig } from "../config-loader/resolve-config.bare.js";
|
|
12
|
-
import { initializeWorkerCore,
|
|
12
|
+
import { initializeWorkerCore, cleanupForTerminate, } from "../../server/worker-core.js";
|
|
13
13
|
import { assertLifecycleAllowed } from "../../server/bare/runtime-lifecycle.js";
|
|
14
14
|
import { ensurePluginsRegistered } from "./ensure-worker-ready.js";
|
|
15
15
|
async function ensureWorkerReady() {
|
|
@@ -216,7 +216,7 @@ export async function getRPC() {
|
|
|
216
216
|
return mockRPC;
|
|
217
217
|
}
|
|
218
218
|
export async function close() {
|
|
219
|
-
await
|
|
219
|
+
await cleanupForTerminate();
|
|
220
220
|
}
|
|
221
221
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
222
222
|
export async function createDuplexSession(payload, _commandId) {
|
|
@@ -2,11 +2,13 @@ export const BUILTIN_PLUGINS = {
|
|
|
2
2
|
"llamacpp-completion": { exportName: "llmPlugin" },
|
|
3
3
|
"llamacpp-embedding": { exportName: "embeddingsPlugin" },
|
|
4
4
|
"whispercpp-transcription": { exportName: "whisperPlugin" },
|
|
5
|
+
"bci-whispercpp-transcription": { exportName: "bciPlugin" },
|
|
5
6
|
"parakeet-transcription": { exportName: "parakeetPlugin" },
|
|
6
7
|
"nmtcpp-translation": { exportName: "nmtPlugin" },
|
|
7
8
|
"tts-ggml": { exportName: "ttsPlugin" },
|
|
8
9
|
"onnx-ocr": { exportName: "ocrPlugin" },
|
|
9
10
|
"sdcpp-generation": { exportName: "diffusionPlugin" },
|
|
11
|
+
"ggml-vla": { exportName: "vlaPlugin" },
|
|
10
12
|
};
|
|
11
13
|
export const BUILTIN_SUFFIXES = Object.keys(BUILTIN_PLUGINS);
|
|
12
14
|
export const DEFAULT_HOSTS = [
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming BCI transcription from a neural-signal file.
|
|
3
|
+
*
|
|
4
|
+
* Reads a raw neural-signal `.bin` file and feeds it to the BCI
|
|
5
|
+
* (whisper.cpp) addon chunk-by-chunk through a duplex `bciTranscribeStream`
|
|
6
|
+
* session, printing transcript text as the sliding window decodes
|
|
7
|
+
* successive windows.
|
|
8
|
+
*
|
|
9
|
+
* Usage: bun run examples/bci/bci-filesystem-streaming.ts <neural-bin-file-path>
|
|
10
|
+
*/
|
|
11
|
+
import { loadModel, unloadModel, bciTranscribeStream, BCI_WINDOWED, } from "@qvac/sdk";
|
|
12
|
+
import { readFileSync } from "fs";
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
if (!args[0]) {
|
|
15
|
+
console.error("Usage: bun run examples/bci/bci-filesystem-streaming.ts <neural-bin-file-path>");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const neuralFilePath = args[0];
|
|
19
|
+
// Feed the neural buffer in fixed-size chunks to simulate a live stream.
|
|
20
|
+
const CHUNK_SIZE = 64 * 1024;
|
|
21
|
+
try {
|
|
22
|
+
console.log("=== BCI transcribeStream file test ===");
|
|
23
|
+
console.log(`File: ${neuralFilePath}`);
|
|
24
|
+
console.log(`Chunk size: ${CHUNK_SIZE} bytes\n`);
|
|
25
|
+
console.log("Loading model...");
|
|
26
|
+
const modelId = await loadModel({
|
|
27
|
+
modelSrc: BCI_WINDOWED,
|
|
28
|
+
modelConfig: {
|
|
29
|
+
whisperConfig: {
|
|
30
|
+
language: "en",
|
|
31
|
+
n_threads: 4,
|
|
32
|
+
temperature: 0.0,
|
|
33
|
+
},
|
|
34
|
+
// Session day index selects the day-specific projection matrices.
|
|
35
|
+
// Set this to match the recording session your neural file came from.
|
|
36
|
+
bciConfig: {
|
|
37
|
+
day_idx: 1,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
console.log(`Model loaded: ${modelId}\n`);
|
|
42
|
+
console.log("Opening live session...");
|
|
43
|
+
const session = await bciTranscribeStream({ modelId, emit: "delta" });
|
|
44
|
+
console.log("Session open. Streaming neural signal...\n");
|
|
45
|
+
// Drain the session concurrently with writing so the sliding-window
|
|
46
|
+
// decode can make progress as chunks arrive instead of stalling.
|
|
47
|
+
const consume = (async () => {
|
|
48
|
+
let transcript = "";
|
|
49
|
+
for await (const text of session) {
|
|
50
|
+
transcript += text;
|
|
51
|
+
process.stdout.write(text);
|
|
52
|
+
}
|
|
53
|
+
return transcript;
|
|
54
|
+
})();
|
|
55
|
+
const data = readFileSync(neuralFilePath);
|
|
56
|
+
let totalBytes = 0;
|
|
57
|
+
for (let offset = 0; offset < data.length; offset += CHUNK_SIZE) {
|
|
58
|
+
const chunk = data.subarray(offset, offset + CHUNK_SIZE);
|
|
59
|
+
session.write(chunk);
|
|
60
|
+
totalBytes += chunk.length;
|
|
61
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
62
|
+
}
|
|
63
|
+
console.log(`\n\nNeural signal streamed: ${totalBytes} bytes`);
|
|
64
|
+
console.log("Waiting for transcription to finish...\n");
|
|
65
|
+
session.end();
|
|
66
|
+
const transcript = await consume;
|
|
67
|
+
console.log("\n=== Results ===");
|
|
68
|
+
console.log(`Transcript: ${transcript.trim() || "(no text received)"}`);
|
|
69
|
+
console.log("\nUnloading model...");
|
|
70
|
+
await unloadModel({ modelId });
|
|
71
|
+
console.log("Done.");
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error("❌ Error:", error);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|