fix(tts): sanitise text before SAPI so it stops reading "J.A.R.V.I.S." as "J точка A точка R точка..."
User complaint: SAPI reads every dotted acronym character-by-character with a "точка" between letters. Unbearable on every speak. No real TTS analog shipping yet (Silero/Coqui/Inworld in roadmap), so the immediate fix is text preprocessing. New module `jarvis-core::text_utils` with `sanitize_for_speech()`: - Specific brand mapping: J.A.R.V.I.S. → Джарвис, U.S.A. → США, U.K. → Британия, U.S. → США, S.O.S. → сос. - Generic dotted-acronym collapse: any `[letter].[letter].[letter].(...)` run of 3+ letters gets its dots stripped. "T.O.N." → "TON". - URL stripping: anything starting with http/https gets replaced with the word "ссылка". SAPI reading URLs char-by-char is unlistenable. - Em/en-dash normalisation: — → -, « » " stripped. - Whitespace collapse. Wired in two places: - `jarvis.speak(text, opts?)` Lua API runs sanitiser unless opts.raw=true. - `jarvis-app/llm_fallback::speak_via_sapi` runs sanitiser before the SAPI PowerShell shell-out for LLM auto-fallback replies. 5 unit tests in `text_utils::tests` covering jarvis-name collapse, generic acronym pattern, URL stripping, dash normalisation, and "leave clean russian sentences alone" — all green. Real TTS upgrade (Silero v4 first cut) tracked as P0.1 in roadmap.
This commit is contained in:
parent
9911b6ec40
commit
80b54af1ee
4 changed files with 164 additions and 5 deletions
|
|
@ -1,20 +1,26 @@
|
|||
use mlua::{Lua, Table};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use crate::text_utils::sanitize_for_speech;
|
||||
|
||||
pub fn register(lua: &Lua, jarvis: &Table) -> mlua::Result<()> {
|
||||
// jarvis.speak(text, opts?)
|
||||
// opts.lang: ISO 2-letter code (ru, en, de, ...). default "ru"
|
||||
// opts.async: if true, fire-and-forget; if false, block until done. default true
|
||||
// opts.raw: if true, skip text sanitization (default false)
|
||||
let speak_fn = lua.create_function(|_, (text, opts): (String, Option<Table>)| {
|
||||
let mut iso = "ru".to_string();
|
||||
let mut detached = true;
|
||||
let mut raw = false;
|
||||
|
||||
if let Some(t) = opts {
|
||||
if let Ok(v) = t.get::<String>("lang") { iso = v; }
|
||||
if let Ok(v) = t.get::<bool>("async") { detached = v; }
|
||||
if let Ok(v) = t.get::<String>("lang") { iso = v; }
|
||||
if let Ok(v) = t.get::<bool>("async") { detached = v; }
|
||||
if let Ok(v) = t.get::<bool>("raw") { raw = v; }
|
||||
}
|
||||
|
||||
speak_via_sapi(&text, &iso, detached);
|
||||
let prepared = if raw { text } else { sanitize_for_speech(&text) };
|
||||
speak_via_sapi(&prepared, &iso, detached);
|
||||
Ok(())
|
||||
})?;
|
||||
jarvis.set("speak", speak_fn)?;
|
||||
|
|
@ -61,3 +67,4 @@ fn speak_via_sapi(text: &str, iso: &str, detached: bool) {
|
|||
fn speak_via_sapi(text: &str, _iso: &str, _detached: bool) {
|
||||
log::info!("[Lua tts] would speak: {}", text);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue