mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(tts): keep Telegram caption on the original reply text
Review follow-up: the spoken script is for synthesis only. Caption eligibility and payload stay on the original reply, so a long reply whose normalized script fits the 1024 char limit is still delivered in full as its own message. Adds the long-original/short-normalized regression case to the auto-TTS caption tests.
This commit is contained in:
parent
a9a9005f31
commit
ef274a4829
2 changed files with 49 additions and 6 deletions
|
|
@ -5545,7 +5545,6 @@ class BasePlatformAdapter(ABC):
|
|||
# an explicit ``/voice on|tts`` opt-in OR when ``voice.auto_tts`` is
|
||||
# True globally and no ``/voice off`` has been issued.
|
||||
_tts_path = None
|
||||
_tts_speech_text = None
|
||||
if (self._should_auto_tts_for_chat(event.source.chat_id)
|
||||
and event.message_type == MessageType.VOICE
|
||||
and text_content
|
||||
|
|
@ -5557,7 +5556,6 @@ class BasePlatformAdapter(ABC):
|
|||
speech_text = self.prepare_tts_text(text_content)
|
||||
if not speech_text:
|
||||
raise ValueError("Empty text after markdown cleanup")
|
||||
_tts_speech_text = speech_text
|
||||
tts_result_str = await asyncio.to_thread(
|
||||
text_to_speech_tool, text=speech_text
|
||||
)
|
||||
|
|
@ -5570,14 +5568,19 @@ class BasePlatformAdapter(ABC):
|
|||
_tts_caption_delivered = False
|
||||
if _tts_path and Path(_tts_path).exists():
|
||||
try:
|
||||
# Caption eligibility and payload stay on the ORIGINAL
|
||||
# reply text. The spoken script is for synthesis only:
|
||||
# normalization can shrink a long reply below the
|
||||
# 1024-char caption limit, and captioning that spoken
|
||||
# form would suppress the full formatted reply the
|
||||
# user is meant to receive as a separate message.
|
||||
telegram_tts_caption = None
|
||||
caption_text = _tts_speech_text or self.prepare_tts_text(text_content)
|
||||
if (
|
||||
self.platform == Platform.TELEGRAM
|
||||
and caption_text
|
||||
and caption_text[:1024] == caption_text
|
||||
and text_content
|
||||
and text_content[:1024] == text_content
|
||||
):
|
||||
telegram_tts_caption = caption_text
|
||||
telegram_tts_caption = text_content
|
||||
tts_result = await self.play_tts(
|
||||
chat_id=event.source.chat_id,
|
||||
audio_path=_tts_path,
|
||||
|
|
|
|||
|
|
@ -333,6 +333,46 @@ class TestTelegramAutoTtsCaptionDelivery:
|
|||
}
|
||||
]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_long_original_with_short_spoken_script_still_sends_full_reply(self, tmp_path):
|
||||
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"))
|
||||
# Markdown-heavy reply: over the 1024-char caption limit as written,
|
||||
# but the normalized spoken script (markdown and URLs removed) is far
|
||||
# below it. Caption eligibility must follow the ORIGINAL reply, so the
|
||||
# full formatted text is still delivered as its own message instead of
|
||||
# being swallowed into a lossy caption.
|
||||
long_reply = "\n".join(
|
||||
f"- **item {i}** [details](https://example.com/some/very/long/path/{i:04d})"
|
||||
for i in range(20)
|
||||
)
|
||||
assert len(long_reply) > 1024
|
||||
assert len(adapter.prepare_tts_text(long_reply)) <= 1024
|
||||
adapter.set_message_handler(lambda _event: asyncio.sleep(0, result=long_reply))
|
||||
|
||||
tts_path = tmp_path / "reply.ogg"
|
||||
tts_path.write_text("audio", encoding="utf-8")
|
||||
event = self._make_voice_event()
|
||||
|
||||
with patch("tools.tts_tool.check_tts_requirements", return_value=True), patch(
|
||||
"tools.tts_tool.text_to_speech_tool",
|
||||
return_value=json.dumps({"file_path": str(tts_path)}),
|
||||
):
|
||||
await adapter._process_message_background(event, build_session_key(event.source))
|
||||
|
||||
adapter.play_tts.assert_awaited_once()
|
||||
assert adapter.play_tts.await_args.kwargs["caption"] is None
|
||||
assert adapter.sent == [
|
||||
{
|
||||
"chat_id": "-1001",
|
||||
"content": long_reply,
|
||||
"reply_to": None,
|
||||
"metadata": {"thread_id": "17585", "notify": True},
|
||||
}
|
||||
]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_telegram_auto_tts_send_failure_keeps_followup_text(self, tmp_path):
|
||||
adapter = DummyTelegramAdapter()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue