simplify(compat): voice_mode/transcription_common — drop 9 re-exports + the _configured_stop_phrases seam, repoint 3 callers + 4 test files
This commit is contained in:
@@ -454,7 +454,7 @@ class CLIVoiceMixin:
|
||||
# Fail-closed echo guard: playback-phase capture has no echo cancellation, so
|
||||
# a close match for what Hermes just spoke is speaker bleed, not a user turn.
|
||||
if getattr(self, "_voice_barge_phase", None) == "playback":
|
||||
from tools.voice_mode import is_tts_echo
|
||||
from tools.voice_mode_transcript import is_tts_echo
|
||||
if is_tts_echo(transcript, getattr(self, "_voice_last_tts_text", "")):
|
||||
logger.debug(
|
||||
"Dropping playback-phase barge transcript as TTS echo: %r", transcript)
|
||||
@@ -529,7 +529,7 @@ class CLIVoiceMixin:
|
||||
_cprint(f" {_DIM}{self._voice_record_key_label()} to start/stop recording{_RST}")
|
||||
# Spoken-stop hint from voice.stop_phrases (first entry); "" when disabled.
|
||||
try:
|
||||
from tools.voice_mode import voice_stop_hint
|
||||
from tools.voice_mode_transcript import voice_stop_hint
|
||||
_stop_hint = voice_stop_hint()
|
||||
except Exception:
|
||||
_stop_hint = ""
|
||||
|
||||
@@ -1802,10 +1802,12 @@ def test_voice_toggle_on_carries_stop_hint(monkeypatch):
|
||||
monkeypatch.setitem(
|
||||
sys.modules,
|
||||
"tools.voice_mode",
|
||||
types.SimpleNamespace(
|
||||
check_voice_requirements=lambda: {"available": True, "details": ""},
|
||||
voice_stop_hint=lambda: 'Say "halt" to end the voice chat.',
|
||||
),
|
||||
types.SimpleNamespace(check_voice_requirements=lambda: {"available": True, "details": ""}),
|
||||
)
|
||||
monkeypatch.setitem(
|
||||
sys.modules,
|
||||
"tools.voice_mode_transcript",
|
||||
types.SimpleNamespace(voice_stop_hint=lambda: 'Say "halt" to end the voice chat.'),
|
||||
)
|
||||
monkeypatch.setenv("HERMES_VOICE", "0")
|
||||
|
||||
@@ -1817,11 +1819,8 @@ def test_voice_toggle_on_carries_stop_hint(monkeypatch):
|
||||
# Disabled stop phrases → empty hint, clients show nothing.
|
||||
monkeypatch.setitem(
|
||||
sys.modules,
|
||||
"tools.voice_mode",
|
||||
types.SimpleNamespace(
|
||||
check_voice_requirements=lambda: {"available": True, "details": ""},
|
||||
voice_stop_hint=lambda: "",
|
||||
),
|
||||
"tools.voice_mode_transcript",
|
||||
types.SimpleNamespace(voice_stop_hint=lambda: ""),
|
||||
)
|
||||
on_resp = _dispatch_sync(
|
||||
{"id": "voice-on2", "method": "voice.toggle", "params": {"action": "on"}}
|
||||
|
||||
@@ -630,7 +630,7 @@ class TestTypedVoiceStop:
|
||||
# Hermetic: don't let a dev machine's voice.stop_phrases config
|
||||
# change which utterances count as a stop phrase.
|
||||
monkeypatch.setattr(
|
||||
"tools.voice_mode._load_voice_stop_phrases", lambda: ("stop",)
|
||||
"tools.voice_mode_transcript._load_voice_stop_phrases", lambda: ("stop",)
|
||||
)
|
||||
|
||||
def test_typed_stop_ends_voice_chat_when_voice_on(self):
|
||||
|
||||
@@ -13,7 +13,7 @@ from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from tools.voice_mode import (
|
||||
from tools.voice_mode_transcript import (
|
||||
DEFAULT_VOICE_STOP_PHRASES,
|
||||
_load_voice_stop_phrases,
|
||||
is_voice_stop_phrase,
|
||||
@@ -25,12 +25,12 @@ class TestVoiceStopHint:
|
||||
"""The 'Say "stop" to end the voice chat.' hint shown on voice-mode start."""
|
||||
|
||||
def test_default_phrase(self):
|
||||
with patch("tools.voice_mode._load_voice_stop_phrases", return_value=("stop",)):
|
||||
with patch("tools.voice_mode_transcript._load_voice_stop_phrases", return_value=("stop",)):
|
||||
assert voice_stop_hint() == 'Say "stop" to end the voice chat.'
|
||||
|
||||
|
||||
def test_disabled_phrases_show_no_hint(self):
|
||||
with patch("tools.voice_mode._load_voice_stop_phrases", return_value=()):
|
||||
with patch("tools.voice_mode_transcript._load_voice_stop_phrases", return_value=()):
|
||||
assert voice_stop_hint() == ""
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class TestIsVoiceStopPhrase:
|
||||
|
||||
|
||||
def test_uses_config_when_phrases_omitted(self):
|
||||
with patch("tools.voice_mode._load_voice_stop_phrases", return_value=("halt",)):
|
||||
with patch("tools.voice_mode_transcript._load_voice_stop_phrases", return_value=("halt",)):
|
||||
assert is_voice_stop_phrase("halt") is True
|
||||
assert is_voice_stop_phrase("stop") is False
|
||||
|
||||
@@ -185,9 +185,10 @@ class TestStopPhraseSurvivesHallucinationFilter:
|
||||
|
||||
def _transcribe(self, text, phrases):
|
||||
import tools.voice_mode as vm
|
||||
import tools.voice_mode_transcript as vmt
|
||||
|
||||
with patch.object(
|
||||
vm, "_load_voice_stop_phrases", return_value=tuple(phrases)
|
||||
vmt, "_load_voice_stop_phrases", return_value=tuple(phrases)
|
||||
), patch(
|
||||
"tools.transcription_tools.transcribe_audio",
|
||||
return_value={"success": True, "transcript": text},
|
||||
|
||||
@@ -12,7 +12,7 @@ Contract:
|
||||
instead of queuing it as the next user turn.
|
||||
"""
|
||||
|
||||
from tools.voice_mode import is_tts_echo
|
||||
from tools.voice_mode_transcript import is_tts_echo
|
||||
|
||||
|
||||
class TestIsTtsEcho:
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess # noqa: F401 (type annotation only)
|
||||
import subprocess
|
||||
from typing import Any, Dict
|
||||
|
||||
from tools.tts_command_provider import _get_provider_section as _get_stt_section
|
||||
|
||||
+1
-6
@@ -23,12 +23,7 @@ from typing import Any, Callable, Dict, List, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from tools.voice_mode_transcript import ( # noqa: F401 - re-exported; tests patch tools.voice_mode.<name>
|
||||
_voice_config, WHISPER_HALLUCINATIONS, _HALLUCINATION_REPEAT_RE, is_whisper_hallucination,
|
||||
DEFAULT_VOICE_STOP_PHRASES, _load_voice_stop_phrases, is_voice_stop_phrase,
|
||||
DEFAULT_TTS_ECHO_SIMILARITY_THRESHOLD, MIN_FRAGMENT_LENGTH_FOR_ECHO, _normalize_for_echo_compare,
|
||||
is_tts_echo, voice_stop_hint,
|
||||
)
|
||||
from tools.voice_mode_transcript import _voice_config, is_voice_stop_phrase, is_whisper_hallucination
|
||||
from hermes_constants import is_termux as _is_termux_environment
|
||||
|
||||
# ── Recording parameters ──
|
||||
|
||||
@@ -56,19 +56,12 @@ def _load_voice_stop_phrases() -> tuple:
|
||||
return DEFAULT_VOICE_STOP_PHRASES
|
||||
|
||||
|
||||
def _configured_stop_phrases() -> tuple:
|
||||
"""Resolve ``_load_voice_stop_phrases`` through ``tools.voice_mode`` so
|
||||
``patch("tools.voice_mode._load_voice_stop_phrases")`` still takes effect."""
|
||||
from tools import voice_mode as _vm
|
||||
return _vm._load_voice_stop_phrases()
|
||||
|
||||
|
||||
def is_voice_stop_phrase(transcript: str, stop_phrases: Optional[tuple] = None) -> bool:
|
||||
"""True when *transcript* is EXACTLY a configured stop phrase. Deliberately strict: the whole
|
||||
utterance — lowercased, surrounding punctuation stripped — must equal a phrase, so "stop doing
|
||||
that and try again" still reaches the agent. ``voice.stop_phrases: []`` disables."""
|
||||
cleaned = transcript.strip().lower().strip(".,!?;: \t\n\"'") if transcript else ""
|
||||
return bool(cleaned) and cleaned in (_configured_stop_phrases() if stop_phrases is None else stop_phrases)
|
||||
return bool(cleaned) and cleaned in (_load_voice_stop_phrases() if stop_phrases is None else stop_phrases)
|
||||
|
||||
|
||||
# Similarity ratio (difflib.SequenceMatcher) above which a playback-phase barge transcript
|
||||
@@ -113,5 +106,5 @@ def voice_stop_hint() -> str:
|
||||
"""One-line 'Say "stop" to end the voice chat.' hint for voice-mode start, using the first
|
||||
``voice.stop_phrases`` entry ("" when disabled). Every surface announcing voice-mode start
|
||||
(CLI, TUI, desktop) uses this one owner instead of hardcoding the wording."""
|
||||
phrases = _configured_stop_phrases()
|
||||
phrases = _load_voice_stop_phrases()
|
||||
return f'Say "{phrases[0]}" to end the voice chat.' if phrases else ""
|
||||
|
||||
@@ -631,7 +631,7 @@ def _voice_toggle_mode(rid, params: dict) -> dict:
|
||||
if enabled:
|
||||
# Spoken-stop hint for the client; sourced from voice.stop_phrases, empty when disabled.
|
||||
with contextlib.suppress(Exception):
|
||||
from tools.voice_mode import voice_stop_hint
|
||||
from tools.voice_mode_transcript import voice_stop_hint
|
||||
stop_hint = voice_stop_hint()
|
||||
# Speech output already on → warm the engine now, not on the first reply.
|
||||
if _voice_tts_enabled():
|
||||
|
||||
Reference in New Issue
Block a user