diff --git a/core/voice/language_policy.py b/core/voice/language_policy.py index d77ff24..3216f94 100644 --- a/core/voice/language_policy.py +++ b/core/voice/language_policy.py @@ -38,6 +38,7 @@ def enforce_input_language(text: str) -> str | None: def clean_speakable_text(text: str) -> str: value = re.sub(r"```.*?```", "", str(text or ""), flags=re.DOTALL) + value = re.sub(r"^\s*\[SPEAKER\]\s*", "", value, flags=re.IGNORECASE) value = re.sub(r"[`*_#>|]", "", value) value = re.sub(r"\s+", " ", value).strip() return value diff --git a/docs/WINDOWS_UPDATE_VISION_SPEAKER.md b/docs/WINDOWS_UPDATE_VISION_SPEAKER.md new file mode 100644 index 0000000..65c8344 --- /dev/null +++ b/docs/WINDOWS_UPDATE_VISION_SPEAKER.md @@ -0,0 +1,47 @@ +# Biz-Windows: Foliensehen und saubere Sprachausgabe + +Dieser Stand ergänzt v0.17.11 um die Entfernung des internen `[SPEAKER]`-Tags +aus gesprochenen Antworten. Der Folienweg vom Companion über `/lecture/context` +bis zur multimodalen Anfrage an das konfigurierte LLM ist bereits in v0.17.11 +enthalten. Die iPad-/iPhone-App wird getrennt aktualisiert. + +## Update der bestehenden Windows-VM + +1. Die aktive Installation, den Git-Stand und lokale Änderungen ermitteln. + Vor dem Update die Konfiguration, `Soul.md`, `User.md`, den gesamten + `TrinityRuntime`-Ordner und die Voice-Referenzdateien sichern. Backups + außerhalb des Git-Repositories ablegen. +2. Den geprüften Git-Stand beziehen und Änderungen an der bestehenden + Installation vergleichen. Windows-spezifische oder lokale Anpassungen + übernehmen, statt sie durch Mac-Dateien zu ersetzen. +3. `system.profile=BIZ`, Modell-Endpunkt, Bridge- und Voice-Tokens, + Ubuntu-Verbindungen, Eve-Stimmprobe, Vault-Pfade und Wakeword-Varianten + aus der Windows-Konfiguration beibehalten. `core/config.json` nicht aus + dem Repository oder vom Mac übernehmen. +4. Die Windows-Trinity kontrolliert neu starten. Bestehende Chat- und + Sprachtests wiederholen; eine Modellantwort mit `[SPEAKER]` muss ohne + dieses Wort gesprochen werden. Die öffentliche Lautsprecherumschaltung + der älteren Desktop-Sprachausgabe separat prüfen. +5. Das Companion mit dem Tailscale-Endpunkt der Windows-Bridge verbinden und + eine PDF- und eine HTML-Folie testen: Seitenwechsel, Bildbestätigung in + den Companion-Einstellungen und eine Frage nach einem Detail, das nur + im Bild steht. Die Antwort muss über die Windows-Trinity laufen. + +## Verteilung der Dienste + +Die bestehende VM-/Ubuntu-Aufteilung ist unterstützt: Die Windows-VM hält +Sessions, Memory, Agenten und Freigaben; Ubuntu berechnet STT, TTS und das +Vision-LLM. Companion-Clients wählen die Windows-Bridge. Die Tailscale- +Verbindungen müssen für Bridge, Voice und Modell vorhanden bleiben. + +Der Gemma4-Dienst auf Ubuntu benötigt für Bildanfragen eine ausreichend große +`batch-size` und `ubatch-size`. Auf der Workstation wurde am 22.09.2026 die +vorherige Kombination 512/128 nach einem reproduzierbaren Bild-Absturz auf +2048/2048 angehoben. Zwei synthetische Bildwerte und eine echte Companion- +Folie wurden anschließend korrekt gelesen. Dieser Serverwert gehört nicht +in die Windows-Trinity-Konfiguration. + +Eine gemeinsame PRIVAT-/BIZ-Instanz ist eine eigene Migration. Sie benötigt +eine Entscheidung über Daten- und Zugriffsgrenzen sowie eine geprüfte +Übernahme der bisherigen Sessions und Memory-Datenbanken. Das Update hier +führt diese Migration nicht aus. diff --git a/tests/voice/test_voice_primitives.py b/tests/voice/test_voice_primitives.py index eda3808..e65a072 100644 --- a/tests/voice/test_voice_primitives.py +++ b/tests/voice/test_voice_primitives.py @@ -34,6 +34,11 @@ def test_german_policy_allows_short_technical_terms_but_rejects_english_paragrap assert segment_for_speech("Erster Satz. Zweiter Satz.", max_chars=14) == ["Erster Satz.", "Zweiter Satz."] +def test_speaker_routing_marker_is_not_spoken(): + assert segment_for_speech("[SPEAKER] Wir beginnen mit der Tabelle.") == ["Wir beginnen mit der Tabelle."] + assert segment_for_speech("Der Speaker ist hier ein Fachbegriff.") == ["Der Speaker ist hier ein Fachbegriff."] + + def test_events_and_metrics_contain_no_prompt_content(tmp_path): event = VoiceEvent("transcript.final", "s", "t", {"length": 12}) assert event.as_dict()["type"] == "transcript.final"