fix(voice): enforce voice.max_recording_seconds (was dead config)

This commit is contained in:
Solitud1nem 2026-05-29 09:40:41 +00:00 committed by Teknium
parent ee5f20bdf2
commit fd213a82f4
3 changed files with 65 additions and 0 deletions

11
cli.py
View file

@ -11766,6 +11766,17 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
self._voice_recorder._silence_duration = (
_duration if isinstance(_duration, (int, float)) and not isinstance(_duration, bool) else 3.0
)
# voice.max_recording_seconds — hard cap on a single recording's length.
# Same numeric guard as the silence params (bool excluded: a hand-edited
# ``max_recording_seconds: true`` must not become ``1``). <= 0 disables
# the cap. Previously this documented key was never read, so the setting
# had no effect (dead config); wiring it here makes it take effect.
_max_rec = voice_cfg.get("max_recording_seconds")
self._voice_recorder._max_recording_seconds = (
_max_rec
if isinstance(_max_rec, (int, float)) and not isinstance(_max_rec, bool) and _max_rec > 0
else 0.0
)
def _on_silence():
"""Called by AudioRecorder when silence is detected after speech."""

View file

@ -0,0 +1,32 @@
"""Regression test: voice.max_recording_seconds is actually enforced.
Before this fix, ``voice.max_recording_seconds`` was defined in the default
config (hermes_cli/config.py) and documented, but no code read it the
AudioRecorder had no total-length cap, so the setting was dead config. This
test pins the enforcement contract on the recorder:
* cap disabled (0 / unset) never auto-stops on duration (previous behaviour),
* cap set to N>0 auto-stop condition becomes true once elapsed >= N.
"""
from tools.voice_mode import AudioRecorder
def test_cap_disabled_by_default():
r = AudioRecorder()
assert r._max_recording_seconds == 0.0
# No cap → duration trigger never fires, even at absurd elapsed times.
assert r._max_duration_reached(10_000.0) is False
def test_cap_enforced_when_set():
r = AudioRecorder()
r._max_recording_seconds = 120
assert r._max_duration_reached(119.9) is False
assert r._max_duration_reached(120.0) is True
assert r._max_duration_reached(300.0) is True
def test_zero_means_unlimited():
r = AudioRecorder()
r._max_recording_seconds = 0
assert r._max_duration_reached(99_999.0) is False

View file

@ -497,11 +497,25 @@ class AudioRecorder:
self._silence_threshold: int = SILENCE_RMS_THRESHOLD
self._silence_duration: float = SILENCE_DURATION_SECONDS
self._max_wait: float = 15.0 # Max seconds to wait for speech before auto-stop
# Hard cap on total recording length, wired from voice.max_recording_seconds
# by the CLI before each recording. 0 (or unset) = no cap (previous behaviour).
self._max_recording_seconds: float = 0.0
# Peak RMS seen during recording (for speech presence check in stop())
self._peak_rms: int = 0
# Live audio level (read by UI for visual feedback)
self._current_rms: int = 0
def _max_duration_reached(self, elapsed: float) -> bool:
"""Whether the configured hard recording-length cap has elapsed.
``voice.max_recording_seconds`` is applied by the CLI before each
recording (see ``HermesCLI._voice_start_recording``). A value <= 0
(or unset) disables the cap, preserving the previous unbounded
behaviour.
"""
cap = self._max_recording_seconds
return bool(cap and cap > 0 and elapsed >= cap)
# -- public properties ---------------------------------------------------
@property
@ -618,6 +632,14 @@ class AudioRecorder:
self._max_wait)
should_fire = True
# 3. Hard cap on total recording length (voice.max_recording_seconds).
# Independent of speech/silence so a continuous speaker past the
# configured limit still auto-stops instead of recording forever.
if not should_fire and self._max_duration_reached(elapsed):
logger.info("Max recording length reached (%.0fs), auto-stopping",
self._max_recording_seconds)
should_fire = True
if should_fire:
with self._lock:
cb = self._on_silence_stop