UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

88 lines (87 loc) • 4.66 kB
/** * Whistle — the built-in, zero-config, on-device speech-to-text engine. * * Runs Cactus's Whistle model (17 MB) on the Cactus "needle" engine, an * Emscripten WebAssembly build, inside a `worker_threads` Worker. No API key, * no network after the first use, no native addon: it is the STT provider * NeuroLink falls back to when no other one is configured. * * **Files.** `needle.js`, `needle.wasm` and `whistle.cact` (~17.9 MB in all) * are downloaded from pinned Hugging Face revisions on first use, checked * against SHA-256 and kept in the model directory: * `credentials.stt.whistle.modelDir` → `NEUROLINK_WHISTLE_DIR` → * `$NEUROLINK_MODEL_DIR/whistle` → `~/.neurolink/models/whistle`. Set * `NEUROLINK_WHISTLE_AUTO_DOWNLOAD=0` (or `autoDownload: false`) to forbid the * download; a missing file then fails with an error naming each file, where it * goes and where to get it. * * **Languages.** English, German, French, Spanish, Italian, Dutch and Polish * (`en de fr es it nl pl`). `options.language` accepts a code or a locale * (`"de-DE"`); `"auto"`, omitted or any other language lets the engine detect * one of the seven. * * **Limits.** The engine reads 30 s per call; longer audio is cut at the * quietest 20 ms frame near every 24 s mark and the word times are offset * back onto the whole recording. One hour per request. Roughly 0.5 s of CPU * per 10 s of audio on a laptop core; one warm worker per handler instance, * requests served in order. * * **Keyword biasing.** `options.vocabulary` is passed to the engine as * keywords only when `options.model` is `"whistle-keywords"` or * `NEUROLINK_WHISTLE_KEYWORDS=1` — the engine can loop on long keyword lists, * so it is opt-in and capped at {@link MAX_KEYWORDS} entries. * * **Formats.** WAV (any PCM/float layout, rate and channel count) and raw * `pcm16` are decoded in-process; mp3, m4a, ogg/opus, webm, flac and mp4 need * ffmpeg (`ffmpeg-static`, `FFMPEG_PATH` or `ffmpeg` on PATH). * * **Streaming.** Native: `transcribeStream()` feeds the engine's own streaming * decoder about a second at a time, on a dedicated worker per stream. Each * step yields a final segment for the words two passes agreed on, and an * interim segment for the unconfirmed tail. Input frames are PCM16LE mono at * `options.sampleRate` (default 16 kHz). * * **Accuracy.** A 17 MB model: good on clear speech in its seven languages, * noticeably behind cloud engines and large Whisper-class models on accents, * noise, crosstalk, names and jargon. Use a dictionary/correction pass, or a * cloud provider, where that matters. * * @module voice/providers/WhistleSTT */ import type { STTCredentials, STTHandler, STTLanguage, STTOptions, STTResult, TranscriptionSegment, TTSAudioFormat } from "../../types/index.js"; export declare class WhistleSTT implements STTHandler { /** One hour per request (16 kHz float samples: ~230 MB in memory). */ readonly maxAudioDuration: number; /** The engine has its own streaming decoder. */ readonly supportsStreaming = true; private readonly modelDir; private readonly autoDownload; private engine; constructor(slice?: STTCredentials["whistle"]); /** * True when the engine can run: its files are already in the model dir, or * it is allowed to fetch them. With `NEUROLINK_WHISTLE_AUTO_DOWNLOAD=0` (or * `autoDownload: false`) and no files, it reports unconfigured, so a caller * asserting "no STT backend" is not answered by a 17 MB download. */ isConfigured(): boolean; describeConfiguration(): string; /** Directory the engine's files live in. */ getModelDir(): string; getSupportedFormats(): TTSAudioFormat[]; getSupportedLanguages(): Promise<STTLanguage[]>; /** Download/verify the files and start the worker now rather than on the first request. */ warmUp(): Promise<void>; /** Stop the warm worker. The handler stays usable; the next call starts a new one. */ dispose(): Promise<void>; transcribe(audio: Buffer | ArrayBuffer, options?: STTOptions): Promise<STTResult>; /** * Native streaming. `audioStream` yields PCM16LE mono frames at * `options.sampleRate` (default 16 kHz), any frame size. Yields a final * segment for each run of words the engine commits (times from the start of * the stream; join the finals with a space for the transcript) and an * interim segment carrying the unconfirmed tail whenever it changes. */ transcribeStream(audioStream: AsyncIterable<Buffer>, options?: STTOptions): AsyncIterable<TranscriptionSegment>; private getEngine; }