fix(fleet): worker_hello periodisch wiederholen (wie Satellit) — Box wird nach Diagnostic/Bridge-Neustart wieder sichtbar

Worker sendeten worker_hello nur EINMAL beim Connect. Startet das Diagnostic
oder die Bridge NACH der Box neu, verpassen sie das hello (RVS spielt es nicht
nach) und sehen nur noch worker_ping → die Box taucht nicht in der Compute-
Flotte auf. Der Satellit ist genau deshalb zuverlaessig sichtbar: er wiederholt
sat_hello periodisch.

- alle vier Worker (f5tts/whisper/voxtral/llm-adapter): worker_hello wird jetzt
  zusaetzlich alle ~30s (jeder 3. Ping-Zyklus) wiederholt → ein neu gestartetes
  Diagnostic/Bridge lernt die Box innerhalb von 30s, ohne Box-Neustart.
- diagnostic/server.js: Voice-Reconcile (f5tts) laeuft nur beim ERSTEN/erneuten
  Auftauchen der Box, nicht bei jedem 30s-hello-Resend (sonst Push/Pull-Dauerlauf).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 04:12:16 +02:00
co-authored by Claude Opus 4.8
parent 831fbfab58
commit 2d2c182a0c
5 changed files with 41 additions and 17 deletions
+6 -2
View File
@@ -1076,6 +1076,10 @@ function connectRVS(forcePlain) {
const p = msg.payload || {};
if (p.instanceId) {
const prev = workers.get(p.instanceId) || {};
// War die Box vor diesem hello schon frisch gesehen? worker_hello wird
// jetzt alle ~30s wiederholt — Reconcile nur beim ERSTEN/erneuten
// Auftauchen, nicht bei jedem Resend.
const wasFresh = prev.last_seen && (Date.now() - prev.last_seen < WORKER_OFFLINE_MS);
workers.set(p.instanceId, {
instanceId: p.instanceId, service: p.service || "",
node: p.node || "", gpus: p.gpus || "", model: p.model || "",
@@ -1083,8 +1087,8 @@ function connectRVS(forcePlain) {
busy: !!prev.busy, last_seen: Date.now(),
});
broadcastWorkers();
// Voice-Flotte: f5tts-Box gerade online → Stimmen abgleichen/provisionieren.
if ((p.service || "") === "f5tts") reconcileVoices(p.instanceId, p.voices);
// Voice-Flotte: f5tts-Box NEU online → Stimmen abgleichen/provisionieren.
if ((p.service || "") === "f5tts" && !wasFresh) reconcileVoices(p.instanceId, p.voices);
}
} else if (msg.type === "worker_ping") {
// Heartbeat eines Workers (traegt busy-Status).
+12 -6
View File
@@ -842,17 +842,23 @@ async def _broadcast_status(ws, state: str, **extra) -> None:
async def _worker_register(ws, *, model: str = "", busy_fn=None) -> None:
"""Meldet diesen Worker bei der aria-bridge an (worker_hello) und haelt die
Flotten-Registry per periodischem worker_ping (mit busy-Status) frisch."""
Flotten-Registry per periodischem worker_ping frisch. worker_hello wird
zusaetzlich alle ~30s WIEDERHOLT (wie der Satellit sat_hello), damit auch ein
neu gestartetes Diagnostic/Bridge uns lernt — RVS spielt hellos nicht nach."""
def _hello():
return {"instanceId": INSTANCE_ID, "service": WORKER_SERVICE,
"node": NODE_NAME, "gpus": GPU_IDS, "model": model,
"voices": _local_voice_names()}
try:
await _send(ws, "worker_hello", {
"instanceId": INSTANCE_ID, "service": WORKER_SERVICE,
"node": NODE_NAME, "gpus": GPU_IDS, "model": model,
"voices": _local_voice_names(), # fuer Voice-Provisioning-Reconciliation
})
await _send(ws, "worker_hello", _hello())
n = 0
while True:
await asyncio.sleep(WORKER_PING_INTERVAL_S)
n += 1
busy = bool(busy_fn()) if busy_fn else False
await _send(ws, "worker_ping", {"instanceId": INSTANCE_ID, "busy": busy})
if n % 3 == 0:
await _send(ws, "worker_hello", _hello())
except asyncio.CancelledError:
raise
except Exception:
+4
View File
@@ -260,10 +260,14 @@ async def _worker_register(ws) -> None:
Flotten-Registry per periodischem worker_ping (mit busy-Status) frisch."""
try:
await _announce(ws)
n = 0
while True:
await asyncio.sleep(WORKER_PING_INTERVAL_S)
n += 1
await _send(ws, "worker_ping",
{"instanceId": INSTANCE_ID, "busy": _inflight > 0})
if n % 3 == 0: # ~30s worker_hello wiederholen (wie der Satellit) →
await _announce(ws) # auch neu gestartetes Diagnostic/Bridge lernt uns
except asyncio.CancelledError:
raise
except Exception:
+8 -4
View File
@@ -713,15 +713,19 @@ async def _broadcast_status(ws, state: str, **extra) -> None:
async def _worker_register(ws, *, model: str = "", busy_fn=None) -> None:
"""Meldet diesen Worker bei der aria-bridge an (worker_hello) und haelt die
Flotten-Registry per periodischem worker_ping (mit busy-Status) frisch."""
def _hello():
return {"instanceId": INSTANCE_ID, "service": WORKER_SERVICE,
"node": NODE_NAME, "gpus": GPU_IDS, "model": model}
try:
await _send(ws, "worker_hello", {
"instanceId": INSTANCE_ID, "service": WORKER_SERVICE,
"node": NODE_NAME, "gpus": GPU_IDS, "model": model,
})
await _send(ws, "worker_hello", _hello())
n = 0
while True:
await asyncio.sleep(WORKER_PING_INTERVAL_S)
n += 1
busy = bool(busy_fn()) if busy_fn else False
await _send(ws, "worker_ping", {"instanceId": INSTANCE_ID, "busy": busy})
if n % 3 == 0: # ~30s worker_hello wiederholen (wie der Satellit)
await _send(ws, "worker_hello", _hello())
except asyncio.CancelledError:
raise
except Exception:
+11 -5
View File
@@ -837,16 +837,22 @@ async def _broadcast_status(ws, state: str, **extra) -> None:
async def _worker_register(ws, *, model: str = "", busy_fn=None) -> None:
"""Meldet diesen Worker bei der aria-bridge an (worker_hello) und haelt die
Flotten-Registry per periodischem worker_ping (mit busy-Status) frisch."""
Flotten-Registry per periodischem worker_ping frisch. worker_hello wird alle
~30s WIEDERHOLT (wie der Satellit), damit ein neu gestartetes Diagnostic/
Bridge uns lernt — RVS spielt hellos nicht nach."""
def _hello():
return {"instanceId": INSTANCE_ID, "service": WORKER_SERVICE,
"node": NODE_NAME, "gpus": GPU_IDS, "model": model}
try:
await _send(ws, "worker_hello", {
"instanceId": INSTANCE_ID, "service": WORKER_SERVICE,
"node": NODE_NAME, "gpus": GPU_IDS, "model": model,
})
await _send(ws, "worker_hello", _hello())
n = 0
while True:
await asyncio.sleep(WORKER_PING_INTERVAL_S)
n += 1
busy = bool(busy_fn()) if busy_fn else False
await _send(ws, "worker_ping", {"instanceId": INSTANCE_ID, "busy": busy})
if n % 3 == 0:
await _send(ws, "worker_hello", _hello())
except asyncio.CancelledError:
raise
except Exception: