diff --git a/linux_voice_assistant/__main__.py b/linux_voice_assistant/__main__.py index 38526d4..1a9b069 100644 --- a/linux_voice_assistant/__main__.py +++ b/linux_voice_assistant/__main__.py @@ -18,7 +18,7 @@ from getmac import get_mac_address # type: ignore from pymicro_wakeword import MicroWakeWord, MicroWakeWordFeatures from pyopen_wakeword import OpenWakeWord, OpenWakeWordFeatures -from .models import Preferences, ServerState, WakeWordType +from .models import Preferences, ServerState, WakeWordType, initial_stop_word_threshold from .mpv_player import MpvMediaPlayer from .peripheral_api import LVAEvent, PeripheralAPIServer from .satellite import VoiceSatelliteProtocol @@ -379,6 +379,10 @@ async def main() -> None: initial_volume = max(0.0, min(1.0, float(initial_volume))) preferences.volume = initial_volume + # Load stop word sensitivity from preferences on startup, and ensure it's between 0.0 and 1.0 + initial_threshold = initial_stop_word_threshold(preferences.stop_word_sensitivity) + preferences.stop_word_sensitivity = initial_threshold + if args.enable_thinking_sound: preferences.thinking_sound = 1 @@ -441,6 +445,7 @@ async def main() -> None: output_only=args.output_only, download_dir=args.download_dir, volume=initial_volume, + stop_word_threshold=initial_threshold, mic_volume=preferences.mic_volume, mic_auto_gain=preferences.mic_auto_gain, mic_noise_suppression=preferences.mic_noise_suppression, diff --git a/linux_voice_assistant/models.py b/linux_voice_assistant/models.py index 85b1673..7586158 100644 --- a/linux_voice_assistant/models.py +++ b/linux_voice_assistant/models.py @@ -265,3 +265,14 @@ class ServerState: self.preferences.mic_volume = volume_int _LOGGER.info("Saving mic_volume %s to %s", volume_int, self.preferences_path) self.save_preferences() + + +def initial_stop_word_threshold(saved_sensitivity: Optional[float]) -> float: + """ + Resolve the stop word probability cutoff to start from, clamped to 0.0-1.0. + :param saved_sensitivity: Value persisted in preferences, or None if it has never been set. + """ + if saved_sensitivity is None: + return ServerState.stop_word_threshold + + return max(0.0, min(1.0, float(saved_sensitivity))) diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index ef10016..974250c 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -282,3 +282,30 @@ class TestPersistMicNoise: with patch.object(state, "save_preferences") as mock_save: state.persist_mic_noise(4.0) mock_save.assert_called_once() + + +# --------------------------------------------------------------------------- +# initial_stop_word_threshold() +# --------------------------------------------------------------------------- + + +class TestInitialStopWordThreshold: + def test_saved_sensitivity_is_restored(self): + from linux_voice_assistant.models import initial_stop_word_threshold + + assert initial_stop_word_threshold(0.9) == pytest.approx(0.9) + + def test_falls_back_to_server_state_default_when_never_set(self): + from linux_voice_assistant.models import ServerState, initial_stop_word_threshold + + assert initial_stop_word_threshold(None) == pytest.approx(ServerState.stop_word_threshold) + + def test_clamped_to_one_when_above(self): + from linux_voice_assistant.models import initial_stop_word_threshold + + assert initial_stop_word_threshold(1.5) == pytest.approx(1.0) + + def test_clamped_to_zero_when_below(self): + from linux_voice_assistant.models import initial_stop_word_threshold + + assert initial_stop_word_threshold(-0.5) == pytest.approx(0.0)