Edge TTS audio conversion scoped to Telegram only
Cluster 26404. Data as of
Nothing open on GitHub. Fix merged in #28512 on 19 May 2026. Check it covers your case.
The short answer
- Known?
- Yes. 4 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #28512. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
unresolved. That is separate from the fix event. - Still open
- Nothing open on GitHub.
A bug was identified where Edge TTS audio was being unnecessarily converted from MP3 to OGG even outside of Telegram voice delivery contexts, breaking local CLI playback on macOS. Fixes aim to restrict the OGG conversion to only when Telegram voice delivery is the target.
Open issues0
Open PRs0
Fixes on record
-
#28512 fix(tts): preserve native audio outside Telegram voice delivery
files:
scripts/release.py,tests/tools/test_tts_opus_routing.py,tools/tts_tool.pyShow the diff
-
scripts/release.py@@ -174,6 +174,7 @@ "[redacted:email]": "Zyrixtrex", # PR #26754 salvage (avoid duplicate text after auto-TTS) "[email protected]": "nftpoetrist", # PR #25856 salvage (escape slash-confirm preview) "[email protected]": "samahn0601", # PR #27887 salvage (retry wrapped connect timeouts) + "[redacted:email]": "aqilaziz", # PR #26406 salvage (preserve native audio outside Telegram) "[email protected]": "eliteworkstation94-ai", # PR #28157 salvage (group reply session splits) "[redacted:email]": "hllqkb", "[email protected]": "Bongulielmi", -
tests/tools/test_tts_opus_routing.py@@ -0,0 +1,70 @@ +import json +from pathlib import Path +from unittest.mock import Mock + +import pytest + +from gateway.session_context import _UNSET, _VAR_MAP +from tools import tts_tool + + +def _reset_session_context() -> None: + for var in _VAR_MAP.values(): + var.set(_UNSET) + + [redacted:email](autouse=True) +def _clean_session_platform(monkeypatch): + _reset_session_context() + monkeypatch.delenv("HERMES_SESSION_PLATFORM", raising=False) + yield + _reset_session_context() + + +async def _write_edge_output(_text: str, output_path: str, _tts_config: dict) -> str: + Path(output_path).write_bytes(b"mp3") + return output_path + + +def test_edge_cli_preserves_native_mp3(tmp_path, monkeypatch): + out = tmp_path / "speech.mp3" + convert = Mock() + + monkeypatch.setattr(tts_tool, "_load_tts_config", lambda: {"provider": "edge"}) + monkeypatch.setattr(tts_tool, "_import_edge_tts", lambda: object()) + monkeypatch.setattr(tts_tool, "_generate_edge_tts", _write_edge_output) + monkeypatch.setattr(tts_tool, "_convert_to_opus", convert) + + result = json.loads(tts_tool.text_to_speech_tool("hello", output_path=str(out))) + + assert result["success"] is True + assert result["file_path"] == str(out) + assert result["voice_compatible"] is False + assert result["media_tag"] == f"MEDIA:{out}" + convert.assert_not_called() + + +def test_edge_telegram_converts_to_opus_voice(tmp_path, monkeypatch): + out = tmp_path / "speech.mp3" + opus = tmp_path / "speech.ogg" + + def fake_convert(path: str) -> str: + assert path == str(out) + opus.write_bytes(b"ogg") + return str(opus) + + convert = Mock(side_effect=fake_convert) + + monkeypatch.setenv("HERMES_SESSION_PLATFORM", "telegram") + monkeypatch.setattr(tts_tool, "_load_tts_config", lambda: {"provider": "edge"}) + monkeypatch.setattr(tts_tool, "_import_edge_tts", lambda: object()) + monkeypatch.setattr(tts_tool, "_generate_edge_tts", _write_edge_output) + monkeypatch.setattr(tts_tool, "_convert_to_opus", convert) + + result = json.loads(tts_tool.text_to_speech_tool("hello", output_path=str(out))) + + assert result["success"] is True + assert result["file_path"] == str(opus) + assert result["voice_compatible"] is True + assert result["media_tag"] == f"[[audio_as_voice]]\nMEDIA:{opus}" + convert.assert_called_once_with(str(out)) -
tools/tts_tool.py@@ -1830,8 +1830,10 @@ def text_to_speech_tool( "error": f"TTS generation produced no output (provider: {provider})" }, ensure_ascii=False) - # Try Opus conversion for Telegram compatibility - # Edge TTS outputs MP3, NeuTTS/KittenTTS output WAV — all need ffmpeg conversion + # Try Opus conversion for Telegram compatibility. + # Edge TTS outputs MP3, NeuTTS/KittenTTS output WAV. Keep those native + # formats for local/CLI playback and only convert when the current + # platform actually needs Opus voice delivery. voice_compatible = False if command_provider_config is not None: # Command providers are documents by default. Voice-bubble @@ -1843,13 +1845,17 @@ def text_to_speech_tool( if opus_path: file_str = opus_path voice_compatible = file_str.endswith(".ogg") - elif provider in {"edge", "neutts", "minimax", "xai", "kittentts", "piper"} and not file_str.endswith(".ogg"): + elif ( + want_opus + and provider in {"edge", "neutts", "minimax", "xai", "kittentts", "piper"} + and not file_str.endswith(".ogg") + ): opus_path = _convert_to_opus(file_str) if opus_path: file_str = opus_path voice_compatible = True elif provider in {"elevenlabs", "openai", "mistral", "gemini"}: - voice_compatible = file_str.endswith(".ogg") + voice_compatible = want_opus and file_str.endswith(".ogg") file_size = os.path.getsize(file_str) logger.info("TTS audio saved: %s (%s bytes, provider: %s)", file_str, f"{file_size:,}", provider)
-
2 PRs closed without merging
From the close comments: duplicate 1 · merged via another change 1
Closed issues · 1
- Closed#26404[Bug]: Unnecessary MP3→OGG conversion in Edge TTS breaks local CLI playback on macOS
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.50: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Merged PRs
- #28512
- Closed issues
- 1
- Attempts
- 2 PRs closed without merging
- Fix link checked
- 19 May 2026