From 9d93c3fe3d840313496c5fed2774210d6ff3e290 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Thu, 24 Sep 2026 17:22:27 +0200 Subject: [PATCH] =?UTF-8?q?feat(xtts):=20RVS=5FSNI=20in=20allen=20Compute-?= =?UTF-8?q?Bridges=20=E2=80=94=20AI-Box=20im=20RZ-Netz=20auf=20interne=20I?= =?UTF-8?q?P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Damit eine AI-Box (f5tts/whisper/voxtral/llm-adapter) auch im selben Netz wie der RVS direkt auf dessen interne IP verbinden kann (kein NAT-Hairpin ueber den externen Hostnamen), ohne am SNI/Cert zu scheitern: RVS_HOST= + RVS_SNI=. server_hostname im ssl-Context, gated auf use_tls (respektiert den TLS-Fallback). Leer = Verhalten wie bisher. Szenario: zweite Box im RZ + eine zuhause. Co-Authored-By: Claude Opus 4.8 --- xtts/.env.example | 7 +++++++ xtts/f5tts/bridge.py | 12 ++++++++++-- xtts/llm-adapter/adapter.py | 15 +++++++++++---- xtts/voxtral/bridge.py | 13 ++++++++++--- xtts/whisper/bridge.py | 12 ++++++++++-- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/xtts/.env.example b/xtts/.env.example index e799d1c..edff6bf 100644 --- a/xtts/.env.example +++ b/xtts/.env.example @@ -43,6 +43,13 @@ RVS_PORT=444 RVS_TLS=true RVS_TLS_FALLBACK=true RVS_TOKEN=dein_token_hier +# RVS_SNI: nur noetig, wenn die Box im SELBEN Netz wie der RVS steht und direkt +# auf dessen interne IP verbinden soll (kein NAT-Hairpin ueber den externen +# Hostnamen). Dann RVS_HOST= und hier den Zertifikats-/Hostnamen, fuer +# den Caddy sein Cert hat. Zuhause / normal: leer lassen. +# RVS_HOST=10.0.0.2 +# RVS_SNI=example.com +RVS_SNI= # ─── Optional ───────────────────────────────────── # HF_TOKEN= # nur falls ein HF-gated Modell (z.B. Voxtral) geladen wird diff --git a/xtts/f5tts/bridge.py b/xtts/f5tts/bridge.py index 137e7aa..9b7a64b 100644 --- a/xtts/f5tts/bridge.py +++ b/xtts/f5tts/bridge.py @@ -51,6 +51,9 @@ RVS_PORT = int(os.getenv("RVS_PORT", "443")) RVS_TLS = os.getenv("RVS_TLS", "true").lower() == "true" RVS_TLS_FALLBACK = os.getenv("RVS_TLS_FALLBACK", "true").lower() == "true" RVS_TOKEN = os.getenv("RVS_TOKEN", "").strip() +# TLS-Hostname (SNI + Cert), falls RVS_HOST eine IP ist (Box im selben Netz wie +# der RVS, direkt auf die interne IP). Leer = SNI = RVS_HOST. +RVS_SNI = os.getenv("RVS_SNI", "").strip() # F5-TTS Konfiguration # ───────────────────────────────────────────────────────────────── @@ -880,9 +883,14 @@ async def run_loop(runner: F5Runner) -> None: url = f"{scheme}://{RVS_HOST}:{RVS_PORT}?token={RVS_TOKEN}" masked = url.replace(RVS_TOKEN, "***") if RVS_TOKEN else url + connect_kwargs = {"ping_interval": 20, "ping_timeout": 10, "max_size": 50 * 1024 * 1024} + if use_tls and RVS_SNI: + import ssl + connect_kwargs["ssl"] = ssl.create_default_context() + connect_kwargs["server_hostname"] = RVS_SNI try: - logger.info("Verbinde zu RVS: %s", masked) - async with websockets.connect(url, ping_interval=20, ping_timeout=10, max_size=50 * 1024 * 1024) as ws: + logger.info("Verbinde zu RVS: %s%s", masked, f" (SNI {RVS_SNI})" if (use_tls and RVS_SNI) else "") + async with websockets.connect(url, **connect_kwargs) as ws: logger.info("RVS verbunden") retry_s = 2 tls_fallback_tried = False diff --git a/xtts/llm-adapter/adapter.py b/xtts/llm-adapter/adapter.py index 208955c..feda627 100644 --- a/xtts/llm-adapter/adapter.py +++ b/xtts/llm-adapter/adapter.py @@ -43,6 +43,9 @@ RVS_PORT = os.getenv("RVS_PORT", "443").strip() RVS_TLS = os.getenv("RVS_TLS", "true").lower() == "true" RVS_TLS_FALLBACK = os.getenv("RVS_TLS_FALLBACK", "true").lower() == "true" RVS_TOKEN = os.getenv("RVS_TOKEN", "").strip() +# TLS-Hostname (SNI + Cert), falls RVS_HOST eine IP ist (Box im selben Netz wie +# der RVS, direkt auf die interne IP). Leer = SNI = RVS_HOST. +RVS_SNI = os.getenv("RVS_SNI", "").strip() LLAMA_URL = os.getenv("LLAMA_URL", "http://llama:8081").rstrip("/") LLM_MODEL = os.getenv("LLM_MODEL", "qwen3-8b") @@ -419,11 +422,15 @@ async def _run() -> None: scheme = "wss" if use_tls else "ws" url = f"{scheme}://{RVS_HOST}:{RVS_PORT}?token={RVS_TOKEN}" masked = url.replace(RVS_TOKEN, "***") if RVS_TOKEN else url + connect_kwargs = {"ping_interval": 20, "ping_timeout": 10, "max_size": 16 * 1024 * 1024} + if use_tls and RVS_SNI: + import ssl + connect_kwargs["ssl"] = ssl.create_default_context() + connect_kwargs["server_hostname"] = RVS_SNI try: - logger.info("Verbinde zu RVS: %s (llama=%s)", masked, LLAMA_URL) - async with websockets.connect( - url, ping_interval=20, ping_timeout=10, max_size=16 * 1024 * 1024 - ) as ws: + logger.info("Verbinde zu RVS: %s%s (llama=%s)", masked, + f" (SNI {RVS_SNI})" if (use_tls and RVS_SNI) else "", LLAMA_URL) + async with websockets.connect(url, **connect_kwargs) as ws: logger.info("RVS verbunden — llm-adapter online") retry_s = 2 tls_fallback_tried = False diff --git a/xtts/voxtral/bridge.py b/xtts/voxtral/bridge.py index 99bc92e..e2c78bc 100644 --- a/xtts/voxtral/bridge.py +++ b/xtts/voxtral/bridge.py @@ -53,6 +53,9 @@ RVS_PORT = int(os.getenv("RVS_PORT", "443")) RVS_TLS = os.getenv("RVS_TLS", "true").lower() == "true" RVS_TLS_FALLBACK = os.getenv("RVS_TLS_FALLBACK", "true").lower() == "true" RVS_TOKEN = os.getenv("RVS_TOKEN", "").strip() +# TLS-Hostname (SNI + Cert), falls RVS_HOST eine IP ist (Box im selben Netz wie +# der RVS, direkt auf die interne IP). Leer = SNI = RVS_HOST. +RVS_SNI = os.getenv("RVS_SNI", "").strip() VOXTRAL_MODEL = os.getenv("VOXTRAL_MODEL", "mistralai/Voxtral-Mini-3B-2507") VOXTRAL_LANGUAGE = os.getenv("VOXTRAL_LANGUAGE", "de") @@ -744,10 +747,14 @@ async def run_loop(sessions: SessionManager) -> None: scheme = "wss" if use_tls else "ws" url = f"{scheme}://{RVS_HOST}:{RVS_PORT}?token={RVS_TOKEN}" masked = url.replace(RVS_TOKEN, "***") if RVS_TOKEN else url + connect_kwargs = {"ping_interval": 20, "ping_timeout": 10, "max_size": 50 * 1024 * 1024} + if use_tls and RVS_SNI: + import ssl + connect_kwargs["ssl"] = ssl.create_default_context() + connect_kwargs["server_hostname"] = RVS_SNI try: - logger.info("Verbinde zu RVS: %s", masked) - async with websockets.connect(url, ping_interval=20, ping_timeout=10, - max_size=50 * 1024 * 1024) as ws: + logger.info("Verbinde zu RVS: %s%s", masked, f" (SNI {RVS_SNI})" if (use_tls and RVS_SNI) else "") + async with websockets.connect(url, **connect_kwargs) as ws: logger.info("RVS verbunden") retry_s = 2 tls_fallback_tried = False diff --git a/xtts/whisper/bridge.py b/xtts/whisper/bridge.py index 42e254a..86c928e 100644 --- a/xtts/whisper/bridge.py +++ b/xtts/whisper/bridge.py @@ -53,6 +53,9 @@ RVS_PORT = int(os.getenv("RVS_PORT", "443")) RVS_TLS = os.getenv("RVS_TLS", "true").lower() == "true" RVS_TLS_FALLBACK = os.getenv("RVS_TLS_FALLBACK", "true").lower() == "true" RVS_TOKEN = os.getenv("RVS_TOKEN", "").strip() +# TLS-Hostname (SNI + Cert), falls RVS_HOST eine IP ist (Box im selben Netz wie +# der RVS, direkt auf die interne IP). Leer = SNI = RVS_HOST. +RVS_SNI = os.getenv("RVS_SNI", "").strip() WHISPER_MODEL = os.getenv("WHISPER_MODEL", "small") WHISPER_DEVICE = os.getenv("WHISPER_DEVICE", "cuda") @@ -876,9 +879,14 @@ async def run_loop(runner: WhisperRunner, sessions: SessionManager) -> None: scheme = "wss" if use_tls else "ws" url = f"{scheme}://{RVS_HOST}:{RVS_PORT}?token={RVS_TOKEN}" masked = url.replace(RVS_TOKEN, "***") if RVS_TOKEN else url + connect_kwargs = {"ping_interval": 20, "ping_timeout": 10, "max_size": 50 * 1024 * 1024} + if use_tls and RVS_SNI: + import ssl + connect_kwargs["ssl"] = ssl.create_default_context() + connect_kwargs["server_hostname"] = RVS_SNI try: - logger.info("Verbinde zu RVS: %s", masked) - async with websockets.connect(url, ping_interval=20, ping_timeout=10, max_size=50 * 1024 * 1024) as ws: + logger.info("Verbinde zu RVS: %s%s", masked, f" (SNI {RVS_SNI})" if (use_tls and RVS_SNI) else "") + async with websockets.connect(url, **connect_kwargs) as ws: logger.info("RVS verbunden") retry_s = 2 tls_fallback_tried = False