@moxxy/plugin-stt-local 0.44.0 → 0.46.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/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type Plugin } from '@moxxy/sdk';
2
2
  import { type LocalWhisperOptions } from './local-stt.js';
3
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';
4
+ export { MODEL_CATALOG, DEFAULT_MODEL_ID, modelIds, findModel, requireModel, modelFiles, modelBytes, modelDir, defaultModelsDir, type WhisperModelEntry, type WhisperModelId, } from './models.js';
5
5
  export { pcm16ToFloat32, downmixToMono, resampleLinear, parseWav, isRiffWave, TARGET_SAMPLE_RATE, type ParsedWav, } from './audio.js';
6
6
  export { decodeToMono16k, type DecodeOptions } from './decode.js';
7
7
  export { decodeViaFfmpeg, missingFfmpegError, __resetFfmpegProbeForTest, } from './ffmpeg.js';
@@ -9,7 +9,7 @@ export { HostClient, type HostClientLike, type ForkLike, type ChildHandle } from
9
9
  export { sherpaPlatformPackage, resolveSherpaLibDir, sherpaEnv, libraryPathVar, } from './platform.js';
10
10
  export interface BuildLocalSttPluginOptions {
11
11
  /** Build-time defaults / test seams for the transcriber (injected
12
- * `ensureModelImpl`, `hostFactory`, `fetchImpl`, `modelsDir`, `log`, …). */
12
+ * `ensureModelFilesImpl`, `hostFactory`, `fetchImpl`, `modelsDir`, `log`, …). */
13
13
  readonly defaults?: LocalWhisperOptions;
14
14
  }
15
15
  /**
@@ -1 +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"}
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,UAAU,EACV,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,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;sFACkF;IAClF,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 CHANGED
@@ -1,7 +1,7 @@
1
1
  import { definePlugin, defineTranscriber } from '@moxxy/sdk';
2
2
  import { createLocalWhisperTranscriber, LOCAL_WHISPER_TRANSCRIBER_NAME, shutdownLocalStt, } from './local-stt.js';
3
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';
4
+ export { MODEL_CATALOG, DEFAULT_MODEL_ID, modelIds, findModel, requireModel, modelFiles, modelBytes, modelDir, defaultModelsDir, } from './models.js';
5
5
  export { pcm16ToFloat32, downmixToMono, resampleLinear, parseWav, isRiffWave, TARGET_SAMPLE_RATE, } from './audio.js';
6
6
  export { decodeToMono16k } from './decode.js';
7
7
  export { decodeViaFfmpeg, missingFfmpegError, __resetFfmpegProbeForTest, } from './ffmpeg.js';
package/dist/index.js.map CHANGED
@@ -1 +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"}
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,EACZ,UAAU,EACV,UAAU,EACV,QAAQ,EACR,gBAAgB,GAGjB,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"}
@@ -2,18 +2,18 @@
2
2
  * The `local-whisper` Transcriber: fully local, on-device speech-to-text.
3
3
  *
4
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
5
+ * Whisper files (once, via @moxxy/model-fetch), then decodes the inbound audio
6
6
  * to Float32 mono @ 16 kHz IN-PROCESS (raw PCM / WAV) or via ffmpeg (compressed
7
7
  * containers) and hands the samples to the sherpa sidecar ({@link HostClient}).
8
8
  * No key, no network at transcription time.
9
9
  *
10
- * Every collaborator is injectable (`ensureModelImpl`, `hostFactory`, `log`,
10
+ * Every collaborator is injectable (`ensureModelFilesImpl`, `hostFactory`, `log`,
11
11
  * `modelsDir`, `fetchImpl`, `spawnImpl`) so the whole flow is unit-testable
12
12
  * without the native addon, a real download, or ffmpeg.
13
13
  */
14
14
  import type { spawn } from 'node:child_process';
15
15
  import { type TranscribeOptions, type Transcriber, type TranscriptionResult } from '@moxxy/sdk';
16
- import { ensureModel, type FetchLike } from '@moxxy/model-fetch';
16
+ import { ensureModelFiles, type FetchLike } from '@moxxy/model-fetch';
17
17
  import { type HostClientLike } from './host-client.js';
18
18
  /** The single registered transcriber name. */
19
19
  export declare const LOCAL_WHISPER_TRANSCRIBER_NAME = "local-whisper";
@@ -24,7 +24,7 @@ export interface WhisperModelPaths {
24
24
  readonly tokens: string;
25
25
  }
26
26
  export interface LocalWhisperOptions {
27
- /** Model id: `tiny` | `base` | `small`. Default `base` (`small` for Polish). */
27
+ /** Model id. The only one is `turbo`, also the default. */
28
28
  readonly model?: string;
29
29
  /** Default Whisper language tag (BCP-47-ish, e.g. `en`, `pl`). Per-call
30
30
  * `TranscribeOptions.language` overrides it; omit to let Whisper auto-detect. */
@@ -38,9 +38,9 @@ export interface LocalWhisperOptions {
38
38
  /** Absolute path to the built sidecar entry. Default: `sidecar.js` beside
39
39
  * this module (i.e. `dist/sidecar.js` in a published build). */
40
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). */
41
+ /** Injected model download (tests). Defaults to @moxxy/model-fetch `ensureModelFiles`. */
42
+ readonly ensureModelFilesImpl?: typeof ensureModelFiles;
43
+ /** Injected `fetch`, threaded into the model download (tests). */
44
44
  readonly fetchImpl?: FetchLike;
45
45
  /** Injected sidecar host factory (tests). Defaults to a real {@link HostClient}. */
46
46
  readonly hostFactory?: () => HostClientLike;
@@ -59,7 +59,7 @@ export declare class LocalWhisperTranscriber implements Transcriber {
59
59
  private readonly modelsDir;
60
60
  private readonly provider;
61
61
  private readonly sidecarPath;
62
- private readonly ensureModelImpl;
62
+ private readonly ensureModelFilesImpl;
63
63
  private readonly fetchImpl;
64
64
  private readonly hostFactory;
65
65
  private readonly spawnImpl;
@@ -72,11 +72,10 @@ export declare class LocalWhisperTranscriber implements Transcriber {
72
72
  /** Kill this transcriber's sidecar (if any) and drop registration. */
73
73
  shutdown(): void;
74
74
  private getHost;
75
- /** Resolve on-disk paths for the model, downloading + extracting it once. */
75
+ /** Resolve on-disk paths for the model, downloading its files once. */
76
76
  private ensureModelFiles;
77
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`). */
78
+ * but stays silent when the model is already present (nothing to fetch). */
80
79
  private makeProgressLogger;
81
80
  /** Build a real sidecar-backed host, resolving the platform loader path.
82
81
  * Fails clearly when no sherpa binary exists for this platform/arch. */
@@ -1 +1 @@
1
- {"version":3,"file":"local-stt.d.ts","sourceRoot":"","sources":["../src/local-stt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-stt.d.ts","sourceRoot":"","sources":["../src/local-stt.ts"],"names":[],"mappings":"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"}
package/dist/local-stt.js CHANGED
@@ -2,24 +2,23 @@
2
2
  * The `local-whisper` Transcriber: fully local, on-device speech-to-text.
3
3
  *
4
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
5
+ * Whisper files (once, via @moxxy/model-fetch), then decodes the inbound audio
6
6
  * to Float32 mono @ 16 kHz IN-PROCESS (raw PCM / WAV) or via ffmpeg (compressed
7
7
  * containers) and hands the samples to the sherpa sidecar ({@link HostClient}).
8
8
  * No key, no network at transcription time.
9
9
  *
10
- * Every collaborator is injectable (`ensureModelImpl`, `hostFactory`, `log`,
10
+ * Every collaborator is injectable (`ensureModelFilesImpl`, `hostFactory`, `log`,
11
11
  * `modelsDir`, `fetchImpl`, `spawnImpl`) so the whole flow is unit-testable
12
12
  * without the native addon, a real download, or ffmpeg.
13
13
  */
14
14
  import path from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
16
  import { MoxxyError, } from '@moxxy/sdk';
17
- import { moxxyPath } from '@moxxy/sdk/server';
18
- import { ensureModel, } from '@moxxy/model-fetch';
17
+ import { ensureModelFiles, } from '@moxxy/model-fetch';
19
18
  import { TARGET_SAMPLE_RATE } from './audio.js';
20
19
  import { decodeToMono16k } from './decode.js';
21
20
  import { HostClient } from './host-client.js';
22
- import { DEFAULT_MODEL_ID, requireModel } from './models.js';
21
+ import { DEFAULT_MODEL_ID, defaultModelsDir, modelBytes, modelDir, modelFiles, requireModel, } from './models.js';
23
22
  import { resolveSherpaLibDir, sherpaEnv, sherpaPlatformPackage } from './platform.js';
24
23
  /** The single registered transcriber name. */
25
24
  export const LOCAL_WHISPER_TRANSCRIBER_NAME = 'local-whisper';
@@ -41,7 +40,7 @@ export class LocalWhisperTranscriber {
41
40
  modelsDir;
42
41
  provider;
43
42
  sidecarPath;
44
- ensureModelImpl;
43
+ ensureModelFilesImpl;
45
44
  fetchImpl;
46
45
  hostFactory;
47
46
  spawnImpl;
@@ -58,10 +57,10 @@ export class LocalWhisperTranscriber {
58
57
  opts.numThreads && Number.isInteger(opts.numThreads) && opts.numThreads > 0
59
58
  ? opts.numThreads
60
59
  : DEFAULT_NUM_THREADS;
61
- this.modelsDir = opts.modelsDir ?? moxxyPath('models', 'stt');
60
+ this.modelsDir = opts.modelsDir ?? defaultModelsDir();
62
61
  this.provider = opts.provider ?? 'cpu';
63
62
  this.sidecarPath = opts.sidecarPath;
64
- this.ensureModelImpl = opts.ensureModelImpl ?? ensureModel;
63
+ this.ensureModelFilesImpl = opts.ensureModelFilesImpl ?? ensureModelFiles;
65
64
  this.fetchImpl = opts.fetchImpl;
66
65
  this.hostFactory = opts.hostFactory ?? (() => this.defaultHost());
67
66
  this.spawnImpl = opts.spawnImpl;
@@ -114,14 +113,13 @@ export class LocalWhisperTranscriber {
114
113
  this.host ??= this.hostFactory();
115
114
  return this.host;
116
115
  }
117
- /** Resolve on-disk paths for the model, downloading + extracting it once. */
116
+ /** Resolve on-disk paths for the model, downloading its files once. */
118
117
  async ensureModelFiles(signal) {
119
- const dir = path.join(this.modelsDir, this.model.id);
118
+ const dir = modelDir(this.model, this.modelsDir);
120
119
  let inflight = this.ensuring;
121
120
  if (!inflight) {
122
- inflight = this.ensureModelImpl({
123
- url: this.model.url,
124
- sha256: this.model.sha256,
121
+ inflight = this.ensureModelFilesImpl({
122
+ files: modelFiles(this.model),
125
123
  dir,
126
124
  ...(this.fetchImpl ? { fetchImpl: this.fetchImpl } : {}),
127
125
  ...(signal ? { signal } : {}),
@@ -135,40 +133,29 @@ export class LocalWhisperTranscriber {
135
133
  });
136
134
  }
137
135
  await inflight;
138
- const root = path.join(dir, this.model.archiveRootDir);
139
136
  return {
140
- encoder: path.join(root, this.model.encoderFile),
141
- decoder: path.join(root, this.model.decoderFile),
142
- tokens: path.join(root, this.model.tokensFile),
137
+ encoder: path.join(dir, this.model.encoder.name),
138
+ decoder: path.join(dir, this.model.decoder.name),
139
+ tokens: path.join(dir, this.model.tokens.name),
143
140
  };
144
141
  }
145
142
  /** A progress callback that logs a single start line then ~every-10% ticks,
146
- * and an extraction line — but stays silent when the model is already
147
- * present (ensureModel emits only `done`). */
143
+ * but stays silent when the model is already present (nothing to fetch). */
148
144
  makeProgressLogger() {
149
145
  let started = false;
150
- let extracting = false;
151
146
  let lastPct = -1;
152
147
  return (p) => {
153
- if (p.phase === 'downloading') {
154
- if (!started) {
155
- started = true;
156
- this.log(`stt-local: first use — downloading Whisper ${this.model.id} (~${this.model.approxMb} MB) to ${this.modelsDir}, one-time`);
157
- }
158
- if (p.totalBytes > 0) {
159
- const pct = Math.floor((p.receivedBytes / p.totalBytes) * 100);
160
- if (pct >= lastPct + 10) {
161
- lastPct = pct - (pct % 10);
162
- this.log(`stt-local: downloading ${this.model.id} … ${lastPct}%`);
163
- }
164
- }
148
+ const pct = p.totalBytes > 0 ? Math.floor((p.receivedBytes / p.totalBytes) * 100) : 0;
149
+ if (!started) {
150
+ if (pct >= 100)
151
+ return;
152
+ started = true;
153
+ const mb = Math.round(modelBytes(this.model) / 1_000_000);
154
+ this.log(`stt-local: first use — downloading Whisper ${this.model.id} (~${mb} MB) to ${this.modelsDir}, one-time`);
165
155
  }
166
- else if (p.phase === 'extracting' && !extracting) {
167
- extracting = true;
168
- this.log(`stt-local: extracting ${this.model.id} …`);
169
- }
170
- else if (p.phase === 'done' && started) {
171
- this.log(`stt-local: ${this.model.id} ready.`);
156
+ if (pct >= lastPct + 10) {
157
+ lastPct = pct - (pct % 10);
158
+ this.log(lastPct >= 100 ? `stt-local: ${this.model.id} ready.` : `stt-local: downloading ${this.model.id} … ${lastPct}%`);
172
159
  }
173
160
  };
174
161
  }
@@ -1 +1 @@
1
- {"version":3,"file":"local-stt.js","sourceRoot":"","sources":["../src/local-stt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-stt.js","sourceRoot":"","sources":["../src/local-stt.ts"],"names":[],"mappings":"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"}
package/dist/models.d.ts CHANGED
@@ -1,42 +1,37 @@
1
1
  /**
2
2
  * The pinned Whisper model catalog.
3
3
  *
4
- * Every model is a multilingual sherpa-onnx Whisper export published as a
5
- * `.tar.bz2` under the project's `asr-models` GitHub release. Each `sha256` is
6
- * copied VERBATIM from that release's `checksum.txt` (re-verified 2026-07-05) —
7
- * so a first-use download is content-addressed and tamper-evident, unlike an
8
- * unverified blob fetch. The archive extracts to a single top directory
9
- * (`archiveRootDir`) holding `<id>-encoder.onnx`, `<id>-decoder.onnx` (+ int8
10
- * variants we don't use) and `<id>-tokens.txt`.
4
+ * The one model is Whisper large-v3 turbo, the multilingual sherpa-onnx int8
5
+ * export. Its three files download as they are from Hugging Face, pinned to a
6
+ * fixed revision and checked against their size and sha256 — byte for byte the
7
+ * files the project's `asr-models` GitHub release packs into a `.tar.bz2`
8
+ * (verified 2026-10-24), but with no archive to unpack: unpacking it in
9
+ * JavaScript took about 67 s against 9 s for the download.
11
10
  *
12
- * All three are the MULTILINGUAL models (not the `.en` English-only exports),
13
- * so Polish (and any other Whisper language) works — `small` is the accuracy
14
- * sweet spot for Polish, `base` the balanced default, `tiny` the fastest.
11
+ * Multilingual (not an `.en` export), so Polish and every other Whisper
12
+ * language work.
15
13
  */
16
- export type WhisperModelId = 'tiny' | 'base' | 'small';
14
+ import type { ModelFile } from '@moxxy/model-fetch/files';
15
+ export type WhisperModelId = 'turbo';
17
16
  export interface WhisperModelEntry {
18
- /** Stable model id (also the `model` config value). */
17
+ /** Stable model id (also the `model` config value and its directory name). */
19
18
  readonly id: WhisperModelId;
20
19
  /** Human label for UI surfaces / the first-use notice. */
21
20
  readonly label: string;
22
- /** Pinned archive URL (sherpa-onnx GitHub `asr-models` release). */
23
- readonly url: string;
24
- /** Pinned hex sha256 of the archive (from the release checksum.txt). */
25
- readonly sha256: string;
26
- /** Top directory the archive extracts to (`sherpa-onnx-whisper-<id>`). */
27
- readonly archiveRootDir: string;
28
- /** Encoder ONNX filename inside `archiveRootDir` (`<id>-encoder.onnx`). */
29
- readonly encoderFile: string;
30
- /** Decoder ONNX filename inside `archiveRootDir` (`<id>-decoder.onnx`). */
31
- readonly decoderFile: string;
32
- /** Tokens filename inside `archiveRootDir` (`<id>-tokens.txt`). */
33
- readonly tokensFile: string;
34
- /** Approximate download size in MB (for the one-time first-use notice). */
35
- readonly approxMb: number;
21
+ readonly encoder: ModelFile;
22
+ readonly decoder: ModelFile;
23
+ readonly tokens: ModelFile;
36
24
  }
37
25
  export declare const MODEL_CATALOG: readonly WhisperModelEntry[];
38
- /** Default model when the caller doesn't pick one. `base` balances size vs.
39
- * accuracy; `small` is recommended for Polish (see the catalog description). */
26
+ /** Every file of a model, in download order. */
27
+ export declare function modelFiles(entry: WhisperModelEntry): ModelFile[];
28
+ /** The model's download size in bytes. */
29
+ export declare function modelBytes(entry: WhisperModelEntry): number;
30
+ /** The models root: `~/.moxxy/models/stt`, MOXXY_HOME-aware. */
31
+ export declare function defaultModelsDir(): string;
32
+ /** Where a model's files live under the models root. */
33
+ export declare function modelDir(entry: WhisperModelEntry, modelsDir: string): string;
34
+ /** The model used when the caller doesn't pick one — the only one there is. */
40
35
  export declare const DEFAULT_MODEL_ID: WhisperModelId;
41
36
  /** All valid model ids, for error messages and validation. */
42
37
  export declare function modelIds(): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAwBD,eAAO,MAAM,aAAa,EAAE,SAAS,iBAAiB,EAmBrD,CAAC;AAEF;iFACiF;AACjF,eAAO,MAAM,gBAAgB,EAAE,cAAuB,CAAC;AAEvD,8DAA8D;AAC9D,wBAAgB,QAAQ,IAAI,MAAM,EAAE,CAEnC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAEnE;AAED;uEACuE;AACvE,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAWzE"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AAErC,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;CAC5B;AAKD,eAAO,MAAM,aAAa,EAAE,SAAS,iBAAiB,EAuBrD,CAAC;AAEF,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,EAAE,CAEhE;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAE3D;AAED,gEAAgE;AAChE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wDAAwD;AACxD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,cAAwB,CAAC;AAExD,8DAA8D;AAC9D,wBAAgB,QAAQ,IAAI,MAAM,EAAE,CAEnC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAEnE;AAED;uEACuE;AACvE,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAWzE"}
package/dist/models.js CHANGED
@@ -1,42 +1,63 @@
1
1
  /**
2
2
  * The pinned Whisper model catalog.
3
3
  *
4
- * Every model is a multilingual sherpa-onnx Whisper export published as a
5
- * `.tar.bz2` under the project's `asr-models` GitHub release. Each `sha256` is
6
- * copied VERBATIM from that release's `checksum.txt` (re-verified 2026-07-05) —
7
- * so a first-use download is content-addressed and tamper-evident, unlike an
8
- * unverified blob fetch. The archive extracts to a single top directory
9
- * (`archiveRootDir`) holding `<id>-encoder.onnx`, `<id>-decoder.onnx` (+ int8
10
- * variants we don't use) and `<id>-tokens.txt`.
4
+ * The one model is Whisper large-v3 turbo, the multilingual sherpa-onnx int8
5
+ * export. Its three files download as they are from Hugging Face, pinned to a
6
+ * fixed revision and checked against their size and sha256 — byte for byte the
7
+ * files the project's `asr-models` GitHub release packs into a `.tar.bz2`
8
+ * (verified 2026-10-24), but with no archive to unpack: unpacking it in
9
+ * JavaScript took about 67 s against 9 s for the download.
11
10
  *
12
- * All three are the MULTILINGUAL models (not the `.en` English-only exports),
13
- * so Polish (and any other Whisper language) works — `small` is the accuracy
14
- * sweet spot for Polish, `base` the balanced default, `tiny` the fastest.
11
+ * Multilingual (not an `.en` export), so Polish and every other Whisper
12
+ * language work.
15
13
  */
14
+ import path from 'node:path';
16
15
  import { MoxxyError } from '@moxxy/sdk';
17
- const RELEASE_BASE = 'https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models';
18
- function whisperModel(id, label, sha256, approxMb) {
19
- const archiveRootDir = `sherpa-onnx-whisper-${id}`;
20
- return {
21
- id,
22
- label,
23
- url: `${RELEASE_BASE}/${archiveRootDir}.tar.bz2`,
24
- sha256,
25
- archiveRootDir,
26
- encoderFile: `${id}-encoder.onnx`,
27
- decoderFile: `${id}-decoder.onnx`,
28
- tokensFile: `${id}-tokens.txt`,
29
- approxMb,
30
- };
31
- }
16
+ import { moxxyPath } from '@moxxy/sdk/server';
17
+ const TURBO_REVISION = '2ca6ff69fc878651b770880507669577ac41c2ff';
18
+ const TURBO_BASE = `https://huggingface.co/csukuangfj/sherpa-onnx-whisper-turbo/resolve/${TURBO_REVISION}`;
32
19
  export const MODEL_CATALOG = [
33
- whisperModel('tiny', 'Whisper tiny (multilingual — fastest, lowest accuracy)', 'c46116994e539aa165266d96b325252728429c12535eb9d8b6a2b10f129e66b1', 111),
34
- whisperModel('base', 'Whisper base (multilingual — balanced; default)', '911b2083efd7c0dca2ac3b358b75222660dc09fb716d64fbfc417ba6c99ff3de', 198),
35
- whisperModel('small', 'Whisper small (multilingual — best accuracy; recommended for Polish)', '486a46afbb7ba798507190ffe02fea2dd726049af212e774537efac6afb210a6', 610),
20
+ {
21
+ id: 'turbo',
22
+ label: 'Whisper turbo (multilingual, large-v3 turbo)',
23
+ encoder: {
24
+ name: 'turbo-encoder.int8.onnx',
25
+ url: `${TURBO_BASE}/turbo-encoder.int8.onnx`,
26
+ bytes: 674_716_297,
27
+ sha256: 'b02dcdf54f348741e93fe732b67d933c8dcb6735655f710640143081db38878b',
28
+ },
29
+ decoder: {
30
+ name: 'turbo-decoder.int8.onnx',
31
+ url: `${TURBO_BASE}/turbo-decoder.int8.onnx`,
32
+ bytes: 361_080_764,
33
+ sha256: '20accd02388482eb3a46bd615631adfdc85e1eb2c7db9ea3f02a40ffe6b81547',
34
+ },
35
+ tokens: {
36
+ name: 'turbo-tokens.txt',
37
+ url: `${TURBO_BASE}/turbo-tokens.txt`,
38
+ bytes: 816_730,
39
+ sha256: 'b34b360dbb493e781e479794586d661700670d65564001f23024971d1f2fa126',
40
+ },
41
+ },
36
42
  ];
37
- /** Default model when the caller doesn't pick one. `base` balances size vs.
38
- * accuracy; `small` is recommended for Polish (see the catalog description). */
39
- export const DEFAULT_MODEL_ID = 'base';
43
+ /** Every file of a model, in download order. */
44
+ export function modelFiles(entry) {
45
+ return [entry.encoder, entry.decoder, entry.tokens];
46
+ }
47
+ /** The model's download size in bytes. */
48
+ export function modelBytes(entry) {
49
+ return modelFiles(entry).reduce((sum, file) => sum + file.bytes, 0);
50
+ }
51
+ /** The models root: `~/.moxxy/models/stt`, MOXXY_HOME-aware. */
52
+ export function defaultModelsDir() {
53
+ return moxxyPath('models', 'stt');
54
+ }
55
+ /** Where a model's files live under the models root. */
56
+ export function modelDir(entry, modelsDir) {
57
+ return path.join(modelsDir, entry.id);
58
+ }
59
+ /** The model used when the caller doesn't pick one — the only one there is. */
60
+ export const DEFAULT_MODEL_ID = 'turbo';
40
61
  /** All valid model ids, for error messages and validation. */
41
62
  export function modelIds() {
42
63
  return MODEL_CATALOG.map((m) => m.id);
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAyBxC,MAAM,YAAY,GAAG,oEAAoE,CAAC;AAE1F,SAAS,YAAY,CACnB,EAAkB,EAClB,KAAa,EACb,MAAc,EACd,QAAgB;IAEhB,MAAM,cAAc,GAAG,uBAAuB,EAAE,EAAE,CAAC;IACnD,OAAO;QACL,EAAE;QACF,KAAK;QACL,GAAG,EAAE,GAAG,YAAY,IAAI,cAAc,UAAU;QAChD,MAAM;QACN,cAAc;QACd,WAAW,EAAE,GAAG,EAAE,eAAe;QACjC,WAAW,EAAE,GAAG,EAAE,eAAe;QACjC,UAAU,EAAE,GAAG,EAAE,aAAa;QAC9B,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAiC;IACzD,YAAY,CACV,MAAM,EACN,wDAAwD,EACxD,kEAAkE,EAClE,GAAG,CACJ;IACD,YAAY,CACV,MAAM,EACN,iDAAiD,EACjD,kEAAkE,EAClE,GAAG,CACJ;IACD,YAAY,CACV,OAAO,EACP,sEAAsE,EACtE,kEAAkE,EAClE,GAAG,CACJ;CACF,CAAC;AAEF;iFACiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAmB,MAAM,CAAC;AAEvD,8DAA8D;AAC9D,MAAM,UAAU,QAAQ;IACtB,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAChD,CAAC;AAED;uEACuE;AACvE,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,KAAa;IACpD,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,UAAU,CAAC;YACnB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,+BAA+B,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,KAAK,IAAI;YACxE,IAAI,EAAE,iBAAiB,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC/C,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;SAC9B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAe9C,MAAM,cAAc,GAAG,0CAA0C,CAAC;AAClE,MAAM,UAAU,GAAG,uEAAuE,cAAc,EAAE,CAAC;AAE3G,MAAM,CAAC,MAAM,aAAa,GAAiC;IACzD;QACE,EAAE,EAAE,OAAO;QACX,KAAK,EAAE,8CAA8C;QACrD,OAAO,EAAE;YACP,IAAI,EAAE,yBAAyB;YAC/B,GAAG,EAAE,GAAG,UAAU,0BAA0B;YAC5C,KAAK,EAAE,WAAW;YAClB,MAAM,EAAE,kEAAkE;SAC3E;QACD,OAAO,EAAE;YACP,IAAI,EAAE,yBAAyB;YAC/B,GAAG,EAAE,GAAG,UAAU,0BAA0B;YAC5C,KAAK,EAAE,WAAW;YAClB,MAAM,EAAE,kEAAkE;SAC3E;QACD,MAAM,EAAE;YACN,IAAI,EAAE,kBAAkB;YACxB,GAAG,EAAE,GAAG,UAAU,mBAAmB;YACrC,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,kEAAkE;SAC3E;KACF;CACF,CAAC;AAEF,gDAAgD;AAChD,MAAM,UAAU,UAAU,CAAC,KAAwB;IACjD,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,UAAU,CAAC,KAAwB;IACjD,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,gBAAgB;IAC9B,OAAO,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,QAAQ,CAAC,KAAwB,EAAE,SAAiB;IAClE,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAAmB,OAAO,CAAC;AAExD,8DAA8D;AAC9D,MAAM,UAAU,QAAQ;IACtB,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAChD,CAAC;AAED;uEACuE;AACvE,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,KAAa;IACpD,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,UAAU,CAAC;YACnB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,+BAA+B,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,KAAK,IAAI;YACxE,IAAI,EAAE,iBAAiB,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC/C,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;SAC9B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@moxxy/plugin-stt-local",
3
- "version": "0.44.0",
4
- "description": "Fully local, on-device speech-to-text Transcriber for moxxy. Runs multilingual Whisper (English + Polish) via sherpa-onnx in a forked sidecar process; models download once on first use from sherpa-onnx's pinned releases (sha256-verified). No API key, no network at transcription time. Plugs into the session TranscriberRegistry — the TUI and channel voice notes consume it transparently.",
3
+ "version": "0.46.0",
4
+ "description": "Fully local, on-device speech-to-text Transcriber for moxxy. Runs multilingual Whisper (English + Polish) via sherpa-onnx in a forked sidecar process; the Whisper turbo model downloads once on first use, pinned and sha256-verified. No API key, no network at transcription time. Plugs into the session TranscriberRegistry — the TUI and channel voice notes consume it transparently.",
5
5
  "keywords": [
6
6
  "moxxy",
7
7
  "agent",
@@ -34,6 +34,10 @@
34
34
  ".": {
35
35
  "types": "./dist/index.d.ts",
36
36
  "import": "./dist/index.js"
37
+ },
38
+ "./models": {
39
+ "types": "./dist/models.d.ts",
40
+ "import": "./dist/models.js"
37
41
  }
38
42
  },
39
43
  "files": [
@@ -48,15 +52,15 @@
48
52
  },
49
53
  "dependencies": {
50
54
  "sherpa-onnx-node": "^1.13.8",
51
- "@moxxy/model-fetch": "0.1.0",
52
- "@moxxy/sdk": "0.44.0"
55
+ "@moxxy/sdk": "0.46.0",
56
+ "@moxxy/model-fetch": "0.2.0"
53
57
  },
54
58
  "devDependencies": {
55
59
  "@types/node": "^22.20.5",
56
60
  "typescript": "^6.0.3",
57
61
  "vitest": "^5.0.3",
58
- "@moxxy/tsconfig": "0.0.0",
59
- "@moxxy/vitest-preset": "0.0.0"
62
+ "@moxxy/vitest-preset": "0.0.0",
63
+ "@moxxy/tsconfig": "0.0.0"
60
64
  },
61
65
  "scripts": {
62
66
  "build": "tsc -p tsconfig.json",
@@ -137,7 +137,7 @@ describe('createMessageHandler', () => {
137
137
  await handle(req({ id: 1 }));
138
138
  await handle(req({ id: 2 }));
139
139
  await handle(
140
- req({ id: 3, modelKey: '/models/small/small-encoder.onnx', encoder: '/models/small/small-encoder.onnx' }),
140
+ req({ id: 3, modelKey: '/models/turbo/turbo-encoder.int8.onnx', encoder: '/models/turbo/turbo-encoder.int8.onnx' }),
141
141
  );
142
142
  expect(loads).toBe(1); // module loaded once
143
143
  expect(s.constructed).toBe(2); // one recognizer per distinct modelKey
package/src/index.ts CHANGED
@@ -21,6 +21,10 @@ export {
21
21
  modelIds,
22
22
  findModel,
23
23
  requireModel,
24
+ modelFiles,
25
+ modelBytes,
26
+ modelDir,
27
+ defaultModelsDir,
24
28
  type WhisperModelEntry,
25
29
  type WhisperModelId,
26
30
  } from './models.js';
@@ -49,7 +53,7 @@ export {
49
53
 
50
54
  export interface BuildLocalSttPluginOptions {
51
55
  /** Build-time defaults / test seams for the transcriber (injected
52
- * `ensureModelImpl`, `hostFactory`, `fetchImpl`, `modelsDir`, `log`, …). */
56
+ * `ensureModelFilesImpl`, `hostFactory`, `fetchImpl`, `modelsDir`, `log`, …). */
53
57
  readonly defaults?: LocalWhisperOptions;
54
58
  }
55
59
 
package/src/live.test.ts CHANGED
@@ -14,8 +14,8 @@ import { removeDir } from '@moxxy/vitest-preset/fs';
14
14
  const SIDECAR_PATH = fileURLToPath(new URL('../dist/sidecar.js', import.meta.url));
15
15
 
16
16
  /**
17
- * Opt-in end-to-end test that REALLY downloads the tiny Whisper model and runs
18
- * the native sherpa sidecar — off by default (it fetches ~111 MB and needs the
17
+ * Opt-in end-to-end test that REALLY downloads the turbo Whisper model and runs
18
+ * the native sherpa sidecar — off by default (it fetches ~1 GB and needs the
19
19
  * platform binary). Run manually with:
20
20
  *
21
21
  * MOXXY_STT_LOCAL_LIVE=1 pnpm -F @moxxy/plugin-stt-local test
@@ -64,9 +64,9 @@ afterAll(async () => {
64
64
  });
65
65
 
66
66
  describe.skipIf(!LIVE)('local STT (live, real download + native sidecar)', () => {
67
- it('downloads the tiny model and transcribes a generated WAV fixture', async () => {
67
+ it('downloads the turbo model and transcribes a generated WAV fixture', async () => {
68
68
  const stt = createLocalWhisperTranscriber({
69
- model: 'tiny',
69
+ model: 'turbo',
70
70
  modelsDir,
71
71
  sidecarPath: SIDECAR_PATH,
72
72
  });
@@ -2,7 +2,7 @@ import * as path from 'node:path';
2
2
  import { describe, expect, it } from 'vitest';
3
3
 
4
4
  import { MOXXY_PCM16_24KHZ_MIME } from '@moxxy/sdk';
5
- import { ensureModel } from '@moxxy/model-fetch';
5
+ import type { ensureModelFiles } from '@moxxy/model-fetch';
6
6
 
7
7
  import {
8
8
  createLocalWhisperTranscriber,
@@ -49,14 +49,14 @@ function fakeHost(result: TranscribeResult = { text: 'hello world' }): {
49
49
  };
50
50
  }
51
51
 
52
- /** A fake ensureModel recording the urls it was asked to fetch. */
53
- function fakeEnsure(): { impl: typeof ensureModel; urls: string[] } {
54
- const urls: string[] = [];
55
- const impl = (async (opts: Parameters<typeof ensureModel>[0]) => {
56
- urls.push(opts.url);
52
+ /** A fake ensureModelFiles recording each model it was asked to fetch. */
53
+ function fakeEnsure(): { impl: typeof ensureModelFiles; calls: Parameters<typeof ensureModelFiles>[0][] } {
54
+ const calls: Parameters<typeof ensureModelFiles>[0][] = [];
55
+ const impl = (async (opts: Parameters<typeof ensureModelFiles>[0]) => {
56
+ calls.push(opts);
57
57
  return { dir: opts.dir, skipped: false };
58
- }) as unknown as typeof ensureModel;
59
- return { impl, urls };
58
+ }) as typeof ensureModelFiles;
59
+ return { impl, calls };
60
60
  }
61
61
 
62
62
  function makeTranscriber(
@@ -71,7 +71,7 @@ function makeTranscriber(
71
71
  const ensure = fakeEnsure();
72
72
  const transcriber = createLocalWhisperTranscriber({
73
73
  modelsDir: '/models/stt',
74
- ensureModelImpl: ensure.impl,
74
+ ensureModelFilesImpl: ensure.impl,
75
75
  hostFactory: () => host.host,
76
76
  log: () => {},
77
77
  ...over,
@@ -80,28 +80,29 @@ function makeTranscriber(
80
80
  }
81
81
 
82
82
  describe('LocalWhisperTranscriber.transcribe', () => {
83
- it('defaults to the base model, decodes raw PCM, and returns the transcript', async () => {
83
+ it('defaults to the turbo model, decodes raw PCM, and returns the transcript', async () => {
84
84
  const { transcriber, host, ensure } = makeTranscriber();
85
85
  const out = await transcriber.transcribe(AUDIO, { mimeType: MOXXY_PCM16_24KHZ_MIME });
86
86
  expect(out.text).toBe('hello world');
87
- expect(ensure.urls).toEqual([
88
- 'https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-whisper-base.tar.bz2',
87
+ const modelRoot = path.join('/models/stt', 'turbo');
88
+ expect(ensure.calls.map((call) => call.dir)).toEqual([modelRoot]);
89
+ expect(ensure.calls[0]!.files.map((file) => file.name)).toEqual([
90
+ 'turbo-encoder.int8.onnx',
91
+ 'turbo-decoder.int8.onnx',
92
+ 'turbo-tokens.txt',
89
93
  ]);
90
94
  const req = host.reqs[0]!;
91
- expect(req.encoder).toContain(path.join('sherpa-onnx-whisper-base', 'base-encoder.onnx'));
92
- expect(req.decoder).toContain('base-decoder.onnx');
93
- expect(req.tokens).toContain('base-tokens.txt');
95
+ expect(req.encoder).toBe(path.join(modelRoot, 'turbo-encoder.int8.onnx'));
96
+ expect(req.decoder).toBe(path.join(modelRoot, 'turbo-decoder.int8.onnx'));
97
+ expect(req.tokens).toBe(path.join(modelRoot, 'turbo-tokens.txt'));
94
98
  expect(req.sampleRate).toBe(16_000);
95
99
  expect(req.samples.length).toBe(160); // resampled 24k → 16k
96
100
  expect(req.task).toBe('transcribe');
97
101
  expect(out.durationSec).toBeCloseTo(160 / 16_000, 6);
98
102
  });
99
103
 
100
- it('selects a configured model (small) and its files', async () => {
101
- const { transcriber, host, ensure } = makeTranscriber({ model: 'small' });
102
- await transcriber.transcribe(AUDIO, { mimeType: MOXXY_PCM16_24KHZ_MIME });
103
- expect(ensure.urls[0]).toContain('sherpa-onnx-whisper-small.tar.bz2');
104
- expect(host.reqs[0]!.encoder).toContain('small-encoder.onnx');
104
+ it('rejects a removed model (small) left in config, before any download', () => {
105
+ expect(() => makeTranscriber({ model: 'small' })).toThrow(/Unknown local Whisper model "small"/);
105
106
  });
106
107
 
107
108
  it('passes a per-call language hint through to the recognizer', async () => {
@@ -145,14 +146,14 @@ describe('LocalWhisperTranscriber.transcribe', () => {
145
146
  await transcriber.transcribe(AUDIO, { mimeType: MOXXY_PCM16_24KHZ_MIME });
146
147
  await transcriber.transcribe(AUDIO, { mimeType: MOXXY_PCM16_24KHZ_MIME });
147
148
  await transcriber.transcribe(AUDIO, { mimeType: MOXXY_PCM16_24KHZ_MIME });
148
- expect(ensure.urls.length).toBe(1);
149
+ expect(ensure.calls.length).toBe(1);
149
150
  });
150
151
 
151
152
  it('returns empty text for empty/silent audio without touching the model', async () => {
152
153
  const { transcriber, host, ensure } = makeTranscriber();
153
154
  const out = await transcriber.transcribe(new Uint8Array(0), { mimeType: MOXXY_PCM16_24KHZ_MIME });
154
155
  expect(out.text).toBe('');
155
- expect(ensure.urls.length).toBe(0);
156
+ expect(ensure.calls.length).toBe(0);
156
157
  expect(host.reqs.length).toBe(0);
157
158
  });
158
159
 
@@ -202,14 +203,14 @@ describe('buildLocalSttPlugin', () => {
202
203
  const plugin = buildLocalSttPlugin({
203
204
  defaults: {
204
205
  modelsDir: '/models/stt',
205
- ensureModelImpl: ensure.impl,
206
+ ensureModelFilesImpl: ensure.impl,
206
207
  hostFactory: () => host.host,
207
208
  log: () => {},
208
209
  },
209
210
  });
210
- const inst = plugin.transcribers![0]!.createClient({ model: 'small', language: 'pl' });
211
+ const inst = plugin.transcribers![0]!.createClient({ model: 'turbo', language: 'pl' });
211
212
  await inst.transcribe(AUDIO, { mimeType: MOXXY_PCM16_24KHZ_MIME });
212
- expect(ensure.urls[0]).toContain('sherpa-onnx-whisper-small.tar.bz2');
213
+ expect(ensure.calls[0]!.dir).toBe(path.join('/models/stt', 'turbo'));
213
214
  expect(host.reqs[0]!.language).toBe('pl');
214
215
  });
215
216
 
package/src/local-stt.ts CHANGED
@@ -2,12 +2,12 @@
2
2
  * The `local-whisper` Transcriber: fully local, on-device speech-to-text.
3
3
  *
4
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
5
+ * Whisper files (once, via @moxxy/model-fetch), then decodes the inbound audio
6
6
  * to Float32 mono @ 16 kHz IN-PROCESS (raw PCM / WAV) or via ffmpeg (compressed
7
7
  * containers) and hands the samples to the sherpa sidecar ({@link HostClient}).
8
8
  * No key, no network at transcription time.
9
9
  *
10
- * Every collaborator is injectable (`ensureModelImpl`, `hostFactory`, `log`,
10
+ * Every collaborator is injectable (`ensureModelFilesImpl`, `hostFactory`, `log`,
11
11
  * `modelsDir`, `fetchImpl`, `spawnImpl`) so the whole flow is unit-testable
12
12
  * without the native addon, a real download, or ffmpeg.
13
13
  */
@@ -22,18 +22,25 @@ import {
22
22
  type Transcriber,
23
23
  type TranscriptionResult,
24
24
  } from '@moxxy/sdk';
25
- import { moxxyPath } from '@moxxy/sdk/server';
26
25
  import {
27
- ensureModel,
28
- type EnsureModelProgress,
29
- type EnsureModelResult,
26
+ ensureModelFiles,
27
+ type EnsureModelFilesResult,
30
28
  type FetchLike,
29
+ type ModelFilesProgress,
31
30
  } from '@moxxy/model-fetch';
32
31
 
33
32
  import { TARGET_SAMPLE_RATE } from './audio.js';
34
33
  import { decodeToMono16k } from './decode.js';
35
34
  import { HostClient, type HostClientLike } from './host-client.js';
36
- import { DEFAULT_MODEL_ID, requireModel, type WhisperModelEntry } from './models.js';
35
+ import {
36
+ DEFAULT_MODEL_ID,
37
+ defaultModelsDir,
38
+ modelBytes,
39
+ modelDir,
40
+ modelFiles,
41
+ requireModel,
42
+ type WhisperModelEntry,
43
+ } from './models.js';
37
44
  import { resolveSherpaLibDir, sherpaEnv, sherpaPlatformPackage } from './platform.js';
38
45
 
39
46
  /** The single registered transcriber name. */
@@ -49,7 +56,7 @@ export interface WhisperModelPaths {
49
56
  }
50
57
 
51
58
  export interface LocalWhisperOptions {
52
- /** Model id: `tiny` | `base` | `small`. Default `base` (`small` for Polish). */
59
+ /** Model id. The only one is `turbo`, also the default. */
53
60
  readonly model?: string;
54
61
  /** Default Whisper language tag (BCP-47-ish, e.g. `en`, `pl`). Per-call
55
62
  * `TranscribeOptions.language` overrides it; omit to let Whisper auto-detect. */
@@ -63,9 +70,9 @@ export interface LocalWhisperOptions {
63
70
  /** Absolute path to the built sidecar entry. Default: `sidecar.js` beside
64
71
  * this module (i.e. `dist/sidecar.js` in a published build). */
65
72
  readonly sidecarPath?: string;
66
- /** Injected model-ensure (tests). Defaults to @moxxy/model-fetch `ensureModel`. */
67
- readonly ensureModelImpl?: typeof ensureModel;
68
- /** Injected `fetch`, threaded into `ensureModel` (tests). */
73
+ /** Injected model download (tests). Defaults to @moxxy/model-fetch `ensureModelFiles`. */
74
+ readonly ensureModelFilesImpl?: typeof ensureModelFiles;
75
+ /** Injected `fetch`, threaded into the model download (tests). */
69
76
  readonly fetchImpl?: FetchLike;
70
77
  /** Injected sidecar host factory (tests). Defaults to a real {@link HostClient}. */
71
78
  readonly hostFactory?: () => HostClientLike;
@@ -94,7 +101,7 @@ export class LocalWhisperTranscriber implements Transcriber {
94
101
  private readonly modelsDir: string;
95
102
  private readonly provider: string;
96
103
  private readonly sidecarPath: string | undefined;
97
- private readonly ensureModelImpl: typeof ensureModel;
104
+ private readonly ensureModelFilesImpl: typeof ensureModelFiles;
98
105
  private readonly fetchImpl: FetchLike | undefined;
99
106
  private readonly hostFactory: () => HostClientLike;
100
107
  private readonly spawnImpl: typeof spawn | undefined;
@@ -102,7 +109,7 @@ export class LocalWhisperTranscriber implements Transcriber {
102
109
 
103
110
  private host: HostClientLike | null = null;
104
111
  /** In-flight model download promise — de-dupes concurrent first-use. */
105
- private ensuring: Promise<EnsureModelResult> | null = null;
112
+ private ensuring: Promise<EnsureModelFilesResult> | null = null;
106
113
 
107
114
  constructor(opts: LocalWhisperOptions = {}) {
108
115
  // Validate the configured model up front so a bad config fails clearly at
@@ -113,10 +120,10 @@ export class LocalWhisperTranscriber implements Transcriber {
113
120
  opts.numThreads && Number.isInteger(opts.numThreads) && opts.numThreads > 0
114
121
  ? opts.numThreads
115
122
  : DEFAULT_NUM_THREADS;
116
- this.modelsDir = opts.modelsDir ?? moxxyPath('models', 'stt');
123
+ this.modelsDir = opts.modelsDir ?? defaultModelsDir();
117
124
  this.provider = opts.provider ?? 'cpu';
118
125
  this.sidecarPath = opts.sidecarPath;
119
- this.ensureModelImpl = opts.ensureModelImpl ?? ensureModel;
126
+ this.ensureModelFilesImpl = opts.ensureModelFilesImpl ?? ensureModelFiles;
120
127
  this.fetchImpl = opts.fetchImpl;
121
128
  this.hostFactory = opts.hostFactory ?? (() => this.defaultHost());
122
129
  this.spawnImpl = opts.spawnImpl;
@@ -184,14 +191,13 @@ export class LocalWhisperTranscriber implements Transcriber {
184
191
  return this.host;
185
192
  }
186
193
 
187
- /** Resolve on-disk paths for the model, downloading + extracting it once. */
194
+ /** Resolve on-disk paths for the model, downloading its files once. */
188
195
  private async ensureModelFiles(signal?: AbortSignal): Promise<WhisperModelPaths> {
189
- const dir = path.join(this.modelsDir, this.model.id);
196
+ const dir = modelDir(this.model, this.modelsDir);
190
197
  let inflight = this.ensuring;
191
198
  if (!inflight) {
192
- inflight = this.ensureModelImpl({
193
- url: this.model.url,
194
- sha256: this.model.sha256,
199
+ inflight = this.ensureModelFilesImpl({
200
+ files: modelFiles(this.model),
195
201
  dir,
196
202
  ...(this.fetchImpl ? { fetchImpl: this.fetchImpl } : {}),
197
203
  ...(signal ? { signal } : {}),
@@ -204,41 +210,29 @@ export class LocalWhisperTranscriber implements Transcriber {
204
210
  });
205
211
  }
206
212
  await inflight;
207
- const root = path.join(dir, this.model.archiveRootDir);
208
213
  return {
209
- encoder: path.join(root, this.model.encoderFile),
210
- decoder: path.join(root, this.model.decoderFile),
211
- tokens: path.join(root, this.model.tokensFile),
214
+ encoder: path.join(dir, this.model.encoder.name),
215
+ decoder: path.join(dir, this.model.decoder.name),
216
+ tokens: path.join(dir, this.model.tokens.name),
212
217
  };
213
218
  }
214
219
 
215
220
  /** A progress callback that logs a single start line then ~every-10% ticks,
216
- * and an extraction line — but stays silent when the model is already
217
- * present (ensureModel emits only `done`). */
218
- private makeProgressLogger(): (p: EnsureModelProgress) => void {
221
+ * but stays silent when the model is already present (nothing to fetch). */
222
+ private makeProgressLogger(): (p: ModelFilesProgress) => void {
219
223
  let started = false;
220
- let extracting = false;
221
224
  let lastPct = -1;
222
225
  return (p) => {
223
- if (p.phase === 'downloading') {
224
- if (!started) {
225
- started = true;
226
- this.log(
227
- `stt-local: first use — downloading Whisper ${this.model.id} (~${this.model.approxMb} MB) to ${this.modelsDir}, one-time`,
228
- );
229
- }
230
- if (p.totalBytes > 0) {
231
- const pct = Math.floor((p.receivedBytes / p.totalBytes) * 100);
232
- if (pct >= lastPct + 10) {
233
- lastPct = pct - (pct % 10);
234
- this.log(`stt-local: downloading ${this.model.id} … ${lastPct}%`);
235
- }
236
- }
237
- } else if (p.phase === 'extracting' && !extracting) {
238
- extracting = true;
239
- this.log(`stt-local: extracting ${this.model.id} …`);
240
- } else if (p.phase === 'done' && started) {
241
- this.log(`stt-local: ${this.model.id} ready.`);
226
+ const pct = p.totalBytes > 0 ? Math.floor((p.receivedBytes / p.totalBytes) * 100) : 0;
227
+ if (!started) {
228
+ if (pct >= 100) return;
229
+ started = true;
230
+ const mb = Math.round(modelBytes(this.model) / 1_000_000);
231
+ this.log(`stt-local: first use — downloading Whisper ${this.model.id} (~${mb} MB) to ${this.modelsDir}, one-time`);
232
+ }
233
+ if (pct >= lastPct + 10) {
234
+ lastPct = pct - (pct % 10);
235
+ this.log(lastPct >= 100 ? `stt-local: ${this.model.id} ready.` : `stt-local: downloading ${this.model.id} … ${lastPct}%`);
242
236
  }
243
237
  };
244
238
  }
@@ -1,58 +1,79 @@
1
+ import path from 'node:path';
2
+
1
3
  import { describe, expect, it } from 'vitest';
2
4
  import {
3
5
  DEFAULT_MODEL_ID,
4
- MODEL_CATALOG,
5
6
  findModel,
7
+ modelBytes,
8
+ modelDir,
9
+ modelFiles,
6
10
  modelIds,
7
11
  requireModel,
8
12
  } from './models.js';
9
13
 
10
14
  describe('MODEL_CATALOG', () => {
11
- it('pins tiny/base/small multilingual models with their release URLs + hashes', () => {
12
- expect(modelIds()).toEqual(['tiny', 'base', 'small']);
13
- for (const m of MODEL_CATALOG) {
14
- expect(m.url).toBe(
15
- `https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-whisper-${m.id}.tar.bz2`,
16
- );
17
- expect(m.archiveRootDir).toBe(`sherpa-onnx-whisper-${m.id}`);
18
- expect(m.encoderFile).toBe(`${m.id}-encoder.onnx`);
19
- expect(m.decoderFile).toBe(`${m.id}-decoder.onnx`);
20
- expect(m.tokensFile).toBe(`${m.id}-tokens.txt`);
21
- // 64-hex sha256, lower-case.
22
- expect(m.sha256).toMatch(/^[0-9a-f]{64}$/);
23
- }
15
+ const HF = 'https://huggingface.co/csukuangfj/sherpa-onnx-whisper-turbo/resolve/2ca6ff69fc878651b770880507669577ac41c2ff';
16
+
17
+ it('pins only Whisper turbo, as its three int8 files on a fixed Hugging Face revision', () => {
18
+ expect(modelIds()).toEqual(['turbo']);
19
+ const turbo = findModel('turbo')!;
20
+ expect(turbo.encoder).toEqual({
21
+ name: 'turbo-encoder.int8.onnx',
22
+ url: `${HF}/turbo-encoder.int8.onnx`,
23
+ bytes: 674_716_297,
24
+ sha256: 'b02dcdf54f348741e93fe732b67d933c8dcb6735655f710640143081db38878b',
25
+ });
26
+ expect(turbo.decoder).toEqual({
27
+ name: 'turbo-decoder.int8.onnx',
28
+ url: `${HF}/turbo-decoder.int8.onnx`,
29
+ bytes: 361_080_764,
30
+ sha256: '20accd02388482eb3a46bd615631adfdc85e1eb2c7db9ea3f02a40ffe6b81547',
31
+ });
32
+ expect(turbo.tokens).toEqual({
33
+ name: 'turbo-tokens.txt',
34
+ url: `${HF}/turbo-tokens.txt`,
35
+ bytes: 816_730,
36
+ sha256: 'b34b360dbb493e781e479794586d661700670d65564001f23024971d1f2fa126',
37
+ });
24
38
  });
25
39
 
26
- it('records the scout-verified checksums verbatim', () => {
27
- expect(findModel('tiny')!.sha256).toBe(
28
- 'c46116994e539aa165266d96b325252728429c12535eb9d8b6a2b10f129e66b1',
29
- );
30
- expect(findModel('base')!.sha256).toBe(
31
- '911b2083efd7c0dca2ac3b358b75222660dc09fb716d64fbfc417ba6c99ff3de',
32
- );
33
- expect(findModel('small')!.sha256).toBe(
34
- '486a46afbb7ba798507190ffe02fea2dd726049af212e774537efac6afb210a6',
40
+ it('lists the files of a model and their total size', () => {
41
+ const turbo = findModel('turbo')!;
42
+ expect(modelFiles(turbo).map((file) => file.name)).toEqual([
43
+ 'turbo-encoder.int8.onnx',
44
+ 'turbo-decoder.int8.onnx',
45
+ 'turbo-tokens.txt',
46
+ ]);
47
+ expect(modelBytes(turbo)).toBe(674_716_297 + 361_080_764 + 816_730);
48
+ });
49
+
50
+ it('keeps each model in its own directory under the models root', () => {
51
+ expect(modelDir(findModel('turbo')!, '/home/me/.moxxy/models/stt')).toBe(
52
+ path.join('/home/me/.moxxy/models/stt', 'turbo'),
35
53
  );
36
54
  });
37
55
 
38
- it('defaults to base', () => {
39
- expect(DEFAULT_MODEL_ID).toBe('base');
56
+ it('defaults to turbo', () => {
57
+ expect(DEFAULT_MODEL_ID).toBe('turbo');
40
58
  expect(findModel(DEFAULT_MODEL_ID)).toBeDefined();
41
59
  });
42
60
  });
43
61
 
44
62
  describe('requireModel', () => {
45
63
  it('returns the catalog entry for a known id', () => {
46
- expect(requireModel('small', 'model').id).toBe('small');
64
+ expect(requireModel('turbo', 'model').id).toBe('turbo');
47
65
  });
48
66
 
49
- it('throws CONFIG_INVALID with the valid ids for an unknown model', () => {
50
- expect(() => requireModel('medium', 'model')).toThrow(/Unknown local Whisper model/);
51
- try {
52
- requireModel('medium', 'model');
53
- } catch (err) {
54
- expect((err as { code?: string }).code).toBe('CONFIG_INVALID');
55
- expect((err as { hint?: string }).hint).toContain('tiny, base, small');
67
+ it('rejects the removed tiny/base/small models with CONFIG_INVALID naming turbo', () => {
68
+ for (const removed of ['tiny', 'base', 'small']) {
69
+ try {
70
+ requireModel(removed, 'model');
71
+ expect.unreachable(`${removed} should be rejected`);
72
+ } catch (err) {
73
+ expect((err as { code?: string }).code).toBe('CONFIG_INVALID');
74
+ expect((err as Error).message).toMatch(/Unknown local Whisper model/);
75
+ expect((err as { hint?: string }).hint).toBe('Valid models: turbo.');
76
+ }
56
77
  }
57
78
  });
58
79
  });
package/src/models.ts CHANGED
@@ -1,90 +1,85 @@
1
1
  /**
2
2
  * The pinned Whisper model catalog.
3
3
  *
4
- * Every model is a multilingual sherpa-onnx Whisper export published as a
5
- * `.tar.bz2` under the project's `asr-models` GitHub release. Each `sha256` is
6
- * copied VERBATIM from that release's `checksum.txt` (re-verified 2026-07-05) —
7
- * so a first-use download is content-addressed and tamper-evident, unlike an
8
- * unverified blob fetch. The archive extracts to a single top directory
9
- * (`archiveRootDir`) holding `<id>-encoder.onnx`, `<id>-decoder.onnx` (+ int8
10
- * variants we don't use) and `<id>-tokens.txt`.
4
+ * The one model is Whisper large-v3 turbo, the multilingual sherpa-onnx int8
5
+ * export. Its three files download as they are from Hugging Face, pinned to a
6
+ * fixed revision and checked against their size and sha256 — byte for byte the
7
+ * files the project's `asr-models` GitHub release packs into a `.tar.bz2`
8
+ * (verified 2026-10-24), but with no archive to unpack: unpacking it in
9
+ * JavaScript took about 67 s against 9 s for the download.
11
10
  *
12
- * All three are the MULTILINGUAL models (not the `.en` English-only exports),
13
- * so Polish (and any other Whisper language) works — `small` is the accuracy
14
- * sweet spot for Polish, `base` the balanced default, `tiny` the fastest.
11
+ * Multilingual (not an `.en` export), so Polish and every other Whisper
12
+ * language work.
15
13
  */
16
14
 
15
+ import path from 'node:path';
16
+
17
17
  import { MoxxyError } from '@moxxy/sdk';
18
+ import { moxxyPath } from '@moxxy/sdk/server';
19
+ import type { ModelFile } from '@moxxy/model-fetch/files';
18
20
 
19
- export type WhisperModelId = 'tiny' | 'base' | 'small';
21
+ export type WhisperModelId = 'turbo';
20
22
 
21
23
  export interface WhisperModelEntry {
22
- /** Stable model id (also the `model` config value). */
24
+ /** Stable model id (also the `model` config value and its directory name). */
23
25
  readonly id: WhisperModelId;
24
26
  /** Human label for UI surfaces / the first-use notice. */
25
27
  readonly label: string;
26
- /** Pinned archive URL (sherpa-onnx GitHub `asr-models` release). */
27
- readonly url: string;
28
- /** Pinned hex sha256 of the archive (from the release checksum.txt). */
29
- readonly sha256: string;
30
- /** Top directory the archive extracts to (`sherpa-onnx-whisper-<id>`). */
31
- readonly archiveRootDir: string;
32
- /** Encoder ONNX filename inside `archiveRootDir` (`<id>-encoder.onnx`). */
33
- readonly encoderFile: string;
34
- /** Decoder ONNX filename inside `archiveRootDir` (`<id>-decoder.onnx`). */
35
- readonly decoderFile: string;
36
- /** Tokens filename inside `archiveRootDir` (`<id>-tokens.txt`). */
37
- readonly tokensFile: string;
38
- /** Approximate download size in MB (for the one-time first-use notice). */
39
- readonly approxMb: number;
28
+ readonly encoder: ModelFile;
29
+ readonly decoder: ModelFile;
30
+ readonly tokens: ModelFile;
40
31
  }
41
32
 
42
- const RELEASE_BASE = 'https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models';
43
-
44
- function whisperModel(
45
- id: WhisperModelId,
46
- label: string,
47
- sha256: string,
48
- approxMb: number,
49
- ): WhisperModelEntry {
50
- const archiveRootDir = `sherpa-onnx-whisper-${id}`;
51
- return {
52
- id,
53
- label,
54
- url: `${RELEASE_BASE}/${archiveRootDir}.tar.bz2`,
55
- sha256,
56
- archiveRootDir,
57
- encoderFile: `${id}-encoder.onnx`,
58
- decoderFile: `${id}-decoder.onnx`,
59
- tokensFile: `${id}-tokens.txt`,
60
- approxMb,
61
- };
62
- }
33
+ const TURBO_REVISION = '2ca6ff69fc878651b770880507669577ac41c2ff';
34
+ const TURBO_BASE = `https://huggingface.co/csukuangfj/sherpa-onnx-whisper-turbo/resolve/${TURBO_REVISION}`;
63
35
 
64
36
  export const MODEL_CATALOG: readonly WhisperModelEntry[] = [
65
- whisperModel(
66
- 'tiny',
67
- 'Whisper tiny (multilingual — fastest, lowest accuracy)',
68
- 'c46116994e539aa165266d96b325252728429c12535eb9d8b6a2b10f129e66b1',
69
- 111,
70
- ),
71
- whisperModel(
72
- 'base',
73
- 'Whisper base (multilingual — balanced; default)',
74
- '911b2083efd7c0dca2ac3b358b75222660dc09fb716d64fbfc417ba6c99ff3de',
75
- 198,
76
- ),
77
- whisperModel(
78
- 'small',
79
- 'Whisper small (multilingual — best accuracy; recommended for Polish)',
80
- '486a46afbb7ba798507190ffe02fea2dd726049af212e774537efac6afb210a6',
81
- 610,
82
- ),
37
+ {
38
+ id: 'turbo',
39
+ label: 'Whisper turbo (multilingual, large-v3 turbo)',
40
+ encoder: {
41
+ name: 'turbo-encoder.int8.onnx',
42
+ url: `${TURBO_BASE}/turbo-encoder.int8.onnx`,
43
+ bytes: 674_716_297,
44
+ sha256: 'b02dcdf54f348741e93fe732b67d933c8dcb6735655f710640143081db38878b',
45
+ },
46
+ decoder: {
47
+ name: 'turbo-decoder.int8.onnx',
48
+ url: `${TURBO_BASE}/turbo-decoder.int8.onnx`,
49
+ bytes: 361_080_764,
50
+ sha256: '20accd02388482eb3a46bd615631adfdc85e1eb2c7db9ea3f02a40ffe6b81547',
51
+ },
52
+ tokens: {
53
+ name: 'turbo-tokens.txt',
54
+ url: `${TURBO_BASE}/turbo-tokens.txt`,
55
+ bytes: 816_730,
56
+ sha256: 'b34b360dbb493e781e479794586d661700670d65564001f23024971d1f2fa126',
57
+ },
58
+ },
83
59
  ];
84
60
 
85
- /** Default model when the caller doesn't pick one. `base` balances size vs.
86
- * accuracy; `small` is recommended for Polish (see the catalog description). */
87
- export const DEFAULT_MODEL_ID: WhisperModelId = 'base';
61
+ /** Every file of a model, in download order. */
62
+ export function modelFiles(entry: WhisperModelEntry): ModelFile[] {
63
+ return [entry.encoder, entry.decoder, entry.tokens];
64
+ }
65
+
66
+ /** The model's download size in bytes. */
67
+ export function modelBytes(entry: WhisperModelEntry): number {
68
+ return modelFiles(entry).reduce((sum, file) => sum + file.bytes, 0);
69
+ }
70
+
71
+ /** The models root: `~/.moxxy/models/stt`, MOXXY_HOME-aware. */
72
+ export function defaultModelsDir(): string {
73
+ return moxxyPath('models', 'stt');
74
+ }
75
+
76
+ /** Where a model's files live under the models root. */
77
+ export function modelDir(entry: WhisperModelEntry, modelsDir: string): string {
78
+ return path.join(modelsDir, entry.id);
79
+ }
80
+
81
+ /** The model used when the caller doesn't pick one — the only one there is. */
82
+ export const DEFAULT_MODEL_ID: WhisperModelId = 'turbo';
88
83
 
89
84
  /** All valid model ids, for error messages and validation. */
90
85
  export function modelIds(): string[] {