feat(voice): ARIA produziert TTS-sprechbare Variante via <voice>-Tag
Stefan-Beobachtung: Wetterbericht klang scheisse, weil 'kt'/'°C'/Komma-
Zahlen literal vorgelesen wurden. Der clean_text_for_tts-Regex kennt nur
Markdown + ein paar Uhrzeit-Patterns — Einheiten ausschreiben war never
on the table mit Regex (waechst sonst zur Mammut-Tabelle).
Loesung: hybrid. ARIA selbst macht die semantisch korrekte TTS-Variante,
Regex bleibt als Safety-Net.
bridge/aria_bridge.py:
- neue Helper strip_voice_tag_for_display(text)
- chat-broadcast (1188) und chat_backup-Persist (1157) strippen den Tag
BEVOR Output. ARIA's `<voice>...</voice>` lebt nur transient in `text`
bis clean_text_for_tts ihn fuer TTS extrahiert.
- clean_text_for_tts unveraendert (kennt den Tag schon seit Phase 1).
aria-brain/seed_rules.py:
- neue seed-rule voice/tts-voice-tag mit klarer Trigger-Liste
(Einheiten, Komma-Zahlen, Uhrzeiten, Statusberichte, lange IDs/Codes)
- klare Anti-Trigger (kurze 'OK'/'mache ich'-Antworten — kein doppelter
Text fuer Trivia)
- drei konkrete Beispiele (Wetter, Uhrzeit, Server-Status, Music-Now-Playing)
Output-Format: ARIA schreibt erst Chat-Display-Variante (mit Markdown OK),
haengt dann an EINER neuen Zeile den <voice>-Block an. Tag wird automatisch
gestrippt fuer App-Anzeige + Chat-Backup. f5tts kriegt nur den voice-Inhalt.
Beide Welten happy: Stefan sieht hervorgehobenes Markdown in der Bubble,
hoert sprechbar formulierten Text aus dem Lautsprecher. Keine wachsende
Regex-Tabelle mehr.
Deploy: brain rebuild + restart, bridge restart.
This commit is contained in:
+35
-4
@@ -208,6 +208,30 @@ _UNIT_WORDS = [
|
||||
]
|
||||
|
||||
|
||||
def strip_voice_tag_for_display(text: str) -> str:
|
||||
"""Entfernt `<voice>...</voice>`-Bloecke aus dem Chat-Display-Text.
|
||||
|
||||
ARIA kann einen <voice>-Block ANHAENGEN um eine TTS-freundliche Variante
|
||||
ihrer Antwort zu liefern (Zahlen ausgeschrieben, Einheiten als Wort,
|
||||
Markdown entfernt). Der Block wird dann von clean_text_for_tts als
|
||||
TTS-Quelle benutzt — fuer die Chat-Bubble in der App soll er aber NICHT
|
||||
sichtbar sein, sonst sieht Stefan literal '<voice>...' in seinem Chat.
|
||||
|
||||
Beispiel-Input (Stefan-typisch fuer Wetterbericht):
|
||||
'**Wetter:** 23,5°C, Wind 15 kt NW\\n<voice>Wetter: dreiundzwanzig
|
||||
komma fuenf Grad, Wind fuenfzehn Knoten Nordwest.</voice>'
|
||||
Output:
|
||||
'**Wetter:** 23,5°C, Wind 15 kt NW'
|
||||
|
||||
Mehrere Voice-Bloecke werden alle entfernt (ARIA koennte theoretisch
|
||||
mehrere setzen, machen wir robust). Trailing-Whitespace nach dem Block
|
||||
auch wegtrimmen.
|
||||
"""
|
||||
if not text or "<voice>" not in text.lower():
|
||||
return text
|
||||
return _re_tts.sub(r'\s*<voice>[\s\S]*?</voice>\s*', '\n', text, flags=_re_tts.IGNORECASE).strip()
|
||||
|
||||
|
||||
def clean_text_for_tts(text: str) -> str:
|
||||
"""Bereitet Chat-Text fuer Sprachausgabe auf.
|
||||
|
||||
@@ -1150,11 +1174,15 @@ class ARIABridge:
|
||||
f"aber nicht erstellt:\n{missing_list}\n"
|
||||
"Bitte ARIA bitten, sie wirklich zu schreiben.").strip()
|
||||
|
||||
# Antwort in chat_backup.jsonl loggen (gecleanter Text, ohne File-Marker)
|
||||
# Antwort in chat_backup.jsonl loggen (gecleanter Text, ohne File-Marker
|
||||
# UND ohne <voice>-Tag — der ist eine TTS-Annotation, gehoert nicht in
|
||||
# die Chat-Historie weil ARIA ihre eigene Vorgaenger-Antwort sonst mit
|
||||
# Voice-Tag-Noise als Kontext sieht).
|
||||
# File-Marker werden separat als file_from_aria-Events ausgeliefert.
|
||||
display_text = strip_voice_tag_for_display(text)
|
||||
assistant_backup_ts = self._append_chat_backup({
|
||||
"role": "assistant",
|
||||
"text": text,
|
||||
"text": display_text,
|
||||
"files": [{"serverPath": f["serverPath"], "name": f["name"],
|
||||
"mimeType": f["mimeType"], "size": f["size"]} for f in aria_files],
|
||||
})
|
||||
@@ -1181,11 +1209,14 @@ class ARIABridge:
|
||||
# TTS-aufbereitete Variante fuer Debug (Diagnostic zeigt optional)
|
||||
tts_text_preview = clean_text_for_tts(text)
|
||||
|
||||
# Antwort an die App weiterleiten (als Chat-Nachricht)
|
||||
# Antwort an die App weiterleiten (als Chat-Nachricht).
|
||||
# display_text == text aber ohne <voice>-Tag — der lebt nur transient
|
||||
# in `text` damit clean_text_for_tts weiter unten daraus die TTS-
|
||||
# Variante zieht. Im Chat-Bubble soll der Tag nicht erscheinen.
|
||||
await self._send_to_rvs({
|
||||
"type": "chat",
|
||||
"payload": {
|
||||
"text": text,
|
||||
"text": display_text,
|
||||
"sender": "aria",
|
||||
"messageId": message_id,
|
||||
# backupTs = der ts in chat_backup.jsonl. Wird von Clients als
|
||||
|
||||
Reference in New Issue
Block a user