fix: send voice replies as Opus/OGG on Matrix - #14900
Conversation
Matrix requires Opus codec in an OGG container for voice bubbles per MSC3245. Two code paths were defaulting to MP3 for Matrix: 1. tools/tts_tool.py — want_opus predicate only matched Telegram, so model-invoked TTS always produced MP3 on Matrix. 2. gateway/run.py — _send_voice_reply() hardcoded .mp3 extension regardless of platform. Extend the Opus format check to include Matrix alongside Telegram in both locations.
|
Hey! I was affected by this exact same issue on my Matrix instance. I can confirm that these two patches (tts_tool.py + gateway/run.py) fix Matrix voice delivery — voice messages now arrive as proper OGG Opus voice bubbles via MSC3245. The patch has been running stable for me. Anything blocking this from getting merged? Happy to help test or rebase if needed. ★ |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating both affected paths. The premise remains valid on current main: Matrix still follows the MP3 auto-reply branch in gateway/run.py:13035, and Matrix is absent from want_opus in tools/tts_tool.py:2202.
Problems
gateway/run.py:6308calls.lower()onevent.source.platform, butSessionSource.platformis aPlatformenum (gateway/session.py:75), so this path will raise before TTS generation.- The
.ogggeneration path atgateway/run.py:6311is unsafe for Edge TTS. Edge writes its MP3 stream to the supplied path (tools/tts_tool.py:961-963), while_convert_to_opus()derives its output as the same.oggpath (tools/tts_tool.py:912-916). - No Matrix regression test accompanies the change; existing format coverage is Telegram/Slack-only in
tests/gateway/test_auto_voice_reply_format.py:17-72.
Suggested changes
- Compare against
Platform.TELEGRAM/Platform.MATRIXdirectly. - Generate to a distinct compatible path, then post-convert to a distinct Ogg/Opus file when required; preserve already-native Opus output.
- Add Matrix coverage for both auto voice replies and model-invoked TTS. The linked #54488 documents and tests this post-generation conversion direction.
Automated hermes-sweeper review.
| # (Telegram, Matrix); fall back to .mp3 for others. | ||
| # The TTS tool may convert to .ogg — use file_path from result. | ||
| _platform = event.source.platform or "" | ||
| _ext = ".ogg" if _platform.lower() in ("telegram", "matrix") else ".mp3" |
There was a problem hiding this comment.
event.source.platform is a Platform enum (gateway/session.py:75), not a string, so .lower() raises here. Compare directly against {Platform.TELEGRAM, Platform.MATRIX} (or normalize through .value) before choosing the extension.
| audio_path = os.path.join( | ||
| tempfile.gettempdir(), "hermes_voice", | ||
| f"tts_reply_{_uuid.uuid4().hex[:12]}.mp3", | ||
| f"tts_reply_{_uuid.uuid4().hex[:12]}{_ext}", |
There was a problem hiding this comment.
Generating Edge TTS directly to .ogg does not make it Opus: Edge writes MP3 bytes to the requested path, and _convert_to_opus() derives its output by replacing the suffix with .ogg, producing the same input/output path here. Generate to a distinct path and post-convert to a separate OGG file.
|
Downstream confirmation the direction is right (Telegram→Matrix for
That said, for reconcilers: #54488 fixes the same bug more completely — an Edge-safe post-generation transcode (rather than this one-line |
|
Closing as superseded — the same fix landed upstream via the salvage track in commit |
|
Closing per supersede comment above. The fix landed via salvage track. |
Summary
Matrix requires Opus codec in an OGG container for proper voice bubbles per MSC3245. When sent as MP3, Matrix treats voice replies as generic file attachments — on mobile clients they appear as broken or unplayable.
Two code paths were affected:
1.
tools/tts_tool.py— model-invokedtext_to_speechtoolThe
want_opuspredicate at line 960 only matched"telegram", so when the model calls the TTS tool directly on a Matrix session the output was always MP3 regardless of provider capabilities.Fix: Extended the check to
platform in ("telegram", "matrix").2.
gateway/run.py— auto voice-reply in_send_voice_reply()The output path was hardcoded to
.mp3regardless of the target platform. For providers that support native Opus output (OpenAI, ElevenLabs, Mistral, Gemini), passing.oggas the extension allows the tool to requestresponse_format=opusdirectly — no ffmpeg conversion needed.Fix: Platform-aware extension selection (
.oggfor Telegram/Matrix,.mp3otherwise).Testing
py_compilepasses for both modified filestests/gateway/test_matrix_voice.pypassCloses #14841