Skip to content

fix(discord): guard long voice TTS playback - #60403

Open
bmobytes wants to merge 1 commit into
NousResearch:mainfrom
bmobytes:fix/discord-voice-tts-timeout-t_e75400d5
Open

bmobytes wants to merge 1 commit into
NousResearch:mainfrom
bmobytes:fix/discord-voice-tts-timeout-t_e75400d5

Conversation

@bmobytes

@bmobytes bmobytes commented Jul 7, 2026

Copy link
Copy Markdown

What does this PR do?

Fixes Discord voice-channel TTS readbacks getting cut off by Hermes' hardcoded playback watchdog on longer assistant responses.

This PR does two things:

  • makes the Discord VC playback timeout configurable via discord.voice_playback_timeout_seconds (default: 300s)
  • limits only the spoken companion text for Discord VC replies via discord.voice_reply_max_chars (default: 1200), while preserving the full assistant response as the normal Discord text reply

This is intended for long Discord VC replies where TTS generation succeeds, but playback is stopped by Hermes before the audio finishes.

Related Issue

No issue filed.

Related/overlap: #50533 also addresses configurable Discord voice playback timeouts. This PR additionally clamps the spoken VC companion reply so long text remains available in Discord without forcing the bot to read the whole thing aloud.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • plugins/platforms/discord/adapter.py
    • loads discord.voice_playback_timeout_seconds from config
    • uses the configured timeout for Discord VC playback safety stops
  • gateway/run.py
    • clamps only Discord voice-channel spoken companion text through discord.voice_reply_max_chars
    • preserves the full text reply in the Discord channel
  • hermes_cli/config.py
    • adds defaults for the new Discord config keys
  • tests/gateway/test_voice_command.py
    • covers configured playback timeout and Discord VC spoken-text clamping
  • tests/gateway/test_discord_voice_mixer.py
    • covers timeout behavior for mixer playback
  • website/docs/guides/use-voice-mode-with-hermes.md
    • documents the new knobs

How to Test

  1. Run the targeted gateway/Discord voice tests:
    uv run --with pytest --with pytest-asyncio --with numpy python -m pytest tests/gateway/test_voice_command.py tests/gateway/test_discord_voice_mixer.py -q -o 'addopts='
  2. Configure, if desired:
    discord:
      voice_reply_max_chars: 1200
      voice_playback_timeout_seconds: 300
  3. In Discord VC mode, trigger a long assistant reply. Expected result: the spoken reply is bounded/configurable and the full response is still posted as text.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
    • Targeted tests run instead; see Screenshots / Logs.
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Linux
    • Automated/unit test coverage only; live Discord VC playback was not exercised from this environment.

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
    • Not yet updated in this PR.
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
    • N/A.
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
    • N/A; change is Discord gateway config/playback logic.
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A
    • N/A.

Screenshots / Logs

Targeted test run:

uv run --with pytest --with pytest-asyncio --with numpy python -m pytest tests/gateway/test_voice_command.py tests/gateway/test_discord_voice_mixer.py -q -o 'addopts='
190 passed, 21 skipped in 19.18s

Observed symptom before the fix:

[Discord] Playing TTS in voice channel
Voice playback timed out after 120s

@bmobytes bmobytes changed the title fix: guard long Discord voice TTS playback fix(discord): guard long voice TTS playback Jul 7, 2026
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins platform/discord Discord bot adapter area/config Config system, migrations, profiles labels Jul 7, 2026
@alt-glitch

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Competing/complementary with #50533 (which also makes voice_playback_timeout_seconds configurable). This PR adds the spoken-companion-text clamp (voice_reply_max_chars) on top while keeping the full text reply. Related, not a strict duplicate — maintainer picks or merges the superset.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the focused Discord VC fix. The premise is confirmed on current main: plugins/platforms/discord/adapter.py:2925 still sets PLAYBACK_TIMEOUT = 120, used by the mixer and legacy waits at :2955, :2974, and :2992.

Problems

  • gateway/run.py:12852 in this diff uses max(0, value), so a negative voice_reply_max_chars silently disables the spoken-text cap. The documentation only defines 0 as the opt-out; treat negative values as invalid and fall back to the default.
  • tests/gateway/test_voice_command.py:2124 changes an inspect.getsource() assertion. AGENTS.md:1358 prohibits source-reading tests; this should be behavioral coverage instead.

Suggested changes

  • Add negative-value fallback coverage for voice_reply_max_chars.
  • Replace the source assertion with a real configured-timeout playback test; keep coverage for both mixer and legacy paths where practical.

Automated hermes-sweeper review.

Comment thread gateway/run.py
return max(0, value)

@staticmethod
def _clamp_spoken_voice_reply(text: str, max_chars: int) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Negative values currently become 0, which disables the clamp despite only 0 being documented as the opt-out. Treat negative values as invalid and fall back to 1200; add a regression test for -1.

assert "_voice_playback_timeout" in source, \
"play_in_voice_channel must use the configurable playback timeout helper"

def test_playback_timeout_constant_exists(self):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please replace this source-text assertion with behavioral coverage. AGENTS.md:1358 prohibits tests that read source code because they couple to implementation spelling rather than the playback-timeout contract.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have platform/discord Discord bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants