Viestien säätöä
This commit is contained in:
@@ -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 });
|
||||||
|
|||||||
@@ -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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||||
|
}
|
||||||
|
// 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,'<').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,'<')}`);
|
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,'<')}</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,'<')}</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, '<').replace(/>/g, '>')}
|
${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">◠</span> Generating...
|
<span class="spinner" style="display:inline-block;animation:spin 1s linear infinite">◠</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,'<');
|
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, '<').replace(/>/g, '>');
|
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);
|
||||||
|
|||||||
Reference in New Issue
Block a user