Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion gateway/platforms/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import random
import re
import socket as _socket
import tempfile
import subprocess
import sys
import time
Expand Down Expand Up @@ -4965,8 +4966,21 @@ async def _stop_typing_task() -> None:
speech_text = self.prepare_tts_text(text_content)
if not speech_text:
raise ValueError("Empty text after markdown cleanup")
# Pass an explicit output_path with the correct
# extension for the current platform. The
# session contextvar (HERMES_SESSION_PLATFORM) is
# not populated at this call site — it is set by
# _set_session_env() inside _handle_message_with_agent
# and cleared on return. Without an explicit path
# text_to_speech_tool falls back to .mp3, which
# causes Telegram to render sendAudio instead of
# sendVoice.
_tts_ext = "ogg" if self.platform == Platform.TELEGRAM else "mp3"
_ts = datetime.now().strftime("%Y%m%d_%H%M%S")
_tts_out = Path(tempfile.gettempdir()) / f"auto_tts_{_ts}.{_tts_ext}"
tts_result_str = await asyncio.to_thread(
text_to_speech_tool, text=speech_text
text_to_speech_tool, text=speech_text,
output_path=str(_tts_out),
)
tts_data = _json.loads(tts_result_str)
_tts_path = tts_data.get("file_path")
Expand Down
37 changes: 37 additions & 0 deletions tests/gateway/test_base_topic_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,40 @@ async def test_telegram_auto_tts_send_failure_keeps_followup_text(self, tmp_path
"metadata": {"thread_id": "17585", "notify": True},
}
]

@pytest.mark.asyncio
async def test_telegram_auto_tts_passes_explicit_ogg_output_path(self, tmp_path):
"""Auto-TTS must pass an explicit .ogg output_path on Telegram.

Regression test for #57049: the session contextvar
(HERMES_SESSION_PLATFORM) is not populated at the auto-TTS call
site in base.py, so text_to_speech_tool falls back to .mp3.
The fix passes an explicit output_path with the correct
extension based on self.platform.
"""
adapter = DummyTelegramAdapter()
adapter._keep_typing = self._hold_typing()
adapter._should_auto_tts_for_chat = lambda _chat_id: True
adapter.play_tts = AsyncMock(return_value=SendResult(success=True, message_id="tts-1"))
adapter.set_message_handler(lambda _event: asyncio.sleep(0, result="Short reply"))

tts_path = tmp_path / "reply.ogg"
tts_path.write_text("audio", encoding="utf-8")
event = self._make_voice_event()

captured_kwargs = {}

def capture_tts(*args, **kwargs):
captured_kwargs.update(kwargs)
return json.dumps({"file_path": str(tts_path)})

with patch("tools.tts_tool.check_tts_requirements", return_value=True), patch(
"tools.tts_tool.text_to_speech_tool",
side_effect=capture_tts,
):
await adapter._process_message_background(event, build_session_key(event.source))

assert "output_path" in captured_kwargs, "text_to_speech_tool should receive explicit output_path"
assert captured_kwargs["output_path"].endswith(".ogg"), (
f"Telegram auto-TTS must use .ogg extension, got: {captured_kwargs['output_path']}"
)
Loading