@moxxy/plugin-stt-local 0.28.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/LICENSE +21 -0
- package/dist/audio.d.ts +55 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +197 -0
- package/dist/audio.js.map +1 -0
- package/dist/decode.d.ts +25 -0
- package/dist/decode.d.ts.map +1 -0
- package/dist/decode.js +50 -0
- package/dist/decode.js.map +1 -0
- package/dist/ffmpeg.d.ts +26 -0
- package/dist/ffmpeg.d.ts.map +1 -0
- package/dist/ffmpeg.js +207 -0
- package/dist/ffmpeg.js.map +1 -0
- package/dist/host-client.d.ts +70 -0
- package/dist/host-client.d.ts.map +1 -0
- package/dist/host-client.js +156 -0
- package/dist/host-client.js.map +1 -0
- package/dist/host-protocol.d.ts +96 -0
- package/dist/host-protocol.d.ts.map +1 -0
- package/dist/host-protocol.js +70 -0
- package/dist/host-protocol.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/local-stt.d.ts +86 -0
- package/dist/local-stt.d.ts.map +1 -0
- package/dist/local-stt.js +201 -0
- package/dist/local-stt.js.map +1 -0
- package/dist/models.d.ts +47 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +61 -0
- package/dist/models.js.map +1 -0
- package/dist/platform.d.ts +37 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +96 -0
- package/dist/platform.js.map +1 -0
- package/dist/sidecar.d.ts +18 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +86 -0
- package/dist/sidecar.js.map +1 -0
- package/package.json +67 -0
- package/src/audio.test.ts +213 -0
- package/src/audio.ts +220 -0
- package/src/decode.test.ts +126 -0
- package/src/decode.ts +74 -0
- package/src/ffmpeg.test.ts +142 -0
- package/src/ffmpeg.ts +215 -0
- package/src/host-client.test.ts +208 -0
- package/src/host-client.ts +200 -0
- package/src/host-protocol.test.ts +171 -0
- package/src/host-protocol.ts +152 -0
- package/src/index.ts +108 -0
- package/src/live.test.ts +80 -0
- package/src/local-stt.test.ts +224 -0
- package/src/local-stt.ts +273 -0
- package/src/models.test.ts +58 -0
- package/src/models.ts +111 -0
- package/src/platform.test.ts +77 -0
- package/src/platform.ts +109 -0
- package/src/sidecar.ts +97 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent-side manager for the sherpa sidecar: lazy-spawn on first transcribe,
|
|
3
|
+
* a correlated request/reply protocol over the fork IPC channel, restart-once
|
|
4
|
+
* on a crash, and a clean shutdown. The `fork` itself is injectable (a
|
|
5
|
+
* {@link ForkLike}) so tests drive the whole protocol against a fake child with
|
|
6
|
+
* no real process.
|
|
7
|
+
*/
|
|
8
|
+
import type { HostRequest, TranscribeResult } from './host-protocol.js';
|
|
9
|
+
/** The minimal child-process surface the client drives — satisfied by a real
|
|
10
|
+
* `ChildProcess` and by a test fake. */
|
|
11
|
+
export interface ChildHandle {
|
|
12
|
+
send(message: unknown): boolean;
|
|
13
|
+
on(event: 'message', listener: (message: unknown) => void): this;
|
|
14
|
+
on(event: 'exit', listener: (code: number | null, signal: NodeJS.Signals | null) => void): this;
|
|
15
|
+
on(event: 'error', listener: (err: Error) => void): this;
|
|
16
|
+
kill(signal?: NodeJS.Signals): boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Fork the sidecar with the given env overrides. Injectable for tests. */
|
|
19
|
+
export type ForkLike = (modulePath: string, env: NodeJS.ProcessEnv) => ChildHandle;
|
|
20
|
+
/** Default fork: advanced serialization (Float32Array round-trip), an IPC
|
|
21
|
+
* channel, and inherited std streams so sherpa's own diagnostics reach the
|
|
22
|
+
* runner log. `env` already carries the platform loader-path var. */
|
|
23
|
+
export declare const defaultFork: ForkLike;
|
|
24
|
+
export interface HostClientOptions {
|
|
25
|
+
/** Absolute path to the built sidecar (`dist/sidecar.js`). */
|
|
26
|
+
readonly hostPath: string;
|
|
27
|
+
/** Env the child is forked with (platform loader path merged over process.env). */
|
|
28
|
+
readonly env: NodeJS.ProcessEnv;
|
|
29
|
+
/** Injected fork (tests). Defaults to {@link defaultFork}. */
|
|
30
|
+
readonly forkImpl?: ForkLike;
|
|
31
|
+
/** Per-request deadline. Default 300s (a long clip on a big model on CPU). */
|
|
32
|
+
readonly requestTimeoutMs?: number;
|
|
33
|
+
/** Optional diagnostic sink. */
|
|
34
|
+
readonly log?: (msg: string) => void;
|
|
35
|
+
}
|
|
36
|
+
/** The slice of a host the transcriber depends on — lets tests swap a fake. */
|
|
37
|
+
export interface HostClientLike {
|
|
38
|
+
transcribe(req: Omit<HostRequest, 'id' | 'type'>): Promise<TranscribeResult>;
|
|
39
|
+
shutdown(): void;
|
|
40
|
+
}
|
|
41
|
+
/** Raised when the sidecar dies with a request in flight; drives the one retry. */
|
|
42
|
+
export declare class HostCrashError extends Error {
|
|
43
|
+
constructor(message: string);
|
|
44
|
+
}
|
|
45
|
+
export declare class HostClient implements HostClientLike {
|
|
46
|
+
private readonly hostPath;
|
|
47
|
+
private readonly env;
|
|
48
|
+
private readonly forkImpl;
|
|
49
|
+
private readonly timeoutMs;
|
|
50
|
+
private readonly log;
|
|
51
|
+
private child;
|
|
52
|
+
private readonly pending;
|
|
53
|
+
private nextId;
|
|
54
|
+
private disposed;
|
|
55
|
+
constructor(opts: HostClientOptions);
|
|
56
|
+
/** Transcribe, restarting the sidecar ONCE if it crashes mid-request. */
|
|
57
|
+
transcribe(req: Omit<HostRequest, 'id' | 'type'>): Promise<TranscribeResult>;
|
|
58
|
+
private send;
|
|
59
|
+
private ensureChild;
|
|
60
|
+
private onMessage;
|
|
61
|
+
private onExit;
|
|
62
|
+
private onError;
|
|
63
|
+
/** Resolve/reject one pending request, clearing its timer and map entry. */
|
|
64
|
+
private settle;
|
|
65
|
+
private rejectAll;
|
|
66
|
+
private killChild;
|
|
67
|
+
/** Kill the sidecar and fail any in-flight requests. Idempotent. */
|
|
68
|
+
shutdown(): void;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=host-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-client.d.ts","sourceRoot":"","sources":["../src/host-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAa,WAAW,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEnF;yCACyC;AACzC,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;IAChC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IACjE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC;IAChG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IACzD,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;CACxC;AAED,2EAA2E;AAC3E,MAAM,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,WAAW,CAAC;AAEnF;;sEAEsE;AACtE,eAAO,MAAM,WAAW,EAAE,QAMI,CAAC;AAE/B,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,gCAAgC;IAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC7E,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,mFAAmF;AACnF,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAUD,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAwB;IAE5C,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,iBAAiB;IAQnC,yEAAyE;IACnE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAYlF,OAAO,CAAC,IAAI;IAuBZ,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,OAAO;IAKf,4EAA4E;IAC5E,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,SAAS;IAYjB,oEAAoE;IACpE,QAAQ,IAAI,IAAI;CAMjB"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent-side manager for the sherpa sidecar: lazy-spawn on first transcribe,
|
|
3
|
+
* a correlated request/reply protocol over the fork IPC channel, restart-once
|
|
4
|
+
* on a crash, and a clean shutdown. The `fork` itself is injectable (a
|
|
5
|
+
* {@link ForkLike}) so tests drive the whole protocol against a fake child with
|
|
6
|
+
* no real process.
|
|
7
|
+
*/
|
|
8
|
+
import { fork } from 'node:child_process';
|
|
9
|
+
/** Default fork: advanced serialization (Float32Array round-trip), an IPC
|
|
10
|
+
* channel, and inherited std streams so sherpa's own diagnostics reach the
|
|
11
|
+
* runner log. `env` already carries the platform loader-path var. */
|
|
12
|
+
export const defaultFork = (modulePath, env) => fork(modulePath, [], {
|
|
13
|
+
serialization: 'advanced',
|
|
14
|
+
env,
|
|
15
|
+
execArgv: [],
|
|
16
|
+
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
|
|
17
|
+
});
|
|
18
|
+
/** Raised when the sidecar dies with a request in flight; drives the one retry. */
|
|
19
|
+
export class HostCrashError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'HostCrashError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const DEFAULT_TIMEOUT_MS = 300_000;
|
|
26
|
+
export class HostClient {
|
|
27
|
+
hostPath;
|
|
28
|
+
env;
|
|
29
|
+
forkImpl;
|
|
30
|
+
timeoutMs;
|
|
31
|
+
log;
|
|
32
|
+
child = null;
|
|
33
|
+
pending = new Map();
|
|
34
|
+
nextId = 1;
|
|
35
|
+
disposed = false;
|
|
36
|
+
constructor(opts) {
|
|
37
|
+
this.hostPath = opts.hostPath;
|
|
38
|
+
this.env = opts.env;
|
|
39
|
+
this.forkImpl = opts.forkImpl ?? defaultFork;
|
|
40
|
+
this.timeoutMs = opts.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
41
|
+
this.log = opts.log ?? (() => { });
|
|
42
|
+
}
|
|
43
|
+
/** Transcribe, restarting the sidecar ONCE if it crashes mid-request. */
|
|
44
|
+
async transcribe(req) {
|
|
45
|
+
try {
|
|
46
|
+
return await this.send(req);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err instanceof HostCrashError && !this.disposed) {
|
|
50
|
+
this.log(`stt-local: sherpa sidecar crashed (${err.message}) — restarting once`);
|
|
51
|
+
return await this.send(req); // a second crash propagates
|
|
52
|
+
}
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
send(req) {
|
|
57
|
+
if (this.disposed)
|
|
58
|
+
return Promise.reject(new Error('stt-local host is shut down'));
|
|
59
|
+
const child = this.ensureChild();
|
|
60
|
+
const id = this.nextId++;
|
|
61
|
+
const message = { ...req, id, type: 'transcribe' };
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const timer = setTimeout(() => {
|
|
64
|
+
this.pending.delete(id);
|
|
65
|
+
// A hung decode is unrecoverable for this child — reset it so the next
|
|
66
|
+
// call starts fresh.
|
|
67
|
+
this.killChild();
|
|
68
|
+
reject(new Error(`stt-local transcription timed out after ${this.timeoutMs} ms`));
|
|
69
|
+
}, this.timeoutMs);
|
|
70
|
+
timer.unref?.();
|
|
71
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
72
|
+
try {
|
|
73
|
+
child.send(message);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
this.settle(id, () => reject(err instanceof Error ? err : new Error(String(err))));
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
ensureChild() {
|
|
81
|
+
if (this.child)
|
|
82
|
+
return this.child;
|
|
83
|
+
const child = this.forkImpl(this.hostPath, this.env);
|
|
84
|
+
this.child = child;
|
|
85
|
+
child.on('message', (msg) => this.onMessage(msg));
|
|
86
|
+
child.on('exit', (code, signal) => this.onExit(code, signal));
|
|
87
|
+
child.on('error', (err) => this.onError(err));
|
|
88
|
+
return child;
|
|
89
|
+
}
|
|
90
|
+
onMessage(msg) {
|
|
91
|
+
const reply = msg;
|
|
92
|
+
if (!reply || typeof reply !== 'object' || typeof reply.id !== 'number')
|
|
93
|
+
return;
|
|
94
|
+
const p = this.pending.get(reply.id);
|
|
95
|
+
if (!p)
|
|
96
|
+
return;
|
|
97
|
+
this.settle(reply.id, () => {
|
|
98
|
+
if (reply.ok) {
|
|
99
|
+
p.resolve(reply.language !== undefined ? { text: reply.text, language: reply.language } : { text: reply.text });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
p.reject(new Error(`stt-local sidecar ${reply.error.kind} error: ${reply.error.message}`));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
onExit(code, signal) {
|
|
107
|
+
this.child = null;
|
|
108
|
+
if (this.pending.size === 0)
|
|
109
|
+
return;
|
|
110
|
+
const why = `sidecar exited (code=${code ?? 'null'}, signal=${signal ?? 'null'})`;
|
|
111
|
+
this.rejectAll(new HostCrashError(why));
|
|
112
|
+
}
|
|
113
|
+
onError(err) {
|
|
114
|
+
this.child = null;
|
|
115
|
+
this.rejectAll(new HostCrashError(`sidecar spawn/runtime error: ${err.message}`));
|
|
116
|
+
}
|
|
117
|
+
/** Resolve/reject one pending request, clearing its timer and map entry. */
|
|
118
|
+
settle(id, run) {
|
|
119
|
+
const p = this.pending.get(id);
|
|
120
|
+
if (!p)
|
|
121
|
+
return;
|
|
122
|
+
if (p.timer)
|
|
123
|
+
clearTimeout(p.timer);
|
|
124
|
+
this.pending.delete(id);
|
|
125
|
+
run();
|
|
126
|
+
}
|
|
127
|
+
rejectAll(err) {
|
|
128
|
+
for (const [id, p] of this.pending) {
|
|
129
|
+
if (p.timer)
|
|
130
|
+
clearTimeout(p.timer);
|
|
131
|
+
this.pending.delete(id);
|
|
132
|
+
p.reject(err);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
killChild() {
|
|
136
|
+
const child = this.child;
|
|
137
|
+
this.child = null;
|
|
138
|
+
if (child) {
|
|
139
|
+
try {
|
|
140
|
+
child.kill('SIGKILL');
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
/* already gone */
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Kill the sidecar and fail any in-flight requests. Idempotent. */
|
|
148
|
+
shutdown() {
|
|
149
|
+
if (this.disposed)
|
|
150
|
+
return;
|
|
151
|
+
this.disposed = true;
|
|
152
|
+
this.rejectAll(new Error('stt-local host shut down'));
|
|
153
|
+
this.killChild();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=host-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-client.js","sourceRoot":"","sources":["../src/host-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tiny message protocol spoken between the parent (host-client) and the
|
|
3
|
+
* forked sherpa sidecar, plus the pure per-message handler the sidecar runs.
|
|
4
|
+
* Kept free of `node:child_process` so BOTH sides can import it (the parent for
|
|
5
|
+
* the types, the child for the handler) without dragging fork plumbing into
|
|
6
|
+
* either bundle.
|
|
7
|
+
*
|
|
8
|
+
* Wire shape (over the fork IPC channel, `serialization: 'advanced'` so the
|
|
9
|
+
* `Float32Array` of samples round-trips intact):
|
|
10
|
+
* parent → child: TranscribeRequest (carries the decoded 16 kHz mono PCM)
|
|
11
|
+
* child → parent: HostReply (the transcript text)
|
|
12
|
+
*/
|
|
13
|
+
/** One transcription request. Carries the full model config so the sidecar is
|
|
14
|
+
* stateless-per-message except for a recognizer cache keyed by `modelKey`. The
|
|
15
|
+
* audio is already decoded to Float32 mono @ 16 kHz by the parent — the sidecar
|
|
16
|
+
* only owns the native recognition. */
|
|
17
|
+
export interface TranscribeRequest {
|
|
18
|
+
readonly id: number;
|
|
19
|
+
readonly type: 'transcribe';
|
|
20
|
+
/** Cache key for the loaded recognizer (the absolute encoder path). */
|
|
21
|
+
readonly modelKey: string;
|
|
22
|
+
/** Absolute path to the Whisper encoder `.onnx`. */
|
|
23
|
+
readonly encoder: string;
|
|
24
|
+
/** Absolute path to the Whisper decoder `.onnx`. */
|
|
25
|
+
readonly decoder: string;
|
|
26
|
+
/** Absolute path to `<id>-tokens.txt`. */
|
|
27
|
+
readonly tokens: string;
|
|
28
|
+
/** Inference thread count. */
|
|
29
|
+
readonly numThreads: number;
|
|
30
|
+
/** Compute provider (`cpu`). */
|
|
31
|
+
readonly provider: string;
|
|
32
|
+
/** Best-effort Whisper language tag; empty string ⇒ auto-detect. */
|
|
33
|
+
readonly language: string;
|
|
34
|
+
/** Whisper task: `transcribe` (same-language) or `translate` (→ English). */
|
|
35
|
+
readonly task: string;
|
|
36
|
+
/** Mono PCM in [-1, 1] at `sampleRate`; structured-cloned intact over IPC. */
|
|
37
|
+
readonly samples: Float32Array;
|
|
38
|
+
/** Sample rate of `samples` (16000 — the parent resamples before sending). */
|
|
39
|
+
readonly sampleRate: number;
|
|
40
|
+
}
|
|
41
|
+
export type HostRequest = TranscribeRequest;
|
|
42
|
+
export type HostErrorKind =
|
|
43
|
+
/** Recognizer failed to load (bad/absent files, unsupported arch, dlopen). */
|
|
44
|
+
'init'
|
|
45
|
+
/** Decoding itself threw. */
|
|
46
|
+
| 'runtime';
|
|
47
|
+
export interface TranscribeResult {
|
|
48
|
+
readonly text: string;
|
|
49
|
+
/** Whisper's detected language, when the native result reports one. */
|
|
50
|
+
readonly language?: string;
|
|
51
|
+
}
|
|
52
|
+
export type HostReply = ({
|
|
53
|
+
readonly id: number;
|
|
54
|
+
readonly ok: true;
|
|
55
|
+
} & TranscribeResult) | {
|
|
56
|
+
readonly id: number;
|
|
57
|
+
readonly ok: false;
|
|
58
|
+
readonly error: {
|
|
59
|
+
readonly message: string;
|
|
60
|
+
readonly kind: HostErrorKind;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
/** The subset of the sherpa-onnx-node surface the sidecar uses. Declared here
|
|
64
|
+
* (rather than leaning on the addon's ambient types) so the handler is
|
|
65
|
+
* unit-testable with a fake module and typechecks without the native addon. */
|
|
66
|
+
export interface SherpaOfflineStream {
|
|
67
|
+
acceptWaveform(args: {
|
|
68
|
+
sampleRate: number;
|
|
69
|
+
samples: Float32Array;
|
|
70
|
+
}): void;
|
|
71
|
+
}
|
|
72
|
+
export interface SherpaOfflineRecognizerResult {
|
|
73
|
+
readonly text: string;
|
|
74
|
+
/** Whisper detected-language field on some builds; read defensively. */
|
|
75
|
+
readonly lang?: string;
|
|
76
|
+
}
|
|
77
|
+
export interface SherpaOfflineRecognizer {
|
|
78
|
+
createStream(): SherpaOfflineStream;
|
|
79
|
+
decode(stream: SherpaOfflineStream): void;
|
|
80
|
+
getResult(stream: SherpaOfflineStream): SherpaOfflineRecognizerResult;
|
|
81
|
+
}
|
|
82
|
+
export interface SherpaModule {
|
|
83
|
+
OfflineRecognizer: new (config: unknown) => SherpaOfflineRecognizer;
|
|
84
|
+
}
|
|
85
|
+
/** Lazily load the native sherpa module (so a dlopen failure surfaces as a
|
|
86
|
+
* classified reply, not a boot crash). */
|
|
87
|
+
export type LoadSherpa = () => SherpaModule;
|
|
88
|
+
export type HostMessageHandler = (req: HostRequest) => Promise<HostReply>;
|
|
89
|
+
/**
|
|
90
|
+
* Build the sidecar's message handler. Loads the sherpa module on first use and
|
|
91
|
+
* caches one `OfflineRecognizer` per `modelKey` so repeated calls on the same
|
|
92
|
+
* model reuse the loaded weights. Every failure becomes a typed `ok:false`
|
|
93
|
+
* reply — nothing throws out of `handle`.
|
|
94
|
+
*/
|
|
95
|
+
export declare function createMessageHandler(loadSherpa: LoadSherpa): HostMessageHandler;
|
|
96
|
+
//# sourceMappingURL=host-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-protocol.d.ts","sourceRoot":"","sources":["../src/host-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;wCAGwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8BAA8B;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gCAAgC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAE5C,MAAM,MAAM,aAAa;AACvB,8EAA8E;AAC5E,MAAM;AACR,6BAA6B;GAC3B,SAAS,CAAC;AAEd,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,SAAS,GACjB,CAAC;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG,gBAAgB,CAAC,GAC/D;IACE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;KAAE,CAAC;CAC5E,CAAC;AAEN;;gFAEgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,YAAY,CAAA;KAAE,GAAG,IAAI,CAAC;CAC3E;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,IAAI,mBAAmB,CAAC;IACpC,MAAM,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C,SAAS,CAAC,MAAM,EAAE,mBAAmB,GAAG,6BAA6B,CAAC;CACvE;AAED,MAAM,WAAW,YAAY;IAC3B,iBAAiB,EAAE,KAAK,MAAM,EAAE,OAAO,KAAK,uBAAuB,CAAC;CACrE;AAED;2CAC2C;AAC3C,MAAM,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAE1E;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,kBAAkB,CAgD/E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tiny message protocol spoken between the parent (host-client) and the
|
|
3
|
+
* forked sherpa sidecar, plus the pure per-message handler the sidecar runs.
|
|
4
|
+
* Kept free of `node:child_process` so BOTH sides can import it (the parent for
|
|
5
|
+
* the types, the child for the handler) without dragging fork plumbing into
|
|
6
|
+
* either bundle.
|
|
7
|
+
*
|
|
8
|
+
* Wire shape (over the fork IPC channel, `serialization: 'advanced'` so the
|
|
9
|
+
* `Float32Array` of samples round-trips intact):
|
|
10
|
+
* parent → child: TranscribeRequest (carries the decoded 16 kHz mono PCM)
|
|
11
|
+
* child → parent: HostReply (the transcript text)
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Build the sidecar's message handler. Loads the sherpa module on first use and
|
|
15
|
+
* caches one `OfflineRecognizer` per `modelKey` so repeated calls on the same
|
|
16
|
+
* model reuse the loaded weights. Every failure becomes a typed `ok:false`
|
|
17
|
+
* reply — nothing throws out of `handle`.
|
|
18
|
+
*/
|
|
19
|
+
export function createMessageHandler(loadSherpa) {
|
|
20
|
+
const cache = new Map();
|
|
21
|
+
let mod = null;
|
|
22
|
+
return async function handle(req) {
|
|
23
|
+
let recognizer = cache.get(req.modelKey);
|
|
24
|
+
if (!recognizer) {
|
|
25
|
+
try {
|
|
26
|
+
mod ??= loadSherpa();
|
|
27
|
+
recognizer = new mod.OfflineRecognizer({
|
|
28
|
+
featConfig: { sampleRate: 16_000, featureDim: 80 },
|
|
29
|
+
modelConfig: {
|
|
30
|
+
whisper: {
|
|
31
|
+
encoder: req.encoder,
|
|
32
|
+
decoder: req.decoder,
|
|
33
|
+
// `language`/`task` are present in the OfflineWhisperModelConfig
|
|
34
|
+
// types but exercised by no upstream example — pass them through
|
|
35
|
+
// best-effort (empty language ⇒ Whisper auto-detects anyway).
|
|
36
|
+
language: req.language,
|
|
37
|
+
task: req.task,
|
|
38
|
+
},
|
|
39
|
+
tokens: req.tokens,
|
|
40
|
+
numThreads: req.numThreads,
|
|
41
|
+
provider: req.provider,
|
|
42
|
+
debug: false,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
cache.set(req.modelKey, recognizer);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
return { id: req.id, ok: false, error: { message: errMsg(err), kind: 'init' } };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const stream = recognizer.createStream();
|
|
53
|
+
stream.acceptWaveform({ sampleRate: req.sampleRate, samples: req.samples });
|
|
54
|
+
recognizer.decode(stream);
|
|
55
|
+
const result = recognizer.getResult(stream);
|
|
56
|
+
const text = typeof result?.text === 'string' ? result.text : '';
|
|
57
|
+
const reply = typeof result?.lang === 'string' && result.lang
|
|
58
|
+
? { id: req.id, ok: true, text, language: result.lang }
|
|
59
|
+
: { id: req.id, ok: true, text };
|
|
60
|
+
return reply;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
return { id: req.id, ok: false, error: { message: errMsg(err), kind: 'runtime' } };
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function errMsg(err) {
|
|
68
|
+
return err instanceof Error ? err.message : String(err);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=host-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-protocol.js","sourceRoot":"","sources":["../src/host-protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Plugin } from '@moxxy/sdk';
|
|
2
|
+
import { type LocalWhisperOptions } from './local-stt.js';
|
|
3
|
+
export { LocalWhisperTranscriber, createLocalWhisperTranscriber, shutdownLocalStt, LOCAL_WHISPER_TRANSCRIBER_NAME, type LocalWhisperOptions, type WhisperModelPaths, } from './local-stt.js';
|
|
4
|
+
export { MODEL_CATALOG, DEFAULT_MODEL_ID, modelIds, findModel, requireModel, type WhisperModelEntry, type WhisperModelId, } from './models.js';
|
|
5
|
+
export { pcm16ToFloat32, downmixToMono, resampleLinear, parseWav, isRiffWave, TARGET_SAMPLE_RATE, type ParsedWav, } from './audio.js';
|
|
6
|
+
export { decodeToMono16k, type DecodeOptions } from './decode.js';
|
|
7
|
+
export { decodeViaFfmpeg, missingFfmpegError, __resetFfmpegProbeForTest, } from './ffmpeg.js';
|
|
8
|
+
export { HostClient, type HostClientLike, type ForkLike, type ChildHandle } from './host-client.js';
|
|
9
|
+
export { sherpaPlatformPackage, resolveSherpaLibDir, sherpaEnv, libraryPathVar, } from './platform.js';
|
|
10
|
+
export interface BuildLocalSttPluginOptions {
|
|
11
|
+
/** Build-time defaults / test seams for the transcriber (injected
|
|
12
|
+
* `ensureModelImpl`, `hostFactory`, `fetchImpl`, `modelsDir`, `log`, …). */
|
|
13
|
+
readonly defaults?: LocalWhisperOptions;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the @moxxy/plugin-stt-local plugin. Registers exactly one transcriber,
|
|
17
|
+
* `local-whisper`, backed by sherpa-onnx Whisper models running in a forked
|
|
18
|
+
* sidecar. Side-effect free at load: no model is downloaded and no process is
|
|
19
|
+
* spawned until the first `transcribe`. Per-activation config
|
|
20
|
+
* (`session.transcribers.setActive('local-whisper', { model, language,
|
|
21
|
+
* numThreads })`) overrides the build-time defaults.
|
|
22
|
+
*
|
|
23
|
+
* Like the OpenAI STT sibling, the plugin does NOT set itself active —
|
|
24
|
+
* registering it is side-effect free (no auto-adopt in TranscriberRegistry).
|
|
25
|
+
* The host/user activates it explicitly, so a local-only setup never
|
|
26
|
+
* surprises anyone with a network call (there isn't one here) and a mixed
|
|
27
|
+
* setup keeps whatever STT backend was chosen.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildLocalSttPlugin(opts?: BuildLocalSttPluginOptions): Plugin;
|
|
30
|
+
declare const _default: Plugin;
|
|
31
|
+
export default _default;
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,gBAAgB,EAChB,8BAA8B,EAC9B,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,QAAQ,EACR,UAAU,EACV,kBAAkB,EAClB,KAAK,SAAS,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,SAAS,EACT,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,0BAA0B;IACzC;iFAC6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CACzC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,0BAA+B,GAAG,MAAM,CAgBjF;;AAsBD,wBAAqC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { definePlugin, defineTranscriber } from '@moxxy/sdk';
|
|
2
|
+
import { createLocalWhisperTranscriber, LOCAL_WHISPER_TRANSCRIBER_NAME, shutdownLocalStt, } from './local-stt.js';
|
|
3
|
+
export { LocalWhisperTranscriber, createLocalWhisperTranscriber, shutdownLocalStt, LOCAL_WHISPER_TRANSCRIBER_NAME, } from './local-stt.js';
|
|
4
|
+
export { MODEL_CATALOG, DEFAULT_MODEL_ID, modelIds, findModel, requireModel, } from './models.js';
|
|
5
|
+
export { pcm16ToFloat32, downmixToMono, resampleLinear, parseWav, isRiffWave, TARGET_SAMPLE_RATE, } from './audio.js';
|
|
6
|
+
export { decodeToMono16k } from './decode.js';
|
|
7
|
+
export { decodeViaFfmpeg, missingFfmpegError, __resetFfmpegProbeForTest, } from './ffmpeg.js';
|
|
8
|
+
export { HostClient } from './host-client.js';
|
|
9
|
+
export { sherpaPlatformPackage, resolveSherpaLibDir, sherpaEnv, libraryPathVar, } from './platform.js';
|
|
10
|
+
/**
|
|
11
|
+
* Build the @moxxy/plugin-stt-local plugin. Registers exactly one transcriber,
|
|
12
|
+
* `local-whisper`, backed by sherpa-onnx Whisper models running in a forked
|
|
13
|
+
* sidecar. Side-effect free at load: no model is downloaded and no process is
|
|
14
|
+
* spawned until the first `transcribe`. Per-activation config
|
|
15
|
+
* (`session.transcribers.setActive('local-whisper', { model, language,
|
|
16
|
+
* numThreads })`) overrides the build-time defaults.
|
|
17
|
+
*
|
|
18
|
+
* Like the OpenAI STT sibling, the plugin does NOT set itself active —
|
|
19
|
+
* registering it is side-effect free (no auto-adopt in TranscriberRegistry).
|
|
20
|
+
* The host/user activates it explicitly, so a local-only setup never
|
|
21
|
+
* surprises anyone with a network call (there isn't one here) and a mixed
|
|
22
|
+
* setup keeps whatever STT backend was chosen.
|
|
23
|
+
*/
|
|
24
|
+
export function buildLocalSttPlugin(opts = {}) {
|
|
25
|
+
const defaults = opts.defaults ?? {};
|
|
26
|
+
return definePlugin({
|
|
27
|
+
name: '@moxxy/plugin-stt-local',
|
|
28
|
+
version: '0.0.0',
|
|
29
|
+
transcribers: [
|
|
30
|
+
defineTranscriber({
|
|
31
|
+
name: LOCAL_WHISPER_TRANSCRIBER_NAME,
|
|
32
|
+
displayName: 'Local Whisper (offline, multilingual)',
|
|
33
|
+
createClient: (config) => createLocalWhisperTranscriber({ ...defaults, ...configToOptions(config) }),
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
// Kill every spawned sidecar when the session/runner shuts down.
|
|
37
|
+
hooks: { onShutdown: () => shutdownLocalStt() },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** Narrow the untrusted per-activation config into typed options — only the
|
|
41
|
+
* known string `model` / `language` / `provider` and a positive-integer
|
|
42
|
+
* `numThreads` are honored so a malformed config can't break `createClient` or
|
|
43
|
+
* route to a bad model (unknown ids are caught by `requireModel`). */
|
|
44
|
+
function configToOptions(config) {
|
|
45
|
+
const out = {};
|
|
46
|
+
if (typeof config.model === 'string' && config.model)
|
|
47
|
+
out.model = config.model;
|
|
48
|
+
if (typeof config.language === 'string')
|
|
49
|
+
out.language = config.language;
|
|
50
|
+
if (typeof config.provider === 'string' && config.provider)
|
|
51
|
+
out.provider = config.provider;
|
|
52
|
+
if (typeof config.numThreads === 'number' &&
|
|
53
|
+
Number.isInteger(config.numThreads) &&
|
|
54
|
+
config.numThreads > 0) {
|
|
55
|
+
out.numThreads = config.numThreads;
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
// Discovery entry: `createPluginLoader` requires a default Plugin export.
|
|
60
|
+
export default buildLocalSttPlugin();
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAe,MAAM,YAAY,CAAC;AAE1E,OAAO,EACL,6BAA6B,EAC7B,8BAA8B,EAC9B,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,gBAAgB,EAChB,8BAA8B,GAG/B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,SAAS,EACT,YAAY,GAGb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,QAAQ,EACR,UAAU,EACV,kBAAkB,GAEnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAsB,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAwD,MAAM,kBAAkB,CAAC;AACpG,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,SAAS,EACT,cAAc,GACf,MAAM,eAAe,CAAC;AAQvB;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAmC,EAAE;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IACrC,OAAO,YAAY,CAAC;QAClB,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,OAAO;QAChB,YAAY,EAAE;YACZ,iBAAiB,CAAC;gBAChB,IAAI,EAAE,8BAA8B;gBACpC,WAAW,EAAE,uCAAuC;gBACpD,YAAY,EAAE,CAAC,MAA+B,EAAE,EAAE,CAChD,6BAA6B,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;aAC7E,CAAC;SACH;QACD,iEAAiE;QACjE,KAAK,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,gBAAgB,EAAE,EAAE;KAChD,CAAC,CAAC;AACL,CAAC;AAED;;;uEAGuE;AACvE,SAAS,eAAe,CAAC,MAA+B;IACtD,MAAM,GAAG,GAA4E,EAAE,CAAC;IACxF,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK;QAAE,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC/E,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;QAAE,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACxE,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ;QAAE,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC3F,IACE,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ;QACrC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC;QACnC,MAAM,CAAC,UAAU,GAAG,CAAC,EACrB,CAAC;QACD,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,0EAA0E;AAC1E,eAAe,mBAAmB,EAAE,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `local-whisper` Transcriber: fully local, on-device speech-to-text.
|
|
3
|
+
*
|
|
4
|
+
* On the first transcription for a model it downloads + verifies that model's
|
|
5
|
+
* Whisper export (once, via @moxxy/model-fetch), then decodes the inbound audio
|
|
6
|
+
* to Float32 mono @ 16 kHz IN-PROCESS (raw PCM / WAV) or via ffmpeg (compressed
|
|
7
|
+
* containers) and hands the samples to the sherpa sidecar ({@link HostClient}).
|
|
8
|
+
* No key, no network at transcription time.
|
|
9
|
+
*
|
|
10
|
+
* Every collaborator is injectable (`ensureModelImpl`, `hostFactory`, `log`,
|
|
11
|
+
* `modelsDir`, `fetchImpl`, `spawnImpl`) so the whole flow is unit-testable
|
|
12
|
+
* without the native addon, a real download, or ffmpeg.
|
|
13
|
+
*/
|
|
14
|
+
import type { spawn } from 'node:child_process';
|
|
15
|
+
import { type TranscribeOptions, type Transcriber, type TranscriptionResult } from '@moxxy/sdk';
|
|
16
|
+
import { ensureModel, type FetchLike } from '@moxxy/model-fetch';
|
|
17
|
+
import { type HostClientLike } from './host-client.js';
|
|
18
|
+
/** The single registered transcriber name. */
|
|
19
|
+
export declare const LOCAL_WHISPER_TRANSCRIBER_NAME = "local-whisper";
|
|
20
|
+
/** Resolved on-disk paths sherpa needs for one model. */
|
|
21
|
+
export interface WhisperModelPaths {
|
|
22
|
+
readonly encoder: string;
|
|
23
|
+
readonly decoder: string;
|
|
24
|
+
readonly tokens: string;
|
|
25
|
+
}
|
|
26
|
+
export interface LocalWhisperOptions {
|
|
27
|
+
/** Model id: `tiny` | `base` | `small`. Default `base` (`small` for Polish). */
|
|
28
|
+
readonly model?: string;
|
|
29
|
+
/** Default Whisper language tag (BCP-47-ish, e.g. `en`, `pl`). Per-call
|
|
30
|
+
* `TranscribeOptions.language` overrides it; omit to let Whisper auto-detect. */
|
|
31
|
+
readonly language?: string;
|
|
32
|
+
/** Inference threads. Default 2. */
|
|
33
|
+
readonly numThreads?: number;
|
|
34
|
+
/** Root for downloaded models. Default `~/.moxxy/models/stt` (MOXXY_HOME-aware). */
|
|
35
|
+
readonly modelsDir?: string;
|
|
36
|
+
/** sherpa compute provider. Default `cpu`. */
|
|
37
|
+
readonly provider?: string;
|
|
38
|
+
/** Absolute path to the built sidecar entry. Default: `sidecar.js` beside
|
|
39
|
+
* this module (i.e. `dist/sidecar.js` in a published build). */
|
|
40
|
+
readonly sidecarPath?: string;
|
|
41
|
+
/** Injected model-ensure (tests). Defaults to @moxxy/model-fetch `ensureModel`. */
|
|
42
|
+
readonly ensureModelImpl?: typeof ensureModel;
|
|
43
|
+
/** Injected `fetch`, threaded into `ensureModel` (tests). */
|
|
44
|
+
readonly fetchImpl?: FetchLike;
|
|
45
|
+
/** Injected sidecar host factory (tests). Defaults to a real {@link HostClient}. */
|
|
46
|
+
readonly hostFactory?: () => HostClientLike;
|
|
47
|
+
/** Injected `spawn` for the ffmpeg decode path (tests). */
|
|
48
|
+
readonly spawnImpl?: typeof spawn;
|
|
49
|
+
/** Diagnostic log sink (download progress etc.). Defaults to stderr. */
|
|
50
|
+
readonly log?: (msg: string) => void;
|
|
51
|
+
}
|
|
52
|
+
/** Shut down every live local transcriber (kills their sidecars). */
|
|
53
|
+
export declare function shutdownLocalStt(): void;
|
|
54
|
+
export declare class LocalWhisperTranscriber implements Transcriber {
|
|
55
|
+
readonly name = "local-whisper";
|
|
56
|
+
private readonly model;
|
|
57
|
+
private readonly defaultLanguage;
|
|
58
|
+
private readonly numThreads;
|
|
59
|
+
private readonly modelsDir;
|
|
60
|
+
private readonly provider;
|
|
61
|
+
private readonly sidecarPath;
|
|
62
|
+
private readonly ensureModelImpl;
|
|
63
|
+
private readonly fetchImpl;
|
|
64
|
+
private readonly hostFactory;
|
|
65
|
+
private readonly spawnImpl;
|
|
66
|
+
private readonly log;
|
|
67
|
+
private host;
|
|
68
|
+
/** In-flight model download promise — de-dupes concurrent first-use. */
|
|
69
|
+
private ensuring;
|
|
70
|
+
constructor(opts?: LocalWhisperOptions);
|
|
71
|
+
transcribe(audio: Uint8Array | ArrayBuffer, opts?: TranscribeOptions): Promise<TranscriptionResult>;
|
|
72
|
+
/** Kill this transcriber's sidecar (if any) and drop registration. */
|
|
73
|
+
shutdown(): void;
|
|
74
|
+
private getHost;
|
|
75
|
+
/** Resolve on-disk paths for the model, downloading + extracting it once. */
|
|
76
|
+
private ensureModelFiles;
|
|
77
|
+
/** A progress callback that logs a single start line then ~every-10% ticks,
|
|
78
|
+
* and an extraction line — but stays silent when the model is already
|
|
79
|
+
* present (ensureModel emits only `done`). */
|
|
80
|
+
private makeProgressLogger;
|
|
81
|
+
/** Build a real sidecar-backed host, resolving the platform loader path.
|
|
82
|
+
* Fails clearly when no sherpa binary exists for this platform/arch. */
|
|
83
|
+
private defaultHost;
|
|
84
|
+
}
|
|
85
|
+
export declare function createLocalWhisperTranscriber(opts?: LocalWhisperOptions): LocalWhisperTranscriber;
|
|
86
|
+
//# sourceMappingURL=local-stt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-stt.d.ts","sourceRoot":"","sources":["../src/local-stt.ts"],"names":[],"mappings":"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"}
|