From ee90f7c7d83d121cdc81036d4099ad760e999d8c Mon Sep 17 00:00:00 2001 From: MathiasEngel Date: Sat, 1 Aug 2026 17:52:33 +0200 Subject: [PATCH] Release v0.17.10 G2 speech reliability --- README.md | 2 +- RELEASES.md | 1 + core/bridge_audio.py | 51 +++++++++++++++++++++++++------- docs/release_notes/v0.17.10.md | 6 ++++ pyproject.toml | 2 +- tests/test_bridge_audio.py | 53 ++++++++++++++++++++++++++++++---- 6 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 docs/release_notes/v0.17.10.md diff --git a/README.md b/README.md index e5364b1..c289bf2 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ ![Trinity Assistant Banner](assets/banner.png) > [!NOTE] > **Aktuelle Highlights:** +> - **v0.17.10:** Die G2-Spracherkennung reagiert schneller und robuster: Digitale Stille, unsichere No-Speech-Segmente und bekannte Whisper-Untertitelartefakte wie `Copyright WDR`, `Amara.org` oder erfundene Schnellsession-Domains werden verworfen. Der bisher zu starke Fachwort-Bias ist auf das eigentliche Wakeword `Trinity` reduziert. > - **v0.17.9:** Eine Trinity-Instanz in einer Windows-VM kann Eve-STT und Eve-TTS jetzt von einem privaten Ubuntu-NVIDIA-Host beziehen. Windows bleibt Control Plane mit Sessions, Memory, Agenten und UI; Ubuntu uebernimmt nur GPU-Inferenz. Dafuer gibt es die Profile `eve-windows-remote` und `eve-linux-gpu-server`, getrennte Tokens, Diagnosepruefungen und Installationsskripte. > - **v0.17.8:** ClassicUI, iPhone und iPad zeigen nur noch einen zentralen Lautsprecherknopf. Aktiviert ein Client seine Ausgabe, wird er Bridge-weit zum einzigen Sprecher und alle anderen verbundenen Clients zeigen den durchgestrichenen Lautsprecher. Ein erneuter Klick auf dem aktiven Geraet schaltet Trinity ueberall stumm; auch Eves direkter Desktop- und Companion-Audiopfad folgt dieser Auswahl. -> - **v0.17.7:** Die Even G2 erhaelt einen kompakten, zwischengespeicherten Umgebungskontext: die neueste n-tv-Schlagzeile, Wetter und Temperatur der naechsten Stunde sowie optional den naechsten Kalendertermin. iPhone/iPad koennen Standort und Kalender einzeln und widerrufbar an die private Bridge melden; ohne Freigabe gilt der in der G2 gewaehlte Ersatzort. > - Die vollstaendige Historie steht in **[RELEASES.md](RELEASES.md)** und in den detaillierten **[Release Notes](docs/release_notes/)**. > [!IMPORTANT] diff --git a/RELEASES.md b/RELEASES.md index f7e30d8..b91321f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,6 +6,7 @@ Einzelnotizen liegen unter [docs/release_notes](docs/release_notes/). ## Aktuelle Highlights +- **v0.17.10:** Die G2-Audiobridge nutzt einen neutralen deutschen Erkennungskontext mit `Trinity` als einzigem Hotword. Digitale Stille, unsichere No-Speech-Segmente und typische Whisper-Untertitelhalluzinationen wie `Copyright WDR`, `Amara.org` oder erfundene Schnellsession-Domains werden vor dem Routing verworfen. Kleinere Beam-Suchen senken die G2-Latenz, ohne Desktop-, Companion- oder Eve-Sprachpfade zu veraendern. - **v0.17.9:** Windows kann als Trinity-Control-Plane in einer VM laufen, waehrend ein privater Ubuntu-Host mit NVIDIA-GPU Parakeet-STT, Qwen3-TTS/Eve und das konfigurierte OpenAI-kompatible LLM bereitstellt. Die neuen Profile `eve-windows-remote` und `eve-linux-gpu-server`, getrennte Voice-/Core-Tokens, Remote-Diagnosen und Installationsskripte vermeiden unnoetiges GPU-Passthrough. Native Mac- und Windows-Eve-Profile sowie Legacy STT/TTS bleiben unveraendert waehlbar. - **v0.17.8:** ClassicUI, iPhone und iPad verwenden einen einzigen Bridge-weiten Lautsprecherknopf. Das ausgewaehlte Geraet ist exklusiv; alle anderen Clients werden samt direkter Eve-Wiedergabe sofort stumm und zeigen den durchgestrichenen Lautsprecher. Ein zweiter Klick auf dem aktiven Geraet setzt die Ausgabe explizit auf `Stumm`, statt sie unbemerkt an einen anderen Client weiterzureichen. - **v0.17.7:** Even G2 zeigt eine neue n-tv-Schlagzeile kurz als Laufhinweis sowie Wetter und Temperatur der naechsten Stunde im kompakten Statusfeld. Optional meldet der Companion den aktuellen Standort und den naechsten Kalendertermin an die private Bridge; beides bleibt nur im Arbeitsspeicher und wird beim Abschalten entfernt. Ohne Freigabe verwendet Trinity den in G2 konfigurierten Ersatzort. diff --git a/core/bridge_audio.py b/core/bridge_audio.py index 0bcf263..577ea9f 100644 --- a/core/bridge_audio.py +++ b/core/bridge_audio.py @@ -4,21 +4,22 @@ import base64 import binascii +import re import threading G2_SAMPLE_RATE = 16_000 MAX_AUDIO_SECONDS = 20 TRINITY_VOCABULARY = ( - "Trinity. Deutsche Vorlesung und natuerliche Konversation. " - "Kommandos: Trinity, Hilfe, Modus Zuruf, Zurufmodus, " - "Konversationsmodus, Wakeword, Arbeitsraum, " - "Schnellsession, Nash-Gleichgewicht, Gefangenendilemma und Kooperation." + "Natuerliche deutsche Sprache. Trinity ist der Name der Assistentin." ) -TRINITY_HOTWORDS = ( - "Trinity Hilfe Zuruf Zurufmodus Konversationsmodus Wakeword " - "Arbeitsraum Schnellsession Nash-Gleichgewicht " - "Gefangenendilemma Kooperation" +TRINITY_HOTWORDS = "Trinity" + +_KNOWN_SUBTITLE_HALLUCINATIONS = ( + re.compile(r"\bcopyright\b.*\b(?:ard|zdf|wdr|ndr|swr|br|mdr|rbb)\b", re.IGNORECASE), + re.compile(r"\buntertitel\b.*\b(?:auftrag|community|amara)\b", re.IGNORECASE), + re.compile(r"\bamara\s*\.\s*org\b", re.IGNORECASE), + re.compile(r"\b(?:www\s*\.\s*)?schnellsessions?\s*\.\s*com\b", re.IGNORECASE), ) @@ -61,6 +62,10 @@ def _ensure_model(self): def transcribe(self, audio_base64, *, sample_rate=G2_SAMPLE_RATE, language="de", quality="balanced"): audio = self.decode_pcm(audio_base64, sample_rate=sample_rate) + import numpy as np + + if float(np.sqrt(np.mean(np.square(audio), dtype=np.float64))) < 0.0015: + return {"text": "", "language": str(language or ""), "language_probability": 0.0} selected_language = str(language or "de").strip().lower() if selected_language in {"", "auto"}: selected_language = None @@ -70,7 +75,7 @@ def transcribe(self, audio_base64, *, sample_rate=G2_SAMPLE_RATE, language="de", quality = str(quality or "balanced").strip().lower() if quality not in {"fast", "balanced", "precise"}: raise ValueError("Erkennungsqualitaet muss fast, balanced oder precise sein.") - beam_size = {"fast": 1, "balanced": 3, "precise": 5}[quality] + beam_size = {"fast": 1, "balanced": 2, "precise": 4}[quality] with self._lock: segments, info = self._ensure_model().transcribe( @@ -80,13 +85,37 @@ def transcribe(self, audio_base64, *, sample_rate=G2_SAMPLE_RATE, language="de", hotwords=TRINITY_HOTWORDS, condition_on_previous_text=False, vad_filter=True, + vad_parameters={ + "threshold": 0.5, + "min_speech_duration_ms": 180, + "min_silence_duration_ms": 250, + "speech_pad_ms": 120, + }, beam_size=beam_size, - best_of=beam_size, + best_of=1, temperature=0.0, + no_speech_threshold=0.55, + log_prob_threshold=-0.9, ) - text = " ".join(str(segment.text or "").strip() for segment in segments).strip() + accepted = [] + for segment in segments: + text = str(segment.text or "").strip() + no_speech = float(getattr(segment, "no_speech_prob", 0.0) or 0.0) + average_log_probability = float(getattr(segment, "avg_logprob", 0.0) or 0.0) + if no_speech > 0.65 and average_log_probability < -0.7: + continue + if text and not self.is_known_hallucination(text): + accepted.append(text) + text = " ".join(accepted).strip() + if self.is_known_hallucination(text): + text = "" return { "text": text, "language": str(getattr(info, "language", selected_language or "") or ""), "language_probability": float(getattr(info, "language_probability", 0.0) or 0.0), } + + @staticmethod + def is_known_hallucination(text): + normalized = " ".join(str(text or "").split()) + return bool(normalized) and any(pattern.search(normalized) for pattern in _KNOWN_SUBTITLE_HALLUCINATIONS) diff --git a/docs/release_notes/v0.17.10.md b/docs/release_notes/v0.17.10.md new file mode 100644 index 0000000..451f3df --- /dev/null +++ b/docs/release_notes/v0.17.10.md @@ -0,0 +1,6 @@ +# Trinity v0.17.10 - G2 Speech Reliability + +- Replaces the long G2 vocabulary bias with a neutral German recognition prompt and the single `Trinity` hotword. +- Rejects digital silence, low-confidence no-speech segments, and known Whisper subtitle hallucinations before they can reach Trinity. +- Uses smaller balanced and precise beam searches to reduce G2 latency without changing Desktop, Companion, or Eve speech paths. +- Adds regression coverage for `Copyright WDR`, `Amara.org`, fake Schnellsession domains, silence, and uncertain speech segments. diff --git a/pyproject.toml b/pyproject.toml index 1f1a32b..a8ddc3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "trinity-assistant" -version = "0.17.8" +version = "0.17.10" description = "Local-first academic personal concierge for macOS, Windows 11, and Linux servers" readme = "README.md" requires-python = ">=3.10,<3.15" diff --git a/tests/test_bridge_audio.py b/tests/test_bridge_audio.py index 24e95b9..552e422 100644 --- a/tests/test_bridge_audio.py +++ b/tests/test_bridge_audio.py @@ -54,18 +54,19 @@ def transcribe(self, _audio, **kwargs): transcriber = BridgeAudioTranscriber() transcriber._model = FakeModel() - encoded = base64.b64encode(b"\x00\x00" * 1600).decode("ascii") + encoded = base64.b64encode(np.full(1600, 1200, dtype="