@drakulavich/kesha-voice-kit 1.17.1 → 1.18.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/README.md CHANGED
@@ -100,7 +100,7 @@ Each segment gets a `speaker` integer (cluster ID, stable within one file). Linu
100
100
 
101
101
  ## Text-to-speech
102
102
 
103
- Kesha speaks back via Kokoro-82M (English) and Vosk-TTS (Russian) — voice auto-picks from the text's language:
103
+ Kesha speaks back via Kokoro-82M (English) and Vosk-TTS (Russian) — voice auto-picks from the text's language. On darwin-arm64, Kokoro uses FluidAudio CoreML instead of ONNX:
104
104
 
105
105
  ```bash
106
106
  kesha install --tts # ~990MB (Kokoro + Vosk-TTS RU, opt-in)
@@ -158,7 +158,7 @@ See [BENCHMARK.md](BENCHMARK.md) for the full per-file breakdown (Russian + Engl
158
158
  | SpeechBrain ECAPA-TDNN | Audio language detection | ~86MB | [HuggingFace](https://huggingface.co/speechbrain/lang-id-voxlingua107-ecapa) |
159
159
  | Apple NLLanguageRecognizer | Text language detection | built-in | macOS system framework |
160
160
  | Silero VAD v5 (opt-in) | Voice activity detection | ~2.3MB | [snakers4/silero-vad](https://github.com/snakers4/silero-vad) |
161
- | Kokoro-82M / Vosk-TTS (opt-in) | Text-to-speech | ~990MB | [Kokoro](https://huggingface.co/hexgrad/Kokoro-82M) · [Vosk-TTS](https://github.com/alphacep/vosk-tts) |
161
+ | Kokoro-82M / Vosk-TTS (opt-in) | Text-to-speech | ~990MB | [FluidAudio Kokoro](https://github.com/FluidInference/FluidAudio) on darwin-arm64; ONNX Kokoro elsewhere · [Vosk-TTS](https://github.com/alphacep/vosk-tts) |
162
162
 
163
163
  All models run through `kesha-engine` — a Rust binary using [FluidAudio](https://github.com/FluidInference/FluidAudio) (CoreML) on Apple Silicon and [ort](https://github.com/pykeio/ort) (ONNX Runtime) on other platforms.
164
164
 
package/SKILL.md CHANGED
@@ -22,7 +22,7 @@ Local voice toolkit: transcribe voice messages to text, synthesize speech, detec
22
22
  ## When to use
23
23
 
24
24
  - **Voice memo arrived** (Telegram, WhatsApp, Slack, Signal .ogg/.opus/.m4a): transcribe with `kesha --json <path>` and branch on the detected language.
25
- - **Need to reply with audio (file playback)**: synthesize with `kesha say "<text>" > reply.wav`. Auto-routes by detected language (Kokoro-82M for English, Vosk-TTS for Russian). For other languages and ~180 more voices use `--voice macos-*` on macOS (zero model download).
25
+ - **Need to reply with audio (file playback)**: synthesize with `kesha say "<text>" > reply.wav`. Auto-routes by detected language (Kokoro-82M for English, Vosk-TTS for Russian). On darwin-arm64, English Kokoro uses FluidAudio CoreML instead of ONNX. For other languages and ~180 more voices use `--voice macos-*` on macOS (zero model download).
26
26
  - **Need to send a voice note (Telegram, WhatsApp, Signal, Discord)**: synthesize directly into the messenger-native format with `kesha say "<text>" --format ogg-opus --out reply.ogg`. Default is mono 24 kHz @ 32 kbps — what Telegram `sendVoice` expects. No `ffmpeg` round-trip needed.
27
27
  - **Need to detect what language a file is in** before choosing a pipeline: `kesha --json audio.ogg` returns both audio-based and text-based language detection with confidence scores.
28
28
 
@@ -153,7 +153,7 @@ No system deps — English G2P is embedded (`misaki-rs`); Russian G2P is bundled
153
153
 
154
154
  **Speech-to-text (25):** Bulgarian, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hungarian, Italian, Latvian, Lithuanian, Maltese, Polish, Portuguese, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish, Ukrainian.
155
155
 
156
- **Text-to-speech:** English (Kokoro-82M, ~70 voices), Russian (Vosk-TTS, 5 baked-in speakers — default `ru-vosk-m02`), plus any macOS system voice via `--voice macos-*`.
156
+ **Text-to-speech:** English (Kokoro-82M; FluidAudio CoreML on darwin-arm64, ONNX elsewhere), Russian (Vosk-TTS, 5 baked-in speakers — default `ru-vosk-m02`), plus any macOS system voice via `--voice macos-*`.
157
157
 
158
158
  ## Performance
159
159
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drakulavich/kesha-voice-kit",
3
- "version": "1.17.1",
3
+ "version": "1.18.0",
4
4
  "description": "Open-source voice toolkit. Speech-to-text (25 langs, ~19x faster than Whisper on Apple Silicon, ONNX fallback on CPU), text-to-speech (Kokoro + Vosk-TTS + ~180 macOS system voices, SSML), voice-activity detection, language detection (107 langs). Rust engine, OpenClaw skill.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,7 @@
30
30
  ]
31
31
  },
32
32
  "keshaEngine": {
33
- "version": "1.17.0"
33
+ "version": "1.18.0"
34
34
  },
35
35
  "scripts": {
36
36
  "test": "bun test",
@@ -1,5 +1,6 @@
1
1
  import { dirname, join } from "path";
2
- import { existsSync, mkdirSync, chmodSync, accessSync, constants } from "fs";
2
+ import { tmpdir } from "os";
3
+ import { existsSync, mkdirSync, chmodSync, accessSync, constants, rmSync } from "fs";
3
4
  import {
4
5
  getEngineBinPath,
5
6
  getEngineCapabilities,
@@ -74,6 +75,13 @@ const SIDECARS: SidecarSpec[] = [
74
75
  availableHint: "--speakers available",
75
76
  unavailableHint: "--speakers unavailable",
76
77
  },
78
+ {
79
+ fileBasename: "kesha-kokoro",
80
+ assetName: "kesha-kokoro-darwin-arm64",
81
+ displayName: "FluidAudio Kokoro sidecar",
82
+ availableHint: "Kokoro uses FluidAudio CoreML",
83
+ unavailableHint: "en-* Kokoro voices unavailable on Darwin",
84
+ },
77
85
  {
78
86
  // Runtime resolver looks for plain `kesha-textlang` next to the engine
79
87
  // (see `rust/src/text_lang.rs::helper_path`), not the platform-suffixed
@@ -221,6 +229,76 @@ async function downloadSidecar(
221
229
  }
222
230
  }
223
231
 
232
+ async function warmDarwinKokoro(binPath: string): Promise<void> {
233
+ if (process.platform !== "darwin" || process.arch !== "arm64") return;
234
+ const sidecarPath = join(dirname(binPath), "kesha-kokoro");
235
+ if (!existsSync(sidecarPath)) return;
236
+
237
+ const outPath = join(tmpdir(), `kesha-kokoro-warmup-${process.pid}.wav`);
238
+ log.progress("Warming FluidAudio Kokoro CoreML cache...");
239
+
240
+ const startedAt = performance.now();
241
+ const proc = Bun.spawn(
242
+ [
243
+ binPath,
244
+ "say",
245
+ "--voice",
246
+ "en-am_michael",
247
+ "--out",
248
+ outPath,
249
+ "Kesha warmup.",
250
+ ],
251
+ {
252
+ stdout: "ignore",
253
+ stderr: "pipe",
254
+ },
255
+ );
256
+
257
+ let timedOut = false;
258
+ const timer = setTimeout(() => {
259
+ timedOut = true;
260
+ proc.kill();
261
+ }, 180_000);
262
+
263
+ let stderr = "";
264
+ try {
265
+ const stderrStream = proc.stderr as ReadableStream<Uint8Array>;
266
+ const [stderrText, exitCode] = await Promise.all([
267
+ new Response(stderrStream).text(),
268
+ proc.exited,
269
+ ]);
270
+ stderr = stderrText.trim();
271
+
272
+ if (timedOut) {
273
+ log.warn("FluidAudio Kokoro warmup timed out; first `kesha say en-*` may still be slow.");
274
+ return;
275
+ }
276
+ if (exitCode !== 0) {
277
+ log.warn(
278
+ `FluidAudio Kokoro warmup failed${stderr ? `: ${stderr}` : ""}; first ` +
279
+ "`kesha say en-*` may still be slow.",
280
+ );
281
+ return;
282
+ }
283
+
284
+ log.success(
285
+ `FluidAudio Kokoro warmed (${Math.round(performance.now() - startedAt)}ms).`,
286
+ );
287
+ } catch (e) {
288
+ log.warn(
289
+ `FluidAudio Kokoro warmup failed (${e instanceof Error ? e.message : e}); ` +
290
+ "first `kesha say en-*` may still be slow.",
291
+ );
292
+ } finally {
293
+ clearTimeout(timer);
294
+ try {
295
+ rmSync(outPath, { force: true });
296
+ } catch {
297
+ // best-effort cleanup only
298
+ }
299
+ }
300
+ }
301
+
224
302
  export interface InstallOptions {
225
303
  /** Also install Kokoro + Vosk-TTS models. */
226
304
  tts?: boolean;
@@ -427,6 +505,10 @@ export async function downloadEngine(
427
505
  throw new Error(detail ? `Failed to install models: ${detail}` : "Failed to install models");
428
506
  }
429
507
 
508
+ if (options.tts) {
509
+ await warmDarwinKokoro(binPath);
510
+ }
511
+
430
512
  log.success("Backend installed successfully.");
431
513
  return binPath;
432
514
  }