Viestien säätöä

This commit is contained in:
Jaakko Vanhala
2026-04-05 08:09:35 +03:00
parent 1845ddf8c2
commit 12fd3c4eae
2 changed files with 41 additions and 19 deletions

View File

@@ -108,7 +108,10 @@ async fn get_or_build_model(use_3b: bool, ws: &Rc<RefCell<WebSocket>>) -> Result
c.borrow().as_ref().map(|m| m.is_3b == use_3b).unwrap_or(false) c.borrow().as_ref().map(|m| m.is_3b == use_3b).unwrap_or(false)
}); });
if cache_hit { if cache_hit {
console_log!("[Coder] Malli löytyi muistista — ohitetaan lataus"); // Logitetaan kaikki välivaiheet valmiiksi, jotta pipeline-UI päivittyy
console_log!("[Coder] tokenizer löytyi (cache)");
console_log!("[Coder] model löytyi (cache)");
console_log!("[Coder] Malli ladattu (välimuistista)");
return Ok(()); return Ok(());
} }
@@ -160,7 +163,7 @@ async fn get_or_build_model(use_3b: bool, ws: &Rc<RefCell<WebSocket>>) -> Result
}; };
let model = QwenModel::new(&config, vb).map_err(|e| format!("Malli: {}", e))?; let model = QwenModel::new(&config, vb).map_err(|e| format!("Malli: {}", e))?;
console_log!("[Coder] Malli rakennettu ja välimuistitettu"); console_log!("[Coder] Malli ladattu ja välimuistitettu");
MODEL_CACHE.with(|c| { MODEL_CACHE.with(|c| {
*c.borrow_mut() = Some(CachedModel { model, tokenizer, is_3b: use_3b }); *c.borrow_mut() = Some(CachedModel { model, tokenizer, is_3b: use_3b });

View File

@@ -1107,7 +1107,19 @@
<script type="module"> <script type="module">
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';
// Päävälilehtien vaihto // HTML-escape kaikelle käyttäjä-/backendidatalle joka menee innerHTML:ään
function esc(str) {
if (!str) return '';
return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
// Poistaa system-promptin näkyvästä prompt-tekstistä (agents-pipeline lisää sen alkuun)
function stripSystemPrompt(prompt) {
if (!prompt) return '';
// Poistetaan kaikki ennen viimeistä kappaletta (system + agent promptit erotettu \n\n:llä)
const parts = prompt.split('\n\n');
return parts[parts.length - 1] || prompt;
}
// Agenttien system promptit // Agenttien system promptit
const agentPrompts = { const agentPrompts = {
client: { name: 'Asiakas — Projektin vaatimukset', model: 'user-input', default: 'Kirjoita tähän asiakkaan toiveet ja projektin vaatimukset. Orkestraattori (Manageri) purkaa ja delegoi nämä työt asiantuntijoille.' }, client: { name: 'Asiakas — Projektin vaatimukset', model: 'user-input', default: 'Kirjoita tähän asiakkaan toiveet ja projektin vaatimukset. Orkestraattori (Manageri) purkaa ja delegoi nämä työt asiantuntijoille.' },
@@ -1736,7 +1748,7 @@
const tokGen = data.tokens_generated || 0; const tokGen = data.tokens_generated || 0;
termLog(` <span style="color:#3fb950">✓</span> <span style="color:#58a6ff">${data.model || model}</span> <span style="color:#8b949e">(${tokGen} tok)</span>`); termLog(` <span style="color:#3fb950">✓</span> <span style="color:#58a6ff">${data.model || model}</span> <span style="color:#8b949e">(${tokGen} tok)</span>`);
if (!silent) { if (!silent) {
termLog(` ${response.replace(/</g,'&lt;').replace(/\n/g,'\n ')}`, '#c9d1d9'); termLog(` ${esc(response).replace(/\n/g,'\n ')}`, '#c9d1d9');
} }
return response; return response;
} catch (e) { } catch (e) {
@@ -1775,7 +1787,7 @@
} }
function termExec(cmd) { function termExec(cmd) {
termLog(`<span class="terminal-prompt">$</span> ${cmd.replace(/</g,'&lt;')}`); termLog(`<span class="terminal-prompt">$</span> ${esc(cmd)}`);
termHistory.unshift(cmd); termHistory.unshift(cmd);
termHistIdx = -1; termHistIdx = -1;
@@ -1919,7 +1931,7 @@
const cpt = parseFloat((r.chars_per_token || 0).toFixed(2)); const cpt = parseFloat((r.chars_per_token || 0).toFixed(2));
const cptColor = cpt >= 4 ? "#3fb950" : cpt >= 3 ? "#d29922" : "#f85149"; const cptColor = cpt >= 4 ? "#3fb950" : cpt >= 3 ? "#d29922" : "#f85149";
const renderTokens = (tokens) => (tokens || []).map(t => const renderTokens = (tokens) => (tokens || []).map(t =>
`<span class="tok tok-en">${t.replace(/</g,'&lt;')}</span>` `<span class="tok tok-en">${esc(t)}</span>`
).join(''); ).join('');
const tokHtml = renderTokens(r.tokens); const tokHtml = renderTokens(r.tokens);
const detailId = 'stok-' + Date.now(); const detailId = 'stok-' + Date.now();
@@ -1934,7 +1946,7 @@
<span style="color:#8b949e;font-size:13px">${typeof ms === 'number' ? ms.toFixed(2) : ms}ms</span> <span style="color:#8b949e;font-size:13px">${typeof ms === 'number' ? ms.toFixed(2) : ms}ms</span>
</div> </div>
</div> </div>
<div style="font-size:14px;color:#79b8ff;margin-bottom:6px">"${r.text || ''}"</div> <div style="font-size:14px;color:#79b8ff;margin-bottom:6px">"${esc(r.text)}"</div>
<div style="font-size:14px;display:flex;gap:16px"> <div style="font-size:14px;display:flex;gap:16px">
<span style="color:#8b949e">${r.char_count || 0} merkkiä</span> <span style="color:#8b949e">${r.char_count || 0} merkkiä</span>
<span style="color:#8b949e">${r.word_count || 0} sanaa</span> <span style="color:#8b949e">${r.word_count || 0} sanaa</span>
@@ -1957,8 +1969,8 @@
msgDiv.className = 'chat-msg'; msgDiv.className = 'chat-msg';
msgDiv.innerHTML = `<span class="chat-prompt">Tokenisoidaan...</span> msgDiv.innerHTML = `<span class="chat-prompt">Tokenisoidaan...</span>
<div style="font-size:12px;color:#8b949e"> <div style="font-size:12px;color:#8b949e">
<div><strong style="color:#58a6ff">EN</strong> "${data.en}"</div> <div><strong style="color:#58a6ff">EN</strong> "${esc(data.en)}"</div>
<div><strong style="color:#d29922">FI</strong> "${data.fi}"</div> <div><strong style="color:#d29922">FI</strong> "${esc(data.fi)}"</div>
</div>`; </div>`;
chatBox.appendChild(msgDiv); chatBox.appendChild(msgDiv);
if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild); if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild);
@@ -1997,7 +2009,7 @@
// Tokenilistat renderöitäväksi // Tokenilistat renderöitäväksi
const renderTokens = (tokens, cls) => (tokens || []).map(t => const renderTokens = (tokens, cls) => (tokens || []).map(t =>
`<span class="tok ${cls}">${t.replace(/</g,'&lt;')}</span>` `<span class="tok ${cls}">${esc(t)}</span>`
).join(''); ).join('');
const enTokHtml = renderTokens(en.tokens, 'tok-en'); const enTokHtml = renderTokens(en.tokens, 'tok-en');
const fiTokHtml = renderTokens(fi.tokens, 'tok-fi'); const fiTokHtml = renderTokens(fi.tokens, 'tok-fi');
@@ -2013,13 +2025,13 @@
</div> </div>
<div style="font-size:14px;display:grid;grid-template-columns:32px 1fr auto auto auto;gap:6px 10px;align-items:baseline"> <div style="font-size:14px;display:grid;grid-template-columns:32px 1fr auto auto auto;gap:6px 10px;align-items:baseline">
<strong style="color:#58a6ff">EN</strong> <strong style="color:#58a6ff">EN</strong>
<span style="color:#79b8ff">"${en.text || ''}"</span> <span style="color:#79b8ff">"${esc(en.text)}"</span>
<span style="color:#8b949e">${en.char_count} m</span> <span style="color:#8b949e">${en.char_count} m</span>
<span style="color:var(--accent-color);font-weight:600">${en.token_count} tok</span> <span style="color:var(--accent-color);font-weight:600">${en.token_count} tok</span>
<span style="color:${cptColor(enCpt)};font-weight:600">${enCpt} m/t</span> <span style="color:${cptColor(enCpt)};font-weight:600">${enCpt} m/t</span>
<strong style="color:#d29922">FI</strong> <strong style="color:#d29922">FI</strong>
<span style="color:#e3b341">"${fi.text || ''}"</span> <span style="color:#e3b341">"${esc(fi.text)}"</span>
<span style="color:#8b949e">${fi.char_count} m</span> <span style="color:#8b949e">${fi.char_count} m</span>
<span style="color:var(--accent-color);font-weight:600">${fi.token_count} tok</span> <span style="color:var(--accent-color);font-weight:600">${fi.token_count} tok</span>
<span style="color:${cptColor(fiCpt)};font-weight:600">${fiCpt} m/t</span> <span style="color:${cptColor(fiCpt)};font-weight:600">${fiCpt} m/t</span>
@@ -2084,10 +2096,10 @@
<span style="color:#8b949e;font-size:12px">${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms | ${tokS} tok/s</span> <span style="color:#8b949e;font-size:12px">${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms | ${tokS} tok/s</span>
</div> </div>
<div style="font-size:13px;color:#8b949e;margin-bottom:6px"> <div style="font-size:13px;color:#8b949e;margin-bottom:6px">
Prompt: <span style="color:#d29922">"${data.prompt || ''}"</span> Prompt: <span style="color:#d29922">"${esc(stripSystemPrompt(data.prompt))}"</span>
</div> </div>
<div style="font-size:14px;color:var(--text-color);line-height:1.5;${(model.includes('Coder') || (data.response||'').includes('def ')) ? 'font-family:Courier New,monospace;background:#010409;padding:10px;border-radius:4px;white-space:pre-wrap;font-size:12px' : ''}"> <div style="font-size:14px;color:var(--text-color);line-height:1.5;${(model.includes('Coder') || (data.response||'').includes('def ')) ? 'font-family:Courier New,monospace;background:#010409;padding:10px;border-radius:4px;white-space:pre-wrap;font-size:12px' : ''}">
${(data.response || '<em>tyhjä vastaus</em>').replace(/</g, '&lt;').replace(/>/g, '&gt;')} ${data.response ? esc(data.response) : '<em>tyhjä vastaus</em>'}
</div> </div>
<div style="margin-top:8px;font-size:12px;color:#8b949e"> <div style="margin-top:8px;font-size:12px;color:#8b949e">
${tokGen} tokenia generoitu | malli ladattu: ${typeof loadMs === 'number' ? loadMs.toFixed(0) : loadMs}ms ${tokGen} tokenia generoitu | malli ladattu: ${typeof loadMs === 'number' ? loadMs.toFixed(0) : loadMs}ms
@@ -2151,7 +2163,7 @@
<span style="color:#a371f7;font-weight:600">${model}</span> <span style="color:#a371f7;font-weight:600">${model}</span>
<span class="stream-counter" style="color:var(--accent-color);font-size:12px">0 tok</span> <span class="stream-counter" style="color:var(--accent-color);font-size:12px">0 tok</span>
</div> </div>
<div style="font-size:13px;color:#8b949e;margin-bottom:4px">Prompt: "${data.prompt || ''}"</div> <div style="font-size:13px;color:#8b949e;margin-bottom:4px">Prompt: "${esc(stripSystemPrompt(data.prompt))}"</div>
<div class="stream-text" style="font-size:14px;color:var(--text-color);line-height:1.5;${isCoder ? 'font-family:Courier New,monospace;background:#010409;padding:8px;border-radius:4px;white-space:pre-wrap;font-size:12px;color:#3fb950' : ''}"></div> <div class="stream-text" style="font-size:14px;color:var(--text-color);line-height:1.5;${isCoder ? 'font-family:Courier New,monospace;background:#010409;padding:8px;border-radius:4px;white-space:pre-wrap;font-size:12px;color:#3fb950' : ''}"></div>
<div style="margin-top:6px;font-size:11px;color:#d29922"> <div style="margin-top:6px;font-size:11px;color:#d29922">
<span class="spinner" style="display:inline-block;animation:spin 1s linear infinite">&#9696;</span> Generating... <span class="spinner" style="display:inline-block;animation:spin 1s linear infinite">&#9696;</span> Generating...
@@ -2175,7 +2187,7 @@
const term = document.getElementById('agent-terminal'); const term = document.getElementById('agent-terminal');
if (term) { if (term) {
const model = data.model || 'llm'; const model = data.model || 'llm';
const promptShort = (data.prompt || '').substring(0, 50).replace(/</g,'&lt;'); const promptShort = esc(stripSystemPrompt(data.prompt)).substring(0, 50);
const div = document.createElement('div'); const div = document.createElement('div');
div.className = 'terminal-line'; div.className = 'terminal-line';
div.innerHTML = `<span class="terminal-prompt">$</span> kpn run ${model} <span style="color:#8b949e">"${promptShort}"</span>`; div.innerHTML = `<span class="terminal-prompt">$</span> kpn run ${model} <span style="color:#8b949e">"${promptShort}"</span>`;
@@ -2365,7 +2377,7 @@
const tokGen = data.tokens_generated || 0; const tokGen = data.tokens_generated || 0;
const durMs = data.duration_ms || 0; const durMs = data.duration_ms || 0;
const tokS = data.tokens_per_sec || 0; const tokS = data.tokens_per_sec || 0;
const response = (data.response || '').replace(/</g, '&lt;').replace(/>/g, '&gt;'); const response = esc(data.response);
codeMetrics.tasks++; codeMetrics.tasks++;
codeMetrics.tokens += tokGen; codeMetrics.tokens += tokGen;
@@ -2386,7 +2398,7 @@
const card = document.createElement('div'); const card = document.createElement('div');
card.className = 'code-task-card'; card.className = 'code-task-card';
card.innerHTML = ` card.innerHTML = `
<div class="prompt">${data.prompt || ''}</div> <div class="prompt">${esc(stripSystemPrompt(data.prompt))}</div>
<div class="code-output">${highlightPython(response)}</div> <div class="code-output">${highlightPython(response)}</div>
<div class="meta"> <div class="meta">
${model} · ${tokGen} tokenia · ${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms · ${tokS} tok/s ${model} · ${tokGen} tokenia · ${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms · ${tokS} tok/s
@@ -2440,7 +2452,14 @@
if (msg.includes('[Coder]') && msg.includes('model') && msg.includes('löytyi')) { setStep('step-model', 'done', 'cache'); } if (msg.includes('[Coder]') && msg.includes('model') && msg.includes('löytyi')) { setStep('step-model', 'done', 'cache'); }
if (msg.includes('[Coder]') && msg.includes('model') && msg.includes('tallennettu')) { setStep('step-model', 'done', '100%'); } if (msg.includes('[Coder]') && msg.includes('model') && msg.includes('tallennettu')) { setStep('step-model', 'done', '100%'); }
if (msg.includes('[Coder]') && msg.includes('Rakennetaan')) { setStep('step-build', 'active'); } if (msg.includes('[Coder]') && msg.includes('Rakennetaan')) { setStep('step-build', 'active'); }
if (msg.includes('[Coder]') && msg.includes('Malli ladattu')) { setStep('step-build', 'done'); setStep('step-ready', 'done'); } if (msg.includes('[Coder]') && msg.includes('Malli ladattu')) {
// Malli on valmis — merkataan kaikki vaiheet valmiiksi (myös cache-hitillä)
setStep('step-wasm', 'done');
setStep('step-tokenizer', 'done');
setStep('step-model', 'done', 'cache');
setStep('step-build', 'done');
setStep('step-ready', 'done');
}
if (msg.includes('[Coder]') && msg.includes('Syöte:')) { if (msg.includes('[Coder]') && msg.includes('Syöte:')) {
// Pipeline piiloon kun generointi alkaa // Pipeline piiloon kun generointi alkaa
setTimeout(() => { document.getElementById('code-pipeline').style.display = 'none'; }, 1000); setTimeout(() => { document.getElementById('code-pipeline').style.display = 'none'; }, 1000);