[PR #5560] [MERGED] fix(tts): strip Markdown syntax before sending text to TTS engines #5486

Closed
opened 2026-06-05 15:21:30 -04:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Mintplex-Labs/anything-llm/pull/5560
Author: @GopalGB
Created: 4/30/2026
Status: Merged
Merged: 5/1/2026
Merged by: @timothycarambat

Base: masterHead: gb-fix/tts-strip-markdown


📝 Commits (3)

  • ffa4200 fix(tts): strip Markdown syntax before sending text to TTS engines
  • e04566b update comment and lint
  • a6027ab update comment and lint

📊 Changes

3 files changed (+96 additions, -2 deletions)

View changed files

📝 frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/TTSButton/native.jsx (+2 -1)
📝 frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/TTSButton/piperTTS.jsx (+4 -1)
frontend/src/utils/chat/messageToSpeech.js (+90 -0)

📄 Description

Bug

Chat responses are rendered as Markdown, but the TTS components in
`WorkspaceChat/.../TTSButton/` pipe the raw response straight into Piper
and the browser's `SpeechSynthesis` API. The synthesizer reads every
special character literally:

  • `bold` -> "asterisk asterisk bold asterisk asterisk"
  • `# Heading` -> "pound heading"
  • code fences are read backtick-by-backtick
  • `- item` is read as "hyphen item"
  • `label` is read as "open bracket label close bracket open paren u r l close paren"

The result is effectively unusable whenever the assistant includes any
formatting, which is most of the time. The reporter on #5557 explicitly
called out hearing "asterix" repeatedly.

Fix

Add a small `messageToSpeech` helper in
`frontend/src/utils/chat/messageToSpeech.js` that converts a Markdown
message body to plain text suitable for TTS:

Markdown Treatment
Fenced code blocks (```...``` and `~~~...~~~`) dropped (nothing useful to read aloud)
Inline code (``...``) keep text, drop backticks
Images `alt` dropped
Links `label` keep label, drop URL
Reference link definitions dropped
Headings (`#` ... `######`) keep text, drop markers
Blockquote markers (`>`) keep text, drop marker
List markers (`-`, `*`, `+`, `1.`, `12)`) keep text, drop marker
Horizontal rules dropped
Bold / italic / strikethrough emphasis keep text, drop markers
Tables pipes become commas, alignment row dropped
HTML tags strip tags, keep content
Repeated whitespace collapsed to single spaces

The helper is regex-based — no new dependency. It is wired into both the
native (`SpeechSynthesis`) and Piper TTS components.

Test

A standalone Node script verifies 18 cases (bold, italic, struck,
heading, list, code fence, link, image, blockquote, table, HTML, plain
text, etc.) all map to the expected spoken text. There is no existing
`frontend` test infrastructure in this repo, so I did not add a Jest /
Vitest spec — happy to add one if maintainers want to bring up that
framework alongside this change.

resolves #5557.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Mintplex-Labs/anything-llm/pull/5560 **Author:** [@GopalGB](https://github.com/GopalGB) **Created:** 4/30/2026 **Status:** ✅ Merged **Merged:** 5/1/2026 **Merged by:** [@timothycarambat](https://github.com/timothycarambat) **Base:** `master` ← **Head:** `gb-fix/tts-strip-markdown` --- ### 📝 Commits (3) - [`ffa4200`](https://github.com/Mintplex-Labs/anything-llm/commit/ffa42003c77c4eb95074f2acba8792a07a3cf161) fix(tts): strip Markdown syntax before sending text to TTS engines - [`e04566b`](https://github.com/Mintplex-Labs/anything-llm/commit/e04566b50c2bf6a0a5544c808f9eb1c9da83da0a) update comment and lint - [`a6027ab`](https://github.com/Mintplex-Labs/anything-llm/commit/a6027ab1018026b546a8b324d4beca1f014a6777) update comment and lint ### 📊 Changes **3 files changed** (+96 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/TTSButton/native.jsx` (+2 -1) 📝 `frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/Actions/TTSButton/piperTTS.jsx` (+4 -1) ➕ `frontend/src/utils/chat/messageToSpeech.js` (+90 -0) </details> ### 📄 Description ## Bug Chat responses are rendered as Markdown, but the TTS components in \`WorkspaceChat/.../TTSButton/\` pipe the raw response straight into Piper and the browser's \`SpeechSynthesis\` API. The synthesizer reads every special character literally: - \`**bold**\` -> "asterisk asterisk bold asterisk asterisk" - \`# Heading\` -> "pound heading" - code fences are read backtick-by-backtick - \`- item\` is read as "hyphen item" - \`[label](url)\` is read as "open bracket label close bracket open paren u r l close paren" The result is effectively unusable whenever the assistant includes any formatting, which is most of the time. The reporter on #5557 explicitly called out hearing "asterix" repeatedly. ## Fix Add a small \`messageToSpeech\` helper in \`frontend/src/utils/chat/messageToSpeech.js\` that converts a Markdown message body to plain text suitable for TTS: | Markdown | Treatment | |---|---| | Fenced code blocks (\`\`\`...\`\`\` and \`~~~...~~~\`) | dropped (nothing useful to read aloud) | | Inline code (\`\`...\`\`) | keep text, drop backticks | | Images \`![alt](url)\` | dropped | | Links \`[label](url)\` | keep label, drop URL | | Reference link definitions | dropped | | Headings (\`#\` ... \`######\`) | keep text, drop markers | | Blockquote markers (\`>\`) | keep text, drop marker | | List markers (\`-\`, \`*\`, \`+\`, \`1.\`, \`12)\`) | keep text, drop marker | | Horizontal rules | dropped | | Bold / italic / strikethrough emphasis | keep text, drop markers | | Tables | pipes become commas, alignment row dropped | | HTML tags | strip tags, keep content | | Repeated whitespace | collapsed to single spaces | The helper is regex-based — no new dependency. It is wired into both the native (\`SpeechSynthesis\`) and Piper TTS components. ## Test A standalone Node script verifies 18 cases (bold, italic, struck, heading, list, code fence, link, image, blockquote, table, HTML, plain text, etc.) all map to the expected spoken text. There is no existing \`frontend\` test infrastructure in this repo, so I did not add a Jest / Vitest spec — happy to add one if maintainers want to bring up that framework alongside this change. resolves #5557. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-06-05 15:21:30 -04:00
yindo closed this issue 2026-06-05 15:21:30 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Mintplex-Labs/anything-llm#5486