"""Text helpers shared by the adapters.""" from __future__ import annotations import re _WHITESPACE = re.compile(r"\s+") def normalise(text: str) -> str: """Collapse whitespace and trim. Keeps the robot from reading stray newlines.""" return _WHITESPACE.sub(" ", text or "").strip() def is_cjk(char: str) -> bool: code = ord(char) return ( 0x4E00 <= code <= 0x9FFF # CJK unified ideographs or 0x3400 <= code <= 0x4DBF # extension A or 0x3040 <= code <= 0x30FF # kana or 0xAC00 <= code <= 0xD7AF # hangul ) def estimate_speech_seconds(text: str, words_per_minute: int = 150) -> float: """Rough spoken duration. Used only when the robot does not report utterance completion itself, so the dashboard can still return to "Ready" at a believable moment instead of hanging on "Speaking..." forever. """ cleaned = normalise(text) if not cleaned: return 0.0 cjk_chars = sum(1 for ch in cleaned if is_cjk(ch)) latin_words = len([w for w in cleaned.split() if any(not is_cjk(c) for c in w)]) # ~2.5 CJK characters per second is a typical TTS cadence. seconds = (latin_words / max(60, words_per_minute)) * 60.0 + (cjk_chars / 2.5) return max(0.8, seconds)