mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-30 19:09:28 +00:00
Auto-TTS previously fed raw chat Markdown and compact symbols straight to the speech provider, so units were read as stray letters and headings or bullets ran together. This routes spoken text through a new normalizer, tools/tts_text_normalize.prepare_spoken_text, that expands units (for example a temperature written with the degree symbol becomes "degrees Celsius") and flattens Markdown into a transcript-like script with sentence pauses. The normalizer is best-effort: if it ever fails the code falls back to the previous markdown-strip behavior, so auto-TTS keeps working. The Telegram voice caption uses the same normalized text. Includes a unit test.
70 lines
2.7 KiB
Python
70 lines
2.7 KiB
Python
from gateway.config import Platform, PlatformConfig
|
|
from gateway.platforms.base import BasePlatformAdapter
|
|
from tools.tts_text_normalize import prepare_spoken_text
|
|
|
|
|
|
class _DummyAdapter(BasePlatformAdapter):
|
|
def __init__(self):
|
|
super().__init__(PlatformConfig(enabled=True, token="test"), Platform.TELEGRAM)
|
|
|
|
async def connect(self):
|
|
return True
|
|
|
|
async def disconnect(self):
|
|
pass
|
|
|
|
async def send(self, chat_id, content, **kwargs):
|
|
raise AssertionError("not used")
|
|
|
|
async def get_chat_info(self, chat_id):
|
|
return {"id": chat_id, "type": "dm"}
|
|
|
|
|
|
def test_prepare_spoken_text_expands_celsius_and_weather_units():
|
|
raw = """## Christchurch today\n\n- **Now:** about **14°C**, feels like **14°C**\n- **Wind:** 9 km/h\n- **Rain:** 1.3 mm\n- **Range:** 11\u201317°C\n"""
|
|
|
|
spoken = prepare_spoken_text(raw)
|
|
|
|
assert "##" not in spoken
|
|
assert "**" not in spoken
|
|
assert "14 degrees Celsius" in spoken
|
|
assert "11 to 17 degrees Celsius" in spoken
|
|
assert "9 kilometres per hour" in spoken
|
|
assert "1.3 millimetres" in spoken
|
|
assert "°C" not in spoken
|
|
assert "km/h" not in spoken
|
|
|
|
|
|
def test_prepare_spoken_text_flattens_visual_formatting_for_tts():
|
|
raw = """## Short answer\n\n- [link text](https://example.com) → NZ$120 & 80% likely\n- `inline code` should not keep backticks\n"""
|
|
|
|
spoken = prepare_spoken_text(raw)
|
|
|
|
assert "Short answer, link text to 120 New Zealand dollars and 80 percent likely" in spoken
|
|
assert "inline code should not keep backticks" in spoken
|
|
assert "https://" not in spoken
|
|
assert "`" not in spoken
|
|
assert "→" not in spoken
|
|
assert "&" not in spoken
|
|
|
|
|
|
def test_gateway_auto_tts_preparation_uses_spoken_normalizer():
|
|
adapter = _DummyAdapter()
|
|
|
|
spoken = adapter.prepare_tts_text("## Weather\n- Now: 14°C, wind 9 km/h")
|
|
|
|
assert spoken == "Weather, Now: 14 degrees Celsius, wind 9 kilometres per hour."
|
|
|
|
|
|
def test_prepare_spoken_text_polish_edge_cases():
|
|
# Heading folds into the next sentence as a lead-in, not a bare label.
|
|
assert prepare_spoken_text("## Weather\nIt will be sunny") == "Weather, It will be sunny."
|
|
# Bare degree unit (no leading number) still expands.
|
|
assert "degrees Celsius" in prepare_spoken_text("measured in °C")
|
|
# Trailing comma is not swallowed into the amount.
|
|
assert "300 US dollars" in prepare_spoken_text("US$300, next")
|
|
# Real numeric rates expand, but and/or, N/A, IDs and dates are left intact.
|
|
assert "5 dollars per month" in prepare_spoken_text("$5/month")
|
|
assert "and/or" in prepare_spoken_text("choose and/or option")
|
|
assert "N/A" in prepare_spoken_text("status N/A here")
|
|
assert "2026/06/02" in prepare_spoken_text("due 2026/06/02 ok")
|