fix(rvs): Datei-Antworten gezielt an den Fragenden statt an den ganzen Raum
Der RVS ist ein Broadcast-Relay — bisher bekam jeder Client im Raum jede Nachricht. Bei einem Datei-Re-Download war das fatal: ein 150-MB-Video wird als base64 ~200 MB gross und ging an ALLE Clients, auch an die STT/TTS-Worker. Die sind daran mit Code 1009 (message too big) gestorben und haben die gerade laufende Sprachaufnahme mitgerissen — die Ursache von "erste Sprachnachricht kommt nicht durch". Wer eine Anfrage mit requestId stellt, wird jetzt gemerkt; die Antwort geht nur an ihn zurueck (file_response, file_zip_response, file_version_download_response, brain_response). Unbekannte requestId wird wie bisher gebroadcastet, ein weggefallener Fragender verworfen statt an alle geschickt. Verifiziert mit drei WS-Clients gegen einen lokalen RVS: der Fragende bekommt seine Antwort, der Unbeteiligte nicht, chat bleibt Broadcast. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+106
@@ -124,6 +124,86 @@ const ALLOWED_TYPES = new Set([
|
||||
// Token-Raum: token -> { clients: Set<ws> }
|
||||
const rooms = new Map();
|
||||
|
||||
// ── Punkt-zu-Punkt-Antworten ────────────────────────────────────────
|
||||
//
|
||||
// Der RVS ist ein Broadcast-Relay: normalerweise bekommt JEDER Client im Raum
|
||||
// jede Nachricht. Bei Datei-Antworten ist das fatal. Ein 150-MB-Video wird als
|
||||
// base64 ~200 MB gross und ging bisher an ALLE Clients im Raum — auch an die
|
||||
// STT/TTS-Worker, die daran mit Code 1009 (message too big) gestorben sind und
|
||||
// die gerade laufende Sprachaufnahme mitgerissen haben ("erste Sprachnachricht
|
||||
// kommt nicht durch"). Die Worker-max_size-Angleichung verhindert den Absturz,
|
||||
// aber 200 MB an zehn Clients zu schicken bleibt Unsinn: nur EINER hat gefragt.
|
||||
//
|
||||
// Darum: wer eine Anfrage mit requestId stellt, wird gemerkt — die Antwort mit
|
||||
// derselben requestId geht nur an ihn zurueck. Unbekannte requestId → wie
|
||||
// bisher broadcasten (nichts geht verloren, falls ein Client mithoeren muss,
|
||||
// dessen Anfrage der RVS nie gesehen hat).
|
||||
const TARGETED = new Map([
|
||||
["file_request", "file_response"],
|
||||
["file_zip_request", "file_zip_response"],
|
||||
["file_version_download_request", "file_version_download_response"],
|
||||
["brain_request", "brain_response"],
|
||||
]);
|
||||
const TARGETED_REPLIES = new Set(TARGETED.values());
|
||||
|
||||
// `antwortTyp \0 requestId` -> { clients: Set<ws>, at }
|
||||
// Mehrere Clients koennen dieselbe requestId benutzen (die App nimmt die
|
||||
// Nachrichten-ID) — darum ein Set, sonst bekaeme nur der letzte die Antwort.
|
||||
const pendingReplies = new Map();
|
||||
const PENDING_TTL_MS = 180_000;
|
||||
|
||||
function pendingKey(replyType, requestId) {
|
||||
return `${replyType} | ||||