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:
+48
-12
@@ -1684,20 +1684,27 @@ class ARIABridge:
|
||||
if len(self._xtts_request_to_message) > 100:
|
||||
oldest = next(iter(self._xtts_request_to_message))
|
||||
self._xtts_request_to_message.pop(oldest, None)
|
||||
# Redundanz: freie f5tts-Instanz waehlen und gezielt adressieren.
|
||||
# None (keine Instanz bekannt / alle offline) → kein targetInstance,
|
||||
# Broadcast wie bisher (Single-Node laeuft unveraendert).
|
||||
tts_target = self._pick_worker("f5tts")
|
||||
tts_payload = {
|
||||
"text": tts_text,
|
||||
"voice": xtts_voice,
|
||||
"speed": xtts_speed,
|
||||
"language": "de",
|
||||
"requestId": xtts_request_id,
|
||||
"messageId": message_id,
|
||||
}
|
||||
if tts_target:
|
||||
tts_payload["targetInstance"] = tts_target
|
||||
await self._send_to_rvs({
|
||||
"type": "xtts_request",
|
||||
"payload": {
|
||||
"text": tts_text,
|
||||
"voice": xtts_voice,
|
||||
"speed": xtts_speed,
|
||||
"language": "de",
|
||||
"requestId": xtts_request_id,
|
||||
"messageId": message_id,
|
||||
},
|
||||
"payload": tts_payload,
|
||||
"timestamp": int(asyncio.get_event_loop().time() * 1000),
|
||||
})
|
||||
logger.info("[core] XTTS-Request gesendet (voice=%s, speed=%.2fx): '%s'",
|
||||
xtts_voice or "default", xtts_speed, tts_text[:60])
|
||||
logger.info("[core] XTTS-Request gesendet (voice=%s, speed=%.2fx, target=%s): '%s'",
|
||||
xtts_voice or "default", xtts_speed, tts_target or "(broadcast)", tts_text[:60])
|
||||
except Exception as e:
|
||||
logger.error("[core] XTTS-Request fehlgeschlagen: %s — kein Audio", e)
|
||||
|
||||
@@ -3551,6 +3558,23 @@ class ARIABridge:
|
||||
self._workers[iid]["gpus"] or "?", self._workers[iid]["model"] or "?")
|
||||
return
|
||||
|
||||
elif msg_type == "stt_lease_request":
|
||||
# Die App fragt vor dem Aufnahme-Stream, welche STT-Instanz sie
|
||||
# adressieren soll (Redundanz ueber mehrere Apps/Nodes). Wir waehlen
|
||||
# eine freie STT-Instanz und antworten per stt_lease. Ist keine
|
||||
# Instanz bekannt (instanceId leer), streamt die App wie bisher an
|
||||
# ALLE (Broadcast) — Single-Node bleibt unveraendert.
|
||||
req_id = (payload.get("requestId") or "").strip()
|
||||
iid = self._pick_stt_worker() or ""
|
||||
await self._send_to_rvs({
|
||||
"type": "stt_lease",
|
||||
"payload": {"requestId": req_id, "instanceId": iid},
|
||||
"timestamp": int(time.time() * 1000),
|
||||
})
|
||||
logger.info("[stt-lease] req=%s → %s", req_id[:8] if req_id else "?",
|
||||
iid or "(broadcast)")
|
||||
return
|
||||
|
||||
elif msg_type == "worker_ping":
|
||||
iid = (payload.get("instanceId") or "").strip()
|
||||
if iid:
|
||||
@@ -3982,8 +4006,14 @@ class ARIABridge:
|
||||
req_payload["tools"] = tools
|
||||
if model:
|
||||
req_payload["model"] = model
|
||||
logger.info("[rvs] llm_request → llm-adapter (id=%s, msgs=%d, max_tokens=%d, tools=%d, model=%s)",
|
||||
request_id[:8], len(messages), max_tokens, len(tools) if tools else 0, model or "-")
|
||||
# Redundanz/Multitasking: freie llm-Instanz gezielt adressieren; None
|
||||
# → Broadcast wie bisher. Mehrere Instanzen → parallele Turns.
|
||||
llm_target = self._pick_worker("llm")
|
||||
if llm_target:
|
||||
req_payload["targetInstance"] = llm_target
|
||||
logger.info("[rvs] llm_request → llm-adapter (id=%s, msgs=%d, max_tokens=%d, tools=%d, model=%s, target=%s)",
|
||||
request_id[:8], len(messages), max_tokens, len(tools) if tools else 0,
|
||||
model or "-", llm_target or "(broadcast)")
|
||||
ok = await self._send_to_rvs({
|
||||
"type": "llm_request",
|
||||
"payload": req_payload,
|
||||
@@ -4764,6 +4794,12 @@ class ARIABridge:
|
||||
chosen["busy"] = True # optimistisch, bis der naechste Ping korrigiert
|
||||
return chosen["instanceId"]
|
||||
|
||||
def _pick_stt_worker(self) -> Optional[str]:
|
||||
"""Waehlt eine STT-Instanz fuer ein App-Lease. Voxtral (Default-STT) hat
|
||||
Vorrang, Whisper ist der Fallback. None → keine online (App streamt dann
|
||||
ohne targetInstance = heutiges Broadcast-Verhalten)."""
|
||||
return self._pick_worker("voxtral") or self._pick_worker("whisper")
|
||||
|
||||
async def _satellite_request(self, op: str, satellite: str = "",
|
||||
device: str = "", action: str = "",
|
||||
params: Optional[dict] = None,
|
||||
|
||||
Reference in New Issue
Block a user