Commit 30ac7be
authored
feat(runtime): Pocket TTS cloning status fix, mount checks, and env simplification (#117)
Overview
Integrates Pocket TTS as a hotswappable, live-configurable TTS backend
(TTS_BACKEND=pocket_tts), and makes related reliability, observability,
and DX improvements across runtime, frontend, Docker compose, and docs.
Key highlights:
- Pocket TTS wired end-to-end: model loading, generation, voice states,
caching, invalidation, and UI.
- Mount/runtime health checks: /health now reports volumes, paths, and
Pocket TTS status.
- MODEL_DTYPE control: backend-aware dtype selection with bf16/float32
safety.
- TTS_DIAG watchdog: hard timeout on stuck generation (especially
pytorch+bfloat16), with optional bf16→float32 auto-fallback.
- REF_TEXT optional: service starts without a reference transcript;
Whisper auto-drafts it if REF_AUDIO is mounted.
- Unified export target: single compose service exports both Base and
VoiceDesign IR.
- Centralized frontend state: Zustand store for health, swap, runtime
config, and speak-page state.
- Simplified .env.example and ENV_REFERENCE for new users.
Changes
Runtime:
- Added Pocket TTS as a first-class backend (TTS_BACKEND=pocket_tts).
- New src/qwen3_tts/pocket_tts_runtime.py with:
- load_pocket_tts_model, generate_pocket_tts, build_default_voice_state,
get_pocket_tts_voice_state.
- invalidate_voice_state, unload_pocket_tts.
- Post-EOS tail trimming and FRAMES_AFTER_EOS support.
- Caching and fallback for voice_state to avoid redundant re-encoding.
- _run_generate has a dedicated Pocket TTS branch using its own
voice_state resolution.
- TTS_BACKEND is now one of: openvino | pytorch | pocket_tts; switching
is supported via runtime config without restarting.
- Added MODEL_DTYPE:
- openvino: pinned to bfloat16 (user cannot break it).
- pytorch / pocket_tts: configurable (float32 / bfloat16); defaults
float32.
- On backend switch (e.g., openvino → pytorch), dtype is auto-corrected
to avoid unsafe bf16 on CPU.
- Added mount health checks:
- On startup: validates REF_AUDIO, /voices, /segments, HF cache, OV dir,
and /app writability; logs [MOUNT] warnings.
- health_state() now includes a "mount" object reflecting these
statuses.
- Added Pocket TTS health metadata (language, voice_cloning_available,
status message).
- TTS_DIAG unified and hardened:
- TTS_DIAG is no longer OpenVINO-only; applies across backends.
- Hard timeout watchdog on generate_voice_clone:
- Uses a background watchdog thread with periodic logging.
- Fails cleanly with RuntimeError when exceeding TTS_DIAG_GEN_TIMEOUT.
- pytorch+bfloat16:
- Tighter max_new_tokens cap (160 by default) to avoid hung decode.
- Optional auto-fallback to float32
(TTS_DIAG_BF16_AUTO_FALLBACK=1/true), now opt-in only.
- Added "mounted reference" as a first-class voice:
- On startup, ensure_mounted_ref_voice registers REF_AUDIO as voice
vd_000000000001.
- Idempotent by SHA256; marked "Mounted" in VoiceSelector.
- HF_TOKEN handling:
- Removed from live config (no longer editable via RuntimeConfigPage).
- Config precedence: HF_TOKEN env → HF_TOKEN_FILE → /app/.hf_token.
- Missing or empty token file no longer raises; treated as not-set.
- ASR and REF_TEXT:
- Graceful handling when faster_whisper is not installed: logs a warning
instead of crashing.
- REF_TEXT is now optional. When REF_AUDIO is mounted but REF_TEXT is
unset, Whisper auto-drafts a transcript (REF_TEXT_AUTO=whisper).
- New transcribe_reference_audio() in asr_check.py for bootstrap
transcription when no expected_text.
- Runtime persists sample_text_source (env/whisper/user/unused/none) and
ASR metadata on the mounted reference voice.
- Voice library tracks per-voice needs_review and asr fields; UI shows
transcript review status.
Docker / Compose:
- Unified export service:
- Export can now run both Base and VoiceDesign in one pass
(EXPORT_TARGET=both).
- Removed separate export-voice-design service from compose.yml.
- compose.yml:
- Added healthcheck to qwen3-tts service.
- Set LOW_RAM_MODE=1 and FRONTEND_ENABLED=1 as recommended defaults.
- REF_AUDIO_PATH and REF_TEXT no longer required; REF_TEXT_AUTO=whisper
defaults when REF_AUDIO is present.
- Simplified comments and layout.
- .env.example:
- Greatly simplified for new users; REF_AUDIO_PATH and REF_TEXT marked
optional.
Frontend:
- Integrated Pocket TTS into:
- RuntimeConfigPage: pocket_tts backend option + live tunable knobs
(temperature, LSD decode steps, EOS threshold, noise clamp, frames after
EOS).
- SpeakPage: uses centralized Zustand speak state (speakAudioUrl,
speakIsGenerating, etc.).
- AppShell: one-time fetch to initialize runtime config + Pocket TTS
status.
- HealthStatusBanner: fixed clipping and improved truncation; now
mounted in AppShell.
- New PocketTTSWarningBanner:
- Shows when backend is pocket_tts but voice cloning is unavailable
until the user accepts the Hugging Face license.
- Centralized Zustand store:
- Health, swap, and runtime config moved from per-page hooks into
store.ts.
- useSwapStatus now consumes store.swapInProgress instead of doing its
own polling.
- VoiceSelector:
- Labels mounted reference voice with a "Mounted" badge.
- Adds "Review" badge for voices needing transcript review.
- VoiceLibraryPage:
- Shows transcript source badge (Whisper draft, User edited, Startup
override).
- Displays amber review callout when ASR flags issues.
- Shows Whisper draft transcript when it differs from the stored
sample_text.
Docs:
- New docs/architecture/pocket_tts_integration.md with integration
design.
- New docs/plans for voice manipulation, speak tab enhancements, and TTS
audio style pipeline.
- ENV_REFERENCE.md simplified; REF_AUDIO/REF_TEXT marked optional;
REF_TEXT_AUTO documented.
- README.md updated with Pocket TTS and simplified setup guidance.
- HOW_TO_RUN and FRONTEND_OVERVIEW updated to reflect new defaults and
pocket_tts backend.
Tests:
- New tests/tier1_unit/test_pocket_tts_runtime.py:
- Covers load, generate, voice_state resolution, cache invalidation,
unload, and HF gate error handling.
- New tests/tier1_unit/test_run_generate.py:
- Regression test for _run_generate non-TTS_DIAG path (catches
watchdog/local-scope regressions).
- Updated test_model_config.py:
- Aligned with new MODEL_DTYPE behavior and HF_TOKEN graceful
degradation.
New / relevant env vars
Key new or changed:
- TTS_BACKEND: now accepts pocket_tts in addition to openvino / pytorch.
- MODEL_DTYPE: float32 | bfloat16; controls Torch dtype for
pytorch/pocket_tts (pinned to bf16 for openvino).
- TTS_DIAG_BF16_AUTO_FALLBACK: 1 | true to enable auto bf16→float32
fallback on pytorch timeout.
- TTS_DIAG_GEN_TIMEOUT: seconds before watchdog kills stuck generation
(default 180; 120 for pytorch+bf16).
- TTS_SYSTEM_MAX_NEW_TOKENS_PYTORCH_BF16: cap for max_new_tokens when
pytorch+bf16 (default 160).
- POCKET_TTS_TEMP, POCKET_TTS_LSD_DECODE_STEPS,
POCKET_TTS_EOS_THRESHOLD, POCKET_TTS_NOISE_CLAMP,
POCKET_TTS_FRAMES_AFTER_EOS: live-tunable Pocket TTS knobs via runtime
config.
- EXPORT_TARGET: new allowed value both (exports Base + VoiceDesign in
one run).
- REF_TEXT_AUTO: whisper (default) | 0; auto-drafts REF_TEXT from
REF_AUDIO when unset.
Testing / validation
Validated (branch locally, on dockermisc1):
- Docker build: single image builds with pocket-tts==2.1.0 included.
- /health:
- Reports mount statuses, pocket_tts block (when applicable), and
runtime config.
- TTS_BACKEND=pocket_tts:
- Loads Pocket TTS model (with valid HF_TOKEN).
- Generates speech; respects temperature, EOS threshold, etc.
- Voice library and mounted reference voice work as expected.
- invalidate_voice_state clears cached state on voice deletion.
- TTS_BACKEND=openvino and pytorch:
- Existing behavior preserved; no regressions in generation, health,
idle unload, or runtime config.
- MODEL_DTYPE:
- openvino: always bf16.
- pytorch/pocket_tts: honors MODEL_DTYPE, safe defaults on backend
switch.
- TTS_DIAG watchdog:
- On long-running or stuck generation with TTS_DIAG=1, watchdog logs
progress and fails with timeout instead of hanging.
- Frontend:
- Pocket TTS appears in RuntimeConfigPage backend dropdown.
- SpeakPage, VoiceLibraryPage, HealthStatusBanner, and RuntimeConfigPage
render without errors.
Notes
- Pocket TTS currently lives under TTS_BACKEND=pocket_tts; swapping
backends at runtime is supported but is considered advanced.
- Pocket TTS voice cloning requires accepting terms at
https://huggingface.co/kyutai/pocket-tts with the HF account used by the
container.
- The bf16 auto-fallback (TTS_DIAG_BF16_AUTO_FALLBACK) is opt-in and
intentionally conservative; it mutates global state mid-request and is
not for casual use.
BEGIN_COMMIT_OVERRIDE
feat(runtime): integrate Pocket TTS as hotswappable backend with
generation, voice states, and live knobs
feat(runtime): add MODEL_DTYPE control with backend-aware safety and
bf16→float32 auto-correction on swap
feat(runtime): add mount health checks (REF_AUDIO, /voices, /segments,
HF cache, OV dir) and /health mount/pocket_tts reporting
feat(runtime): unify TTS_DIAG across backends and add watchdog with hard
timeout; tighter pytorch+bf16 token cap; opt-in bf16→float32
auto-fallback
feat(runtime): register mounted REF_AUDIO as first-class "Mounted
reference" voice; show "Mounted" badge in VoiceSelector
fix(runtime): gracefully handle missing faster_whisper instead of
crashing at import
fix(runtime): gate bf16→float32 auto-fallback and cache fallback
voice_state
feat(docker): unified export service with EXPORT_TARGET=both for Base +
VoiceDesign; simplified compose.yml and .env.example
feat(frontend): add Pocket TTS options and tuning controls to
RuntimeConfigPage; PocketTTSWarningBanner for cloning unavailability
refactor(frontend): centralize health, swap, runtime config, and
speak-page state in Zustand store; refactor SpeakPage and useSwapStatus
docs: simplify ENV_REFERENCE and .env.example for new users; add
pocket_tts_integration architecture doc
test: add test_pocket_tts_runtime and test_run_generate for
watchdog/watchdog-regression and Pocket TTS behaviors
feat(frontend)(runtime)(docs): make REF_TEXT optional via Whisper
auto-draft and add transcript review status
END_COMMIT_OVERRIDE1 parent 06fd976 commit 30ac7be
35 files changed
Lines changed: 4174 additions & 713 deletions
File tree
- docs
- architecture
- plans
- frontend/src
- components
- hooks
- lib
- pages
- requirements
- scripts
- src/qwen3_tts
- tests/tier1_unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | | - | |
4 | | - | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
| 1 | + | |
| 2 | + | |
| 3 | + | |
29 | 4 | | |
30 | | - | |
31 | | - | |
32 | | - | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
33 | 11 | | |
34 | | - | |
35 | | - | |
36 | | - | |
| 12 | + | |
| 13 | + | |
37 | 14 | | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
84 | 87 | | |
85 | 88 | | |
86 | 89 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
| 23 | + | |
24 | 24 | | |
25 | | - | |
26 | | - | |
| 25 | + | |
27 | 26 | | |
28 | | - | |
29 | | - | |
30 | | - | |
| 27 | + | |
| 28 | + | |
31 | 29 | | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
| 30 | + | |
36 | 31 | | |
37 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
38 | 36 | | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
| 37 | + | |
48 | 38 | | |
49 | 39 | | |
50 | 40 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
3 | 15 | | |
4 | 16 | | |
5 | | - | |
| 17 | + | |
| 18 | + | |
6 | 19 | | |
7 | 20 | | |
8 | | - | |
9 | | - | |
| 21 | + | |
10 | 22 | | |
11 | 23 | | |
12 | 24 | | |
| |||
15 | 27 | | |
16 | 28 | | |
17 | 29 | | |
18 | | - | |
| 30 | + | |
19 | 31 | | |
20 | 32 | | |
21 | 33 | | |
| |||
26 | 38 | | |
27 | 39 | | |
28 | 40 | | |
| 41 | + | |
29 | 42 | | |
30 | 43 | | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
37 | 55 | | |
38 | 56 | | |
39 | 57 | | |
40 | 58 | | |
41 | 59 | | |
42 | 60 | | |
43 | | - | |
| 61 | + | |
44 | 62 | | |
45 | 63 | | |
46 | 64 | | |
47 | 65 | | |
48 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
49 | 73 | | |
| 74 | + | |
50 | 75 | | |
51 | | - | |
52 | 76 | | |
53 | 77 | | |
54 | 78 | | |
55 | 79 | | |
56 | 80 | | |
57 | 81 | | |
58 | 82 | | |
| 83 | + | |
| 84 | + | |
59 | 85 | | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
| 86 | + | |
80 | 87 | | |
81 | 88 | | |
82 | 89 | | |
83 | | - | |
84 | 90 | | |
| 91 | + | |
| 92 | + | |
85 | 93 | | |
86 | 94 | | |
0 commit comments