fix: move process_loop voice restart to daemon thread, use _cprint consistently
- process_loop's continuous mode restart called _voice_start_recording() directly, blocking the loop if play_beep/sd.wait hangs — queued user input would stall silently. Dispatch to daemon thread like Ctrl+B handler. - Replace print() with _cprint() in _handle_voice_command for consistency with the rest of the voice mode code.
This commit is contained in:
28
cli.py
28
cli.py
@@ -3772,8 +3772,8 @@ class HermesCLI:
|
||||
else:
|
||||
self._enable_voice_mode()
|
||||
else:
|
||||
print(f"Unknown voice subcommand: {subcommand}")
|
||||
print("Usage: /voice [on|off|tts|status]")
|
||||
_cprint(f"Unknown voice subcommand: {subcommand}")
|
||||
_cprint("Usage: /voice [on|off|tts|status]")
|
||||
|
||||
def _enable_voice_mode(self):
|
||||
"""Enable voice mode after checking requirements."""
|
||||
@@ -5602,17 +5602,21 @@ class HermesCLI:
|
||||
self._spinner_text = ""
|
||||
app.invalidate() # Refresh status line
|
||||
|
||||
# Continuous voice: auto-restart recording after agent responds
|
||||
# Continuous voice: auto-restart recording after agent responds.
|
||||
# Dispatch to a daemon thread so play_beep (sd.wait) and
|
||||
# AudioRecorder.start (lock acquire) never block process_loop —
|
||||
# otherwise queued user input would stall silently.
|
||||
if self._voice_mode and self._voice_continuous and not self._voice_recording:
|
||||
try:
|
||||
# Wait for TTS to finish so we don't record the speaker
|
||||
if self._voice_tts:
|
||||
self._voice_tts_done.wait(timeout=60)
|
||||
time.sleep(0.3) # Brief pause after TTS ends
|
||||
self._voice_start_recording()
|
||||
app.invalidate()
|
||||
except Exception as e:
|
||||
_cprint(f"{_DIM}Voice auto-restart failed: {e}{_RST}")
|
||||
def _restart_recording():
|
||||
try:
|
||||
if self._voice_tts:
|
||||
self._voice_tts_done.wait(timeout=60)
|
||||
time.sleep(0.3)
|
||||
self._voice_start_recording()
|
||||
app.invalidate()
|
||||
except Exception as e:
|
||||
_cprint(f"{_DIM}Voice auto-restart failed: {e}{_RST}")
|
||||
threading.Thread(target=_restart_recording, daemon=True).start()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
@@ -875,16 +875,15 @@ class TestHandleVoiceCommandReal:
|
||||
cli._handle_voice_command("/voice")
|
||||
cli._enable_voice_mode.assert_called_once()
|
||||
|
||||
@patch("builtins.print")
|
||||
@patch("cli._cprint")
|
||||
def test_unknown_subcommand(self, _cp, mock_print):
|
||||
def test_unknown_subcommand(self, mock_cp):
|
||||
cli = self._cli()
|
||||
cli._handle_voice_command("/voice foobar")
|
||||
cli._enable_voice_mode.assert_not_called()
|
||||
cli._disable_voice_mode.assert_not_called()
|
||||
# Should print usage via print() (not _cprint)
|
||||
# Should print usage via _cprint
|
||||
assert any("Unknown" in str(c) or "unknown" in str(c)
|
||||
for c in mock_print.call_args_list)
|
||||
for c in mock_cp.call_args_list)
|
||||
|
||||
|
||||
class TestEnableVoiceModeReal:
|
||||
|
||||
Reference in New Issue
Block a user