feat(wake): eager-install voice deps with the desktop; wake probes never run pip

Two fixes from live testing (Teknium):

1. Desktop installs now ship the wake/voice stacks up front.
   install.sh + install.ps1 desktop stages run 'uv pip install
   -e .[wake,voice]' (best-effort, lazy-install remains the fallback)
   before building the app, so the first ear-click arms instantly
   instead of sitting through a multi-minute onnxruntime download.
   CLI-only installs keep the lazy path — [all] curation unchanged.

2. The vanished ear: the STT/TTS gate made wake.status call
   check_tts_requirements(), whose edge path runs _import_edge_tts →
   lazy_deps.ensure — a synchronous PIP INSTALL inside a status poll.
   On a venv without edge-tts that blew the desktop's 30s RPC timeout,
   armWakeWord caught the error, the atom never learned enabled=true,
   and the ear unmounted. _tts_ready is now a pure probe: deps missing
   + lazy installs allowed counts as ready (installs at first speak)
   WITHOUT touching pip; check_tts_requirements only runs once deps
   are present. Regression test asserts the probe never calls it while
   deps are missing.
This commit is contained in:
Teknium
2026-07-27 19:30:39 -07:00
parent 46faa4f639
commit a832139ba3
6 changed files with 137 additions and 8 deletions
+32 -3
View File
@@ -519,12 +519,41 @@ def _stt_ready() -> bool:
def _tts_ready() -> bool:
"""Can the configured text-to-speech provider actually run?
"""Can the configured text-to-speech provider run (or install at first use)?
The wake flow is fully hands-free (wake → speak → hear the reply); without
TTS the reply is silent and the loop is pointless. Mirrors /voice's use of
``check_tts_requirements``.
TTS the reply is silent and the loop is pointless.
PROBE, not an installer: ``check_tts_requirements`` lazily pip-installs the
provider SDK via ``_import_*`` → ``lazy_deps.ensure`` — running that inside
a status poll froze wake.status for the length of a pip install (and a
failed install marked the wake word unavailable, unmounting the desktop
ear). When the provider's deps aren't installed yet, "installable at first
use" counts as ready and we never touch pip from here.
"""
try:
from tools.tts_tool import _get_provider, _load_tts_config
provider = _get_provider(_load_tts_config())
except Exception:
return False
_LAZY_TTS_FEATURES = {
"edge": "tts.edge",
"elevenlabs": "tts.elevenlabs",
"mistral": "tts.mistral",
}
feature = _LAZY_TTS_FEATURES.get(provider)
if feature is not None:
try:
from tools import lazy_deps
if not lazy_deps.is_available(feature):
# Not installed: ready iff it can install at first speak.
return lazy_deps._allow_lazy_installs()
except Exception:
return False
try:
from tools.tts_tool import check_tts_requirements