3 Commits

Author SHA1 Message Date
Jaakko Vanhala
7a15cacebf Malli säilyy refreshin yli: automaattinen uudelleenlataus IndexedDB-cachesta
- coderSize tallennetaan localStorageen (valinta säilyy)
- Kun malli on kerran ladattu, 'kpn-coder-loaded' lippu asetetaan
- Sivulatauksessa: jos lippu on asetettu, ensureCoderNode() käynnistyy
  automaattisesti — painot tulevat IndexedDB-cachesta, ei verkosta
- Radio-napit asetetaan oikeaan tilaan localStoragesta

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:13:33 +03:00
Jaakko Vanhala
27135a8f14 Numeroidut mallilistat: kpn models ja kpn load tukevat numerovalintaa
kpn models näyttää:
  1  qwen-coder     Qwen2.5-Coder:0.5B  ~990 MB
  2  qwen-coder-3b  Qwen2.5-Coder:3B    ~6.2 GB
  3  smollm-135m    SmolLM 135M         ~270 MB
  ...

kpn load näyttää ladattavat mallit ja hyväksyy numeron:
  kpn load     → näytä lista
  kpn load 1   → lataa 0.5B
  kpn load 2   → lataa 3B
  kpn load 3b  → toimii myös nimellä

Jo ladattu malli merkitään ✓-merkillä.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:58:50 +03:00
Jaakko Vanhala
e28a715f32 Max tokens 128→256 + coder-3b malli agents-terminaaliin
- Oletustokenimäärä nostettu 256:een (monimutkaisemmat vastaukset mahtuvat)
- kpn run coder-3b "..." käynnistää 3B-mallin (parempi koodinlaatu)
- kpn load 3b lataa 3B-mallin (~6.2 GB)
- Tab-completion tukee coder-3b + esimerkkipromptit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:56:56 +03:00
2 changed files with 61 additions and 19 deletions

View File

@@ -258,13 +258,13 @@ pub async fn run_coder_inference(prompt: String, ws: Rc<RefCell<WebSocket>>, use
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&prompt) { if let Ok(json) = serde_json::from_str::<serde_json::Value>(&prompt) {
let p = json.get("prompt").and_then(|v| v.as_str()).unwrap_or(&prompt).to_string(); let p = json.get("prompt").and_then(|v| v.as_str()).unwrap_or(&prompt).to_string();
let s = json.get("system").and_then(|v| v.as_str()).unwrap_or(default_system).to_string(); let s = json.get("system").and_then(|v| v.as_str()).unwrap_or(default_system).to_string();
let m = json.get("max_tokens").and_then(|v| v.as_u64()).unwrap_or(128) as usize; let m = json.get("max_tokens").and_then(|v| v.as_u64()).unwrap_or(256) as usize;
(p, s, m) (p, s, m)
} else { } else {
(prompt.clone(), default_system.to_string(), 128) (prompt.clone(), default_system.to_string(), 256)
} }
} else { } else {
(prompt.clone(), default_system.to_string(), 128) (prompt.clone(), default_system.to_string(), 256)
}; };
// Prefill: aloitetaan vastaus ```-koodiblokkilla, jolloin malli jatkaa suoraan koodilla // Prefill: aloitetaan vastaus ```-koodiblokkilla, jolloin malli jatkaa suoraan koodilla

View File

@@ -1491,12 +1491,19 @@
let detectedWebGPU = false; let detectedWebGPU = false;
let detectedGpuInfo = null; let detectedGpuInfo = null;
let wasmInitialized = false; let wasmInitialized = false;
let coderSize = '05b'; // '05b' tai '3b' let coderSize = localStorage.getItem('kpn-coder-size') || '05b';
// Mallivalinnan radio-napit // Mallivalinnan radio-napit — asetetaan oikea valinta localStoragesta
const savedRadio = document.querySelector(`input[name="coder-size"][value="${coderSize}"]`);
if (savedRadio) savedRadio.checked = true;
if (coderSize === '3b') {
document.getElementById('coder-opt-05b')?.style && (document.getElementById('coder-opt-05b').style.borderColor = 'var(--border-color)');
document.getElementById('coder-opt-3b')?.style && (document.getElementById('coder-opt-3b').style.borderColor = 'var(--accent-color)');
}
document.querySelectorAll('input[name="coder-size"]').forEach(radio => { document.querySelectorAll('input[name="coder-size"]').forEach(radio => {
radio.addEventListener('change', (e) => { radio.addEventListener('change', (e) => {
coderSize = e.target.value; coderSize = e.target.value;
localStorage.setItem('kpn-coder-size', coderSize);
// Visuaalinen korostus // Visuaalinen korostus
document.getElementById('coder-opt-05b').style.borderColor = coderSize === '05b' ? 'var(--accent-color)' : 'var(--border-color)'; document.getElementById('coder-opt-05b').style.borderColor = coderSize === '05b' ? 'var(--accent-color)' : 'var(--border-color)';
document.getElementById('coder-opt-3b').style.borderColor = coderSize === '3b' ? 'var(--accent-color)' : 'var(--border-color)'; document.getElementById('coder-opt-3b').style.borderColor = coderSize === '3b' ? 'var(--accent-color)' : 'var(--border-color)';
@@ -1849,14 +1856,37 @@
} }
if (sub === 'load') { if (sub === 'load') {
const arg = parts[2];
const btn = document.getElementById('agent-compute-btn'); const btn = document.getElementById('agent-compute-btn');
if (btn && btn.dataset.state === 'ready') { // Mallikatalogista valinta numerolla tai nimellä
termLog(' ✓ Kielimalli on jo ladattu ja valmis', '#3fb950'); const loadModels = [
} else { { id: '1', key: '05b', name: 'Qwen2.5-Coder:0.5B', size: '~990 MB', coderSize: '05b' },
termLog(' Alustetaan laskentasolmua...', '#d29922'); { id: '2', key: '3b', name: 'Qwen2.5-Coder:3B', size: '~6.2 GB', coderSize: '3b' },
if (btn) btn.click(); // Käytetään samaa logiikkaa kuin napissa ];
else ensureCoderNode(); if (!arg) {
// Näytetään lista
termLog(' Ladattavat mallit:', '#c9d1d9');
for (const m of loadModels) {
const active = (btn?.dataset.state === 'ready' && coderSize === m.coderSize) ? ' <span style="color:#3fb950">✓ ladattu</span>' : '';
termLog(` <span style="color:#58a6ff">${m.id}</span> ${m.name} <span style="color:#8b949e">(${m.size})</span>${active}`);
} }
termLog(' Käyttö: kpn load &lt;numero&gt;', '#8b949e');
return;
}
const selected = loadModels.find(m => m.id === arg || m.key === arg || m.coderSize === arg);
if (!selected) {
termLog(` Tuntematon malli "${esc(arg)}". Kokeile: kpn load`, '#f85149');
return;
}
if (btn?.dataset.state === 'ready' && coderSize === selected.coderSize) {
termLog(`${selected.name} on jo ladattu ja valmis`, '#3fb950');
return;
}
coderSize = selected.coderSize;
localStorage.setItem('kpn-coder-size', coderSize);
termLog(` Alustetaan ${selected.name} (${selected.size})...`, '#d29922');
if (btn) btn.click();
else ensureCoderNode();
return; return;
} }
@@ -1868,11 +1898,14 @@
} }
if (sub === 'models') { if (sub === 'models') {
termLog(' smollm-135m — SmolLM 135M (kevyt)', '#a5d6ff'); termLog(' Käytettävissä olevat mallit:', '#c9d1d9');
termLog(' qwen-05b Qwen2.5 0.5B', '#a5d6ff'); termLog(' <span style="color:#58a6ff">1</span> qwen-coder Qwen2.5-Coder:0.5B <span style="color:#8b949e">~990 MB | koodin generointi</span>');
termLog(' phi3-mini — Phi-3 Mini', '#a5d6ff'); termLog(' <span style="color:#58a6ff">2</span> qwen-coder-3b Qwen2.5-Coder:3B <span style="color:#8b949e">~6.2 GB | parempi koodinlaatu</span>');
termLog(' qwen-coder — Qwen2.5-Coder 0.5B', '#a5d6ff'); termLog(' <span style="color:#58a6ff">3</span> smollm-135m SmolLM 135M <span style="color:#8b949e">~270 MB | kevyt, nopea</span>');
termLog(' qwen-coder-3b — Qwen2.5-Coder 3B', '#a5d6ff'); termLog(' <span style="color:#58a6ff">4</span> qwen-05b Qwen2.5:0.5B <span style="color:#8b949e">~990 MB | yleismalli</span>');
termLog(' <span style="color:#58a6ff">5</span> phi3-mini Phi-3 Mini <span style="color:#8b949e">~2.2 GB | Microsoftin malli</span>');
termLog(' Käyttö: kpn run &lt;malli&gt; "&lt;prompti&gt;"', '#8b949e');
termLog(' Lataus: kpn load &lt;numero&gt;', '#8b949e');
return; return;
} }
@@ -1905,7 +1938,9 @@
} }
// Jos käyttäjä syötti agentin nimen (esim. "coder"), vaihdetaan se oikeaksi tekoälymalliksi ("qwen-coder") // Jos käyttäjä syötti agentin nimen (esim. "coder"), vaihdetaan se oikeaksi tekoälymalliksi ("qwen-coder")
if (agentPrompts[model]) { if (model === 'coder-3b') {
model = 'qwen-coder-3b';
} else if (agentPrompts[model]) {
model = agentPrompts[model].model; model = agentPrompts[model].model;
} }
@@ -1919,12 +1954,14 @@
// Tab-completion: ennustava komennonsyöttö sana kerrallaan // Tab-completion: ennustava komennonsyöttö sana kerrallaan
const kpnCommands = { const kpnCommands = {
'kpn': ['help', 'run', 'pipeline', 'load', 'status', 'models', 'hello', 'clear'], 'kpn': ['help', 'run', 'pipeline', 'load', 'status', 'models', 'hello', 'clear'],
'kpn run': ['coder', 'manager', 'tester', 'qa', 'data', 'observer', 'qwen-coder', 'smollm-135m', 'qwen-05b', 'phi3-mini'], 'kpn run': ['coder', 'coder-3b', 'manager', 'tester', 'qa', 'data', 'observer', 'qwen-coder', 'qwen-coder-3b', 'smollm-135m', 'qwen-05b', 'phi3-mini'],
'kpn load': ['1', '2'],
'kpn pipeline': ['"'], 'kpn pipeline': ['"'],
}; };
// Esimerkkipromptit malleittain // Esimerkkipromptit malleittain
const kpnExamples = { const kpnExamples = {
'kpn run coder': ['"hello world in python"', '"fibonacci in rust"', '"quicksort in javascript"'], 'kpn run coder': ['"hello world in python"', '"fibonacci in rust"', '"quicksort in javascript"'],
'kpn run coder-3b': ['"binary search tree in rust"', '"REST API with Flask"', '"async web scraper in python"'],
'kpn run manager': ['"suunnittele REST API"', '"priorisoi tiimin tehtävät"'], 'kpn run manager': ['"suunnittele REST API"', '"priorisoi tiimin tehtävät"'],
'kpn run tester': ['"testaa login-toiminto"'], 'kpn run tester': ['"testaa login-toiminto"'],
'kpn pipeline': ['"rakenna todo-sovellus"', '"tee laskin pythonilla"'], 'kpn pipeline': ['"rakenna todo-sovellus"', '"tee laskin pythonilla"'],
@@ -2747,6 +2784,7 @@
if (cd) cd.style.background = '#3fb950'; if (cd) cd.style.background = '#3fb950';
if (cl) { cl.textContent = 'Qwen2.5-Coder'; cl.style.color = '#3fb950'; } if (cl) { cl.textContent = 'Qwen2.5-Coder'; cl.style.color = '#3fb950'; }
if (btn) { btn.dataset.state = 'ready'; btn.textContent = '✓ Valmis'; btn.style.borderColor = '#3fb950'; btn.style.color = '#3fb950'; btn.style.cursor = 'default'; btn.title = 'Kielimalli ladattu — oma kone on valmis laskentaan'; } if (btn) { btn.dataset.state = 'ready'; btn.textContent = '✓ Valmis'; btn.style.borderColor = '#3fb950'; btn.style.color = '#3fb950'; btn.style.cursor = 'default'; btn.title = 'Kielimalli ladattu — oma kone on valmis laskentaan'; }
localStorage.setItem('kpn-coder-loaded', 'true');
} }
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
@@ -2833,7 +2871,11 @@
} }
} }
// Agents-sivun coder-node käynnistetään "Alusta laskentasolmu" -napista tai kpn load -komennolla // Automaattinen uudelleenkäynnistys: jos malli oli ladattu ennen refreshiä, ladataan se uudelleen cachesta
if (localStorage.getItem('kpn-coder-loaded') === 'true') {
// Pieni viive jotta UI ehtii piirtyä
setTimeout(() => ensureCoderNode(), 500);
}
// Laskentasolmun käynnistys/pysäytys -nappi // Laskentasolmun käynnistys/pysäytys -nappi
let computeAbortController = null; let computeAbortController = null;