fix(tts): strip <think> reasoning blocks from TTS text

When /reasoning show is enabled, the model's <think> blocks appear in
the final assistant message. TTS reads these aloud, which is unwanted —
users want to see reasoning but not hear it spoken.

- Add _THINK_BLOCK regex to _strip_markdown_for_tts() (tools/tts_tool.py)
  — the general TTS text-preparation path used by all TTS providers
- Add <think> stripping to prepare_tts_text() (gateway/platforms/base.py)
  — the gateway auto-TTS path
- Reuses the existing regex pattern from stream_tts_to_speaker()

Closes #34213
This commit is contained in:
Johann 2026-05-28 23:25:45 -04:00 committed by Teknium
parent ef274a4829
commit d9336e7453
2 changed files with 11 additions and 4 deletions

View file

@ -3833,9 +3833,10 @@ class BasePlatformAdapter(ABC):
def prepare_tts_text(self, text: str) -> str:
"""Prepare a spoken script for TTS.
Auto-TTS should not feed raw chat Markdown or compact symbols to the
speech provider. It should receive a transcript-like script: headings
and bullets flattened into sentence pauses, and units like ``°C``
Auto-TTS should not feed raw chat Markdown, ``<think>`` reasoning
blocks, or compact symbols to the speech provider. It should receive
a transcript-like script: reasoning blocks removed, headings and
bullets flattened into sentence pauses, and units like ``°C``
expanded to words such as ``degrees Celsius``.
"""
try:
@ -3843,6 +3844,7 @@ class BasePlatformAdapter(ABC):
return prepare_spoken_text(text, max_chars=4000)
except Exception:
# Keep auto-TTS best-effort if the normalizer ever fails.
text = re.sub(r'<think[\s>].*?</think>', ' ', text, flags=re.DOTALL)
return re.sub(r'[*_`#\[\]()]', '', text)[:4000].strip()
async def play_tts(

View file

@ -3057,9 +3057,14 @@ _EMOJI = re.compile(
'[\U0001F000-\U0001FAFF\u2600-\u27BF\uFE0F\u200D\U000E0020-\U000E007F]+'
)
# Strip <think>...</think> reasoning blocks before TTS — models with
# /reasoning show enabled produce think blocks that shouldn't be spoken.
_THINK_BLOCK = re.compile(r'<think[\s>].*?</think>', flags=re.DOTALL)
def _strip_markdown_for_tts(text: str) -> str:
"""Remove markdown formatting (and emoji) that shouldn't be spoken aloud."""
"""Remove markdown, think blocks, and emoji that shouldn't be spoken."""
text = _THINK_BLOCK.sub(' ', text)
text = _MD_CODE_BLOCK.sub(' ', text)
text = _MD_LINK.sub(r'\1', text)
text = _MD_URL.sub('', text)