From 744a27cfd16fea0ffade649b707aba32e2eb687b Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 24 Apr 2026 17:46:47 +0200 Subject: [PATCH] fix: HF-Cache zurueck + Banner-Bug + config_request Pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vier Bugs in einem Aufwasch: 1. HF-Cache als Bind-Mount zurueck xtts/hf-cache:/root/.cache/huggingface fuer beide Bridges. War vorher raus, dadurch jedes Container-Restart = ~3GB Whisper-Download + ~1GB F5-TTS-Download. User dachte 5min ist einmalig — ist aber bei jedem Restart. Jetzt: einmal pro Maschine geladen, fertig. 2. Banner zeigte stale "ready" whisper-bridge sendete beim Connect nur dann Status wenn Modell schon geladen war. Sonst blieb der App/Diagnostic Banner auf dem alten "ready" State von vor dem Restart haengen — User sah "bereit" obwohl gerade gar nichts geladen war. Jetzt wird IMMER ein Status broadcast: ready oder loading. 3. config_request Pattern aria-bridge wusste nicht wann Gamebox-Bridges sich (re)connecten. Wenn die nach aria-bridge kamen, verpassten sie den Config-Broadcast und blieben mit Hard-Defaults stehen. Jetzt: whisper- und f5tts-bridge senden beim Connect ein config_request, aria-bridge antwortet mit der persistierten Config (whisperModel, xttsVoice, f5tts*-Felder). 4. RVS ALLOWED_TYPES um config_request erweitert. Co-Authored-By: Claude Opus 4.7 (1M context) --- bridge/aria_bridge.py | 11 +++++++++++ rvs/server.js | 1 + xtts/.gitignore | 4 ++++ xtts/docker-compose.yml | 17 ++++++++++------- xtts/f5tts/bridge.py | 21 ++++++++++++--------- xtts/whisper/bridge.py | 23 ++++++++++++++--------- 6 files changed, 52 insertions(+), 25 deletions(-) diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index 5b29890..2a3686e 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -1460,6 +1460,17 @@ class ARIABridge: logger.info("[rvs] whisper-bridge -> %s", state) return + elif msg_type == "config_request": + # Eine andere Bridge (whisper/f5tts) bittet um die aktuelle Voice- + # Config — passiert wenn sie sich connected, weil sie sonst die + # Diagnostic-Settings nicht kennt. Wir broadcasten die persistierte + # Config (auch beim normalen Connect von aria-bridge selber, aber + # da war eventuell die andere Bridge noch nicht connected). + requester = payload.get("service", "?") + logger.info("[rvs] config_request von %s — broadcaste Voice-Config", requester) + asyncio.create_task(self._broadcast_persisted_config()) + return + else: logger.debug("[rvs] Unbekannter Typ: %s", msg_type) diff --git a/rvs/server.js b/rvs/server.js index d55d74d..ab63e53 100644 --- a/rvs/server.js +++ b/rvs/server.js @@ -22,6 +22,7 @@ const ALLOWED_TYPES = new Set([ "voice_preload", "voice_ready", "stt_request", "stt_response", "service_status", + "config_request", ]); // Token-Raum: token -> { clients: Set } diff --git a/xtts/.gitignore b/xtts/.gitignore index fc4c60d..e650d90 100644 --- a/xtts/.gitignore +++ b/xtts/.gitignore @@ -1,3 +1,7 @@ +# HuggingFace Model-Cache (Whisper + F5-TTS, geteilt zwischen den +# beiden Bridges via Bind-Mount, kann mehrere GB werden) +hf-cache/ + # Voice-Samples (lokal, gehoert nicht ins Repo) voices/ diff --git a/xtts/docker-compose.yml b/xtts/docker-compose.yml index 7f31f92..bba432b 100644 --- a/xtts/docker-compose.yml +++ b/xtts/docker-compose.yml @@ -31,11 +31,11 @@ services: capabilities: [gpu] volumes: - ./voices:/voices # WAV + TXT Referenz - # KEIN HF-Cache-Mount mehr — - # Modell wird beim Start neu - # gezogen. Diagnostic zeigt - # "TTS laedt..." Banner bis - # service_status: ready kommt. + - ./hf-cache:/root/.cache/huggingface # HF-Cache als Bind-Mount. + # Direkt sichtbar im xtts/hf-cache/, + # einfach manuell zu loeschen, kein + # Docker-Desktop .vhdx Bloat. + # Wird mit whisper-bridge geteilt. environment: # Bootstrap-only — alle anderen F5-TTS-Settings (Modell, cfg_strength, # nfe_step, Custom-Checkpoint) kommen ueber Diagnostic via RVS-config. @@ -77,6 +77,9 @@ services: - WHISPER_DEVICE=${WHISPER_DEVICE:-cuda} - WHISPER_COMPUTE_TYPE=${WHISPER_COMPUTE_TYPE:-float16} - WHISPER_LANGUAGE=${WHISPER_LANGUAGE:-de} - # KEIN HF-Cache-Mount — Whisper-Modell wird beim Start neu gezogen. - # Wechsel via Diagnostic triggert ebenso Re-Download. + volumes: + - ./hf-cache:/root/.cache/huggingface # gleicher Cache wie f5tts-bridge — + # ein Modell muss nur einmal pro + # Maschine geladen werden, kein + # Re-Download bei Container-Restart. restart: unless-stopped diff --git a/xtts/f5tts/bridge.py b/xtts/f5tts/bridge.py index ffd8fe0..6603b98 100644 --- a/xtts/f5tts/bridge.py +++ b/xtts/f5tts/bridge.py @@ -615,20 +615,23 @@ async def run_loop(runner: F5Runner) -> None: # Status-Broadcast: erst loading, dann ready nach erfolgreichem Load. # Modell wird hier (nicht ausserhalb der Schleife) gestartet damit # der Loading-Status auch wirklich uebertragen werden kann. + # Plus: config_request damit wir die persistierte Diagnostic-Config + # bekommen, falls aria-bridge ihre nicht von alleine sendet. async def _load_with_status(): if runner.model is not None: await _broadcast_status(ws, "ready", model=runner.model_id, loadSeconds=runner.last_load_seconds) - return - await _broadcast_status(ws, "loading", model=runner.model_id) - try: - await runner.ensure_loaded() - await _broadcast_status(ws, "ready", - model=runner.model_id, - loadSeconds=runner.last_load_seconds) - except Exception as e: - await _broadcast_status(ws, "error", error=str(e)[:200]) + else: + await _broadcast_status(ws, "loading", model=runner.model_id) + try: + await runner.ensure_loaded() + await _broadcast_status(ws, "ready", + model=runner.model_id, + loadSeconds=runner.last_load_seconds) + except Exception as e: + await _broadcast_status(ws, "error", error=str(e)[:200]) + await _send(ws, "config_request", {"service": "f5tts"}) asyncio.create_task(_load_with_status()) # TTS-Worker fuer diese Verbindung starten diff --git a/xtts/whisper/bridge.py b/xtts/whisper/bridge.py index 90b38f0..8534413 100644 --- a/xtts/whisper/bridge.py +++ b/xtts/whisper/bridge.py @@ -220,15 +220,20 @@ async def run_loop(runner: WhisperRunner) -> None: retry_s = 2 tls_fallback_tried = False - # KEIN initialer Preload. Der aria-bridge broadcastet kurz nach - # RVS-Connect die persistierte Config (whisperModel) — wir laden - # erst wenn der drinsteht, sonst wuerde 2x geladen werden - # (small als ENV-Default + dann das echte Modell). - # Wenn ein stt_request schneller kommt als die Config: ensure_loaded - # im Handler greift dann ein und laedt das angeforderte Modell. - if runner.model is not None: - # Wir sind reconnectet — Modell schon im RAM, einfach 'ready' - asyncio.create_task(_broadcast_status(ws, "ready", model=runner.model_size)) + # Initialer Status-Broadcast — uebertont alten "ready"-State + # im App/Diagnostic Banner (sonst denkt der User noch alles ist + # gut von vorher). Wenn Modell schon geladen → ready, sonst + # loading mit aktuellem (Default-)Namen. + # Plus: config_request an aria-bridge — wir wissen nicht ob + # sie auch grad reconnected hat oder schon laenger online ist. + async def _initial_handshake(): + if runner.model is not None: + await _broadcast_status(ws, "ready", model=runner.model_size) + else: + await _broadcast_status(ws, "loading", model=runner.model_size or WHISPER_MODEL) + # aria-bridge soll uns die persistierte Voice-Config schicken + await _send(ws, "config_request", {"service": "whisper"}) + asyncio.create_task(_initial_handshake()) async for raw in ws: try: