Korjattu Worker-laskentasolmun timing: odotetaan WS-yhteyden avautumista

Ongelma: start_agent_node palautui heti ennen kuin WebSocket ehti avautua.
Worker lähetti 'started' ja warmup lähti liian aikaisin → hub ei löytänyt
solmua → "Ei vapaata solmua" -virhe.

Korjaukset:
1. worker.js: kuuntelee "Yhteys Hubiin avattu" -logia Wasmista ja
   resolveaa started-Promisen vasta sen jälkeen (15s timeout)
2. worker.js: onerror + onunhandledrejection käsittelijät
3. worker.js: console.error välitetään pääsäikeelle
4. index.astro: ensureCoderNode odottaa (await) workerStarted-Promisea
   ennen warmupia ja pending-promptin lähetystä

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaakko Vanhala
2026-04-09 16:55:22 +03:00
parent 857afbe111
commit 0dc53dba1c
2 changed files with 74 additions and 26 deletions

View File

@@ -2,12 +2,35 @@
import init, { start_agent_node, set_gpu_load, set_auto_tasks } from './pkg/node.js'; import init, { start_agent_node, set_gpu_load, set_auto_tasks } from './pkg/node.js';
let wasmReady = false; let wasmReady = false;
let wsConnectedResolve = null; // Promise-resolve jota kutsutaan kun WS-yhteys aukeaa
// Välitetään console.log -viestit pääsäikeelle jotta UI-kuuntelijat näkevät ne // Välitetään console.log -viestit pääsäikeelle jotta UI-kuuntelijat näkevät ne
const _origLog = console.log; const _origLog = console.log;
console.log = function(...args) { console.log = function(...args) {
_origLog.apply(console, args); _origLog.apply(console, args);
self.postMessage({ type: 'log', message: args.join(' ') }); const msg = args.join(' ');
self.postMessage({ type: 'log', message: msg });
// Tunnistetaan WS-yhteyden avautuminen Wasm-logista
if (msg.includes('Yhteys Hubiin avattu') && wsConnectedResolve) {
wsConnectedResolve();
wsConnectedResolve = null;
}
};
// Välitetään myös console.error/warn
const _origError = console.error;
console.error = function(...args) {
_origError.apply(console, args);
self.postMessage({ type: 'error', message: args.join(' ') });
};
self.onerror = function(e) {
self.postMessage({ type: 'error', message: 'Worker error: ' + (e.message || e) });
};
self.onunhandledrejection = function(e) {
self.postMessage({ type: 'error', message: 'Unhandled rejection: ' + (e.reason || e) });
}; };
self.onmessage = async (e) => { self.onmessage = async (e) => {
@@ -22,10 +45,30 @@ self.onmessage = async (e) => {
self.postMessage({ type: 'error', message: 'WASM init: ' + err.message }); self.postMessage({ type: 'error', message: 'WASM init: ' + err.message });
} }
} else if (type === 'start') { } else if (type === 'start') {
if (!wasmReady) return; if (!wasmReady) {
self.postMessage({ type: 'error', message: 'WASM ei alustettu' });
return;
}
const { hubUrl, hasWebGPU, deviceInfo, taskId } = data; const { hubUrl, hasWebGPU, deviceInfo, taskId } = data;
try { try {
// Luodaan Promise joka resolvataan kun WS-yhteys aukeaa
const wsConnected = new Promise((resolve) => {
wsConnectedResolve = resolve;
// Timeout: jos yhteys ei aukea 15s:ssa
setTimeout(() => {
if (wsConnectedResolve) {
wsConnectedResolve = null;
resolve(); // Resolvataan silti jotta ei jäädä ikuisesti jumiin
self.postMessage({ type: 'error', message: 'WS-yhteys hubiin aikakatkaistiin (15s)' });
}
}, 15000);
});
await start_agent_node(hubUrl, hasWebGPU, deviceInfo, taskId); await start_agent_node(hubUrl, hasWebGPU, deviceInfo, taskId);
// Odotetaan kunnes WS-yhteys on oikeasti auki ja auth lähetetty
await wsConnected;
self.postMessage({ type: 'started' }); self.postMessage({ type: 'started' });
} catch (err) { } catch (err) {
self.postMessage({ type: 'error', message: 'Node: ' + err.message }); self.postMessage({ type: 'error', message: 'Node: ' + err.message });

View File

@@ -3678,26 +3678,32 @@ ${filesHtml}
}; };
const taskId = coderSize === '3b' ? 5 : 4; const taskId = coderSize === '3b' ? 5 : 4;
// Käynnistetään node Workerissa // Käynnistetään node Workerissa — odotetaan WS-yhteyden avautumista
const workerStarted = new Promise((resolve) => {
coderWorker.onmessage = (e) => { coderWorker.onmessage = (e) => {
if (e.data.type === 'started') { if (e.data.type === 'started') {
document.getElementById('coder-status').textContent = 'Connected'; document.getElementById('coder-status').textContent = 'Connected';
document.getElementById('coder-status').style.color = '#d29922'; document.getElementById('coder-status').style.color = '#d29922';
coderWsReady = true; coderWsReady = true;
resolve();
} else if (e.data.type === 'log') { } else if (e.data.type === 'log') {
// Workerin console.log → pääsäikeen kuuntelijat (tilaindikaattori, pipeline-stepit)
console.log(e.data.message); console.log(e.data.message);
} else if (e.data.type === 'error') { } else if (e.data.type === 'error') {
console.log('[Worker] Virhe: ' + e.data.message); console.error('[Worker] ' + e.data.message);
resolve(); // Resolvataan silti jotta ei jäädä jumiin
} }
}; };
});
coderWorker.postMessage({ coderWorker.postMessage({
type: 'start', type: 'start',
data: { hubUrl: wsUrl, hasWebGPU: false, deviceInfo: JSON.stringify(deviceInfo), taskId } data: { hubUrl: wsUrl, hasWebGPU: false, deviceInfo: JSON.stringify(deviceInfo), taskId }
}); });
// Warmup // Odotetaan kunnes Worker on yhdistänyt hubiin
setTimeout(() => { await workerStarted;
// Warmup — nyt WS on varmasti auki
if (uiSocket && uiSocket.readyState === 1) { if (uiSocket && uiSocket.readyState === 1) {
uiSocket.send(JSON.stringify({ uiSocket.send(JSON.stringify({
type: 'user_text', type: 'user_text',
@@ -3705,12 +3711,11 @@ ${filesHtml}
task_type: 'qwen-coder' task_type: 'qwen-coder'
})); }));
} }
}, 500);
if (pendingCodePrompt) { if (pendingCodePrompt) {
setTimeout(() => { setTimeout(() => {
sendCodeToHub(pendingCodePrompt); sendCodeToHub(pendingCodePrompt);
}, 2000); }, 500);
pendingCodePrompt = null; pendingCodePrompt = null;
} }
} catch(e) { } catch(e) {