feat(compute): Redundanz-Routing per targetInstance (Stage 3)

Mehrere Instanzen pro Dienst nutzbar: Anfragen werden gezielt an eine freie
Instanz adressiert statt an alle gebroadcastet. Mehrere f5tts → naechstes
freies; mehrere LLM → parallele Turns (Multitasking); STT-Redundanz ueber
mehrere Apps via Lease.

- Worker (alle vier): filtern am Loop-Eingang — targetInstance gesetzt und
  != eigener INSTANCE_ID → Nachricht ignorieren. Feld fehlt → wie bisher.
- bridge (TTS/LLM, emittiert die Bridge selbst): _pick_worker() waehlt eine
  online+freie Instanz (Round-Robin), stempelt targetInstance auf
  xtts_request / llm_request. Keine Instanz bekannt → Broadcast.
- bridge (STT-Lease, emittiert die App): neuer stt_lease_request-Handler →
  _pick_stt_worker() (voxtral vor whisper) → stt_lease {instanceId}.
- app (audio.ts): requestSttLease() vor dem Stream, stempelt targetInstance
  auf stt_stream_start / stt_audio_chunk / stt_stream_end (+cancel). Kurzer
  Timeout → '' (Broadcast), Aufnahme haengt nie.

Voll rueckwaertskompatibel: Routing aktiviert sich erst, wenn Worker sich per
worker_hello (Stage 2) registriert haben — sonst bleibt alles Broadcast.
Braucht APK-Rebuild fuer die STT-Lease-Seite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 12:27:51 +02:00
co-authored by Claude Opus 4.8
parent f2ead1242f
commit ec78eb8efe
6 changed files with 118 additions and 14 deletions
+6
View File
@@ -751,6 +751,12 @@ async def run_loop(sessions: SessionManager) -> None:
continue
mtype = msg.get("type", "")
payload = msg.get("payload", {}) or {}
# Redundanz-Routing: ist die Anfrage gezielt an eine andere
# Instanz adressiert, ignorieren. Ohne targetInstance (Feld
# fehlt) → wie bisher, jeder Worker nimmt sie an.
tgt = payload.get("targetInstance")
if tgt and tgt != INSTANCE_ID:
continue
if mtype == "stt_stream_start":
sessions.start_session(payload)
elif mtype == "stt_audio_chunk":