@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 +2 -2
- package/SKILL.md +2 -2
- package/package.json +2 -2
- package/src/engine-install.ts +83 -1
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://
|
|
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,
|
|
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.
|
|
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.
|
|
33
|
+
"version": "1.18.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"test": "bun test",
|
package/src/engine-install.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { dirname, join } from "path";
|
|
2
|
-
import {
|
|
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
|
}
|