Skip to content

feat(voice): make beep notification volume configurable in config.yaml - #56116

Closed
Kewe63 wants to merge 1 commit into
NousResearch:mainfrom
Kewe63:issue/55908-configurable-beep-volume
Closed

feat(voice): make beep notification volume configurable in config.yaml#56116
Kewe63 wants to merge 1 commit into
NousResearch:mainfrom
Kewe63:issue/55908-configurable-beep-volume

Conversation

@Kewe63

@Kewe63 Kewe63 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

tools.voice_mode:play_beep() hardcoded the tone amplitude at 0.3 (#55908), so the CLI voice-mode record start/stop beeps were too quiet on low-volume systems / headphones. Users couldn't adjust it without editing source. This PR adds voice.beep_volume to config.yaml with a 0.3 default, preserving existing behavior for unconfigured installs.


Changes

tools/voice_mode.py

  • play_beep() — replaces the literal 0.3 with a dynamic value read from config.
  • Adds _get_beep_volume() next to play_beep() (same module, audio-cue section):
    • Returns 0.3 when the key is absent — byte-for-byte identical to current behavior.
    • Reads via hermes_cli.config.load_config(), the same pattern already used by cli.py:_voice_beeps_enabled() (L10986-10995) and hermes_cli/voice.py:_beeps_enabled() (L247-257).
    • Lazy-imports load_config inside the function (parity with the two existing call sites) so a broken ~/.hermes/config.yaml can never break module import.
    • Clamps 0.0–1.0; falls back to default on out-of-range, NaN, non-numeric, bool, or load_config exception. The bool guard mirrors the long-standing isinstance(_threshold, (int, float)) and not isinstance(_threshold, bool) pattern at cli.py:10764-10765 for silence_threshold.

hermes_cli/config.py_DEFAULT_CONFIG_SCHEMA

  • Adds one line in the voice: section default, adjacent to beep_enabled (L2024):
"beep_enabled": True,
"beep_volume": 0.3,    # Beep amplitude multiplier (0.0-1.0)

tests/tools/test_voice_mode.py — 2 new test classes:

  • TestGetBeepVolume (12 cases): default-when-missing, default-when-section-missing, custom value, boundary 0.0 / 1.0, out-of-range high/low, numeric string coercion, non-numeric fallback, bool fallback, NaN fallback, exception fallback, wrong-type-voice-section fallback.
  • TestPlayBeepVolumeWiring (1 case): inspect.getsource guard against re-introducing a hardcoded 0.3 literal at the amplitude line — regression guard for the original symptom.

website/docs/user-guide/configuration.md

  • Adds one line in the voice: reference block (L1580) with an inline comment matching the other entries' style. Locale translations (zh-Hans, etc.) are intentionally untouched — handled by the regular i18n sync pipeline.

How to Test

# New tests
pytest tests/tools/test_voice_mode.py::TestGetBeepVolume -v
# ✅ 12/12 passed

pytest tests/tools/test_voice_mode.py::TestPlayBeepVolumeWiring -v
# ✅ 1/1 passed

# Full voice test suites
pytest tests/tools/test_voice_mode.py
# ✅ 84/87 (3 pre-existing Docker+PipeWire env-detection failures on main, unrelated)

pytest tests/tools/test_voice_cli_integration.py
# ✅ 84/84 passed

pytest tests/hermes_cli/test_voice_wrapper.py
# ✅ 44/44 passed

# Config drift / validation
pytest tests/hermes_cli/test_config_drift.py tests/hermes_cli/test_config_validation.py tests/tools/test_config_null_guard.py
# ✅ 32/32 passed

# Lint
ruff check tools/voice_mode.py hermes_cli/config.py tests/tools/test_voice_mode.py
# ✅ All checks passed

User Migration

No action required. Users who haven't configured voice.beep_volume keep the existing 0.3 amplitude byte-for-byte. To make the beep louder:

# ~/.hermes/config.yaml
voice:
  beep_volume: 0.6   # any value in 0.0–1.0

Out-of-range values silently use the default — a typo cannot brick the voice loop.


Checklist

  • Tests pass — 13/13 new, 84/87 full file (3 pre-existing unrelated failures)
  • ruff check — PASS, 0 warnings
  • Follows Conventional Commits
  • Changes scoped to this feature only — 4 files (+136/-1)
  • No new public API, no new env vars — config key per AGENTS.md "config over env vars" rule

Risk & Impact

None. No behavior change without an opt-in voice.beep_volume key in config. All fallback paths degrade to the existing 0.3 default. A bad config value can't break the voice loop.

Type: ✨ New feature
Closes: #55908

Closes NousResearch#55908. The CLI voice-mode beep amplitude is hardcoded at 0.3 inside
tools.voice_mode:play_beep(), which makes the record start/stop cues too
quiet on low-volume systems and headphones. Users couldn't adjust it
without editing source.

Move the literal into a configurable voice.beep_volume setting (clamped to
0.0-1.0, default 0.3 to preserve prior behaviour). The new
_get_beep_volume() helper reads via the same load_config() pattern used by
cli.py's _voice_beeps_enabled() and hermes_cli/voice.py's _beeps_enabled(),
keeps bools / out-of-range / non-numeric / NaN values safely on the default,
and falls back silently if config can't load so the audio cue never breaks
the voice loop on a degenerate config.yaml.

Covered by tests/tools/test_voice_mode.py:
- TestGetBeepVolume (12 cases: missing key, custom value, boundary 0.0/1.0,
  out-of-range clamp, type coercion, bool guard, NaN guard, exception
  guard, dict-typed voice section)
- TestPlayBeepVolumeWiring (guards against re-introducing a hardcoded 0.3
  literal in play_beep)

Docs: website/docs/user-guide/configuration.md mentions the new key.
Other locale translations (zh-Hans etc.) intentionally untouched —
handled by the regular i18n sync pipeline as a separate change.

No change in default behaviour: existing users hear exactly the same beep.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have tool/tts Text-to-speech and transcription comp/cli CLI entry point, hermes_cli/, setup wizard labels Jul 1, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused configuration improvement. The underlying issue is present on current main: tools/voice_mode.py:316 still hardcodes the cue amplitude as tone * 0.3 * 32767, and both the classic CLI (cli.py:11216, cli.py:11271) and the wrapper (hermes_cli/voice.py:271-273) route through this helper.

Problems

  • The dedicated voice-mode guide remains incomplete. It says only the silence settings and voice.beep_enabled are configurable at website/docs/user-guide/features/voice-mode.md:158, and its YAML reference at website/docs/user-guide/features/voice-mode.md:386-394 omits the new setting.
  • The new wiring coverage is source-text inspection rather than audio behavior. tests/tools/test_voice_mode.py:979-997 already captures the generated audio through mock_sd; use that path to prove a configured volume affects output.

Suggested changes

  • Update both voice-mode documentation locations with voice.beep_volume.
  • Add a behavioral amplitude assertion using the existing mocked sounddevice fixture.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 15, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Merged into main via consolidated salvage PR #73520 (merge e04c2a9ebd). Your configurable voice.beep_volume was cherry-picked with your authorship.

Your contribution is credited to you in git history. Thank you! Closing this PR as merged-via-salvage.

@teknium1 teknium1 closed this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades tool/tts Text-to-speech and transcription type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants