diff --git a/network-poc/static/index.html b/network-poc/static/index.html
index 9417715..e37d710 100644
--- a/network-poc/static/index.html
+++ b/network-poc/static/index.html
@@ -1417,6 +1417,9 @@
document.querySelector(`.main-tab[onclick*="${tab}"]`).classList.add('active');
window.location.hash = tab;
+ // Siivotaan streaming-kortit näkymistä tab-vaihdon yhteydessä
+ document.querySelectorAll('.streaming-card').forEach(el => el.remove());
+
// Päivitetään admin-sessio vastaamaan nykyistä välilehteä
if (window._uiSocket && window._uiSocket.readyState === 1) {
const viewTask = tab === 'codelab' ? 'codelab-viewer' : 'viewer';
@@ -2034,64 +2037,95 @@
if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild);
chatBox.scrollTop = chatBox.scrollHeight;
} else if (data.type === "llm_done") {
- const term = document.getElementById('agent-terminal');
- if (term) {
- const model = data.model || 'llm';
+ // Reititetäänkö agents-näkymään vai codelab-näkymään?
+ const isAgentsTask = data.task_id && activeStreams[data.task_id];
+ const isCoder = (data.model || '').includes('Coder');
+
+ if (isAgentsTask) {
+ // Agents-pipeline: päivitetään terminaali
+ const term = document.getElementById('agent-terminal');
+ if (term) {
+ const model = data.model || 'llm';
+ const tokGen = data.tokens_generated || 0;
+ const durMs = typeof data.duration_ms === 'number' ? data.duration_ms.toFixed(0) : data.duration_ms || '?';
+ const tokS = data.tokens_per_sec || '?';
+ const div = document.createElement('div');
+ div.className = 'terminal-line';
+ div.style.color = '#a5d6ff';
+ div.innerHTML = ` ✓ ${model} ${tokGen} tok | ${durMs}ms | ${tokS} tok/s`;
+ term.appendChild(div);
+ while (term.children.length > 50) term.removeChild(term.firstChild);
+ term.scrollTop = term.scrollHeight;
+
+ document.querySelectorAll('.avatar-card').forEach(c => c.classList.remove('active'));
+ document.getElementById('avatar-kpn').classList.add('active');
+ }
+ } else if (isCoder) {
+ // Codelab: erillinen addCodeResult-handler käsittelee (rivi 2364)
+ // Poistetaan vain streaming-kortti codelabista
+ if (codeResults) codeResults.querySelector('.streaming-card')?.remove();
+ } else {
+ // Muu malli (network-näkymä): näytetään chatBoxissa
+ chatBox.querySelector('.streaming-card')?.remove();
+ chatBox.classList.remove('hidden');
+ const nodeId = data.node_id || "?";
+ const model = data.model || "LLM";
const tokGen = data.tokens_generated || 0;
- const durMs = typeof data.duration_ms === 'number' ? data.duration_ms.toFixed(0) : data.duration_ms || '?';
- const tokS = data.tokens_per_sec || '?';
- const div = document.createElement('div');
- div.className = 'terminal-line';
- div.style.color = '#a5d6ff';
- div.innerHTML = ` ✓ ${model} ${tokGen} tok | ${durMs}ms | ${tokS} tok/s`;
- term.appendChild(div);
- while (term.children.length > 50) term.removeChild(term.firstChild);
- term.scrollTop = term.scrollHeight;
-
- document.querySelectorAll('.avatar-card').forEach(c => c.classList.remove('active'));
- document.getElementById('avatar-kpn').classList.add('active');
+ const durMs = data.duration_ms || 0;
+ const tokS = data.tokens_per_sec || 0;
+ const loadMs = data.load_time_ms || 0;
+
+ const msgDiv = document.createElement('div');
+ msgDiv.className = 'chat-msg';
+ msgDiv.style.borderLeftColor = '#a371f7';
+ msgDiv.innerHTML = `
+
+ Solmu #${nodeId} — ${model}
+ ${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms | ${tokS} tok/s
+
+
+ Prompt: "${data.prompt || ''}"
+
+
+ ${(data.response || 'tyhjä vastaus').replace(//g, '>')}
+
+
+ ${tokGen} tokenia generoitu | malli ladattu: ${typeof loadMs === 'number' ? loadMs.toFixed(0) : loadMs}ms
+
`;
+ chatBox.appendChild(msgDiv);
+ if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild);
+ chatBox.scrollTop = chatBox.scrollHeight;
}
- // Poistetaan streaming-kortti
- chatBox.querySelector('.streaming-card')?.remove();
- chatBox.classList.remove('hidden');
- const nodeId = data.node_id || "?";
- const model = data.model || "LLM";
- const tokGen = data.tokens_generated || 0;
- const durMs = data.duration_ms || 0;
- const tokS = data.tokens_per_sec || 0;
- const loadMs = data.load_time_ms || 0;
-
- const msgDiv = document.createElement('div');
- msgDiv.className = 'chat-msg';
- msgDiv.style.borderLeftColor = '#a371f7';
- msgDiv.innerHTML = `
-
- Solmu #${nodeId} — ${model}
- ${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms | ${tokS} tok/s
-
-
- Prompt: "${data.prompt || ''}"
-
-
- ${(data.response || 'tyhjä vastaus').replace(//g, '>')}
-
-
- ${tokGen} tokenia generoitu | malli ladattu: ${typeof loadMs === 'number' ? loadMs.toFixed(0) : loadMs}ms
-
`;
- chatBox.appendChild(msgDiv);
- if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild);
- chatBox.scrollTop = chatBox.scrollHeight;
-
metrics.tasks++;
- metrics.totalTokens += tokGen;
- metrics.totalTimeMs += durMs;
+ metrics.totalTokens += (data.tokens_generated || 0);
+ metrics.totalTimeMs += (data.duration_ms || 0);
flashComputing();
updateMetrics();
- console.log(`[${model}] ${tokGen} tokenia | ${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms | ${tokS} tok/s | "${(data.response || '').substring(0, 60)}..."`);
+ console.log(`[${data.model || 'LLM'}] ${data.tokens_generated || 0} tokenia | ${typeof data.duration_ms === 'number' ? data.duration_ms.toFixed(0) : data.duration_ms || '?'}ms | ${data.tokens_per_sec || '?'} tok/s | "${(data.response || '').substring(0, 60)}..."`);
+ } else if (data.type === "llm_error") {
+ // Virheenkäsittely: siivotaan streaming-tila
+ const errMsg = data.error || 'Tuntematon virhe';
+ if (data.task_id && activeStreams[data.task_id]) {
+ // Agents-pipeline: näytetään virhe terminaalissa
+ activeStreams[data.task_id].remove();
+ delete activeStreams[data.task_id];
+ }
+ chatBox.querySelector('.streaming-card')?.remove();
+ if (codeResults) codeResults.querySelector('.streaming-card')?.remove();
+ const term = document.getElementById('agent-terminal');
+ if (term) {
+ const div = document.createElement('div');
+ div.className = 'terminal-line';
+ div.style.color = '#f85149';
+ div.innerHTML = ` ✗ LLM-virhe: ${errMsg}`;
+ term.appendChild(div);
+ term.scrollTop = term.scrollHeight;
+ }
+ console.warn('[LLM Error]', errMsg);
} else if (data.type === "llm_chunk") {
- // Terminaalin streaming: päivitetään aktiivinen rivi
+ // Agents-terminaalin streaming: päivitetään aktiivinen rivi task_id:n perusteella
if (data.task_id && activeStreams[data.task_id]) {
const streamDiv = activeStreams[data.task_id];
const contentEl = streamDiv.querySelector('.stream-content');
@@ -2099,41 +2133,42 @@
contentEl.textContent += data.token || '';
termPanel.scrollTop = termPanel.scrollHeight;
}
- }
+ // Agents-pipeline omistaa tämän chunkin, ei näytetä muualla
+ } else {
+ // Ei agents-task → näytetään streaming-kortti oikeassa näkymässä
+ const model = data.model || '';
+ const isCoder = model.includes('Coder');
+ const targetBox = isCoder ? codeResults : chatBox;
- // Streaming: näytetään generointi reaaliaikaisesti
- const model = data.model || '';
- const isCoder = model.includes('Coder');
- const targetBox = isCoder ? codeResults : chatBox;
-
- if (targetBox) {
- let streamEl = targetBox.querySelector('.streaming-card');
- if (!streamEl) {
- streamEl = document.createElement('div');
- streamEl.className = isCoder ? 'code-task-card streaming-card' : 'chat-msg streaming-card';
- streamEl.style.borderLeftColor = '#a371f7';
- streamEl.innerHTML = `
-
- ${model}
- 0 tok
-
- Prompt: "${data.prompt || ''}"
-
-
- ◠ Generating...
-
`;
- if (isCoder) {
- targetBox.insertBefore(streamEl, targetBox.firstChild);
- } else {
- targetBox.appendChild(streamEl);
+ if (targetBox) {
+ let streamEl = targetBox.querySelector('.streaming-card');
+ if (!streamEl) {
+ streamEl = document.createElement('div');
+ streamEl.className = isCoder ? 'code-task-card streaming-card' : 'chat-msg streaming-card';
+ streamEl.style.borderLeftColor = '#a371f7';
+ streamEl.innerHTML = `
+
+ ${model}
+ 0 tok
+
+ Prompt: "${data.prompt || ''}"
+
+
+ ◠ Generating...
+
`;
+ if (isCoder) {
+ targetBox.insertBefore(streamEl, targetBox.firstChild);
+ } else {
+ targetBox.appendChild(streamEl);
+ }
}
+ const textEl = streamEl.querySelector('.stream-text');
+ const counterEl = streamEl.querySelector('.stream-counter');
+ if (textEl) textEl.textContent += data.token || '';
+ const tokCount = (textEl.textContent || '').split('').length;
+ if (counterEl) counterEl.textContent = tokCount + ' tok';
+ targetBox.scrollTop = targetBox.scrollHeight;
}
- const textEl = streamEl.querySelector('.stream-text');
- const counterEl = streamEl.querySelector('.stream-counter');
- if (textEl) textEl.textContent += data.token || '';
- const tokCount = (textEl.textContent || '').split('').length;
- if (counterEl) counterEl.textContent = tokCount + ' tok';
- targetBox.scrollTop = targetBox.scrollHeight;
}
} else if (data.type === "llm_prompt") {
if (data.task_id) {
@@ -2360,11 +2395,13 @@
if (codeResults.children.length > 10) codeResults.removeChild(codeResults.lastChild);
}
- // Kuuntele coder-tuloksia UI WebSocketista
+ // Kuuntele coder-tuloksia UI WebSocketista (vain ei-agents-tehtävät)
uiSocket.addEventListener('message', (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'llm_done' && (data.model || '').includes('Coder')) {
+ // Ohita agents-pipelinen tehtävät — ne käsitellään kpnRun:issa
+ if (data.task_id && activeStreams[data.task_id]) return;
addCodeResult(data);
}
} catch(e) {}