koodilabran v0.1
This commit is contained in:
@@ -97,6 +97,69 @@
|
||||
h1 span { color: var(--accent-color); }
|
||||
.sub { color: #8b949e; margin-bottom: 25px; }
|
||||
|
||||
.main-tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.main-tab {
|
||||
padding: 10px 20px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #8b949e;
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -2px;
|
||||
transition: color 0.2s, border-color 0.2s;
|
||||
}
|
||||
.main-tab:hover { color: var(--text-color); }
|
||||
.main-tab.active { color: var(--accent-color); border-bottom-color: var(--accent-color); }
|
||||
.main-panel { display: none; }
|
||||
.main-panel.active { display: block; }
|
||||
|
||||
.code-output {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
background: #010409;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 14px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: var(--success-color);
|
||||
white-space: pre-wrap;
|
||||
overflow-x: auto;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.code-output .keyword { color: #ff7b72; }
|
||||
.code-output .string { color: #a5d6ff; }
|
||||
.code-output .comment { color: #8b949e; }
|
||||
|
||||
.code-task-card {
|
||||
background: #0d1117;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
padding: 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.code-task-card .prompt { color: #d29922; font-size: 14px; margin-bottom: 10px; }
|
||||
.code-task-card .meta { color: #8b949e; font-size: 12px; margin-top: 10px; }
|
||||
|
||||
.code-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 13px;
|
||||
color: #8b949e;
|
||||
padding: 6px 0;
|
||||
}
|
||||
.code-step.active { color: var(--accent-color); }
|
||||
.code-step.done { color: var(--success-color); }
|
||||
.code-step.error { color: #f85149; }
|
||||
.step-icon { font-size: 16px; width: 20px; text-align: center; }
|
||||
|
||||
.status-box {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
background-color: #010409;
|
||||
@@ -309,7 +372,16 @@
|
||||
<div class="container">
|
||||
<h1>Kipinä <span>Agent Dashboard</span></h1>
|
||||
<p class="sub">Hajautettu WebGPU Laskentaverkko · <span id="hub-version" style="color:#58a6ff">-</span></p>
|
||||
|
||||
|
||||
<!-- Päävälilehdet -->
|
||||
<div class="main-tabs">
|
||||
<div class="main-tab active" onclick="switchMainTab('network')">Laskentaverkko</div>
|
||||
<div class="main-tab" onclick="switchMainTab('codelab')">Koodilaboratorio</div>
|
||||
</div>
|
||||
|
||||
<!-- PANEELI 1: Laskentaverkko -->
|
||||
<div id="panel-network" class="main-panel active">
|
||||
|
||||
<!-- Global Cluster Statistics (UI) -->
|
||||
<div class="dashboard-panel">
|
||||
<div class="stat-box" style="border-right: 1px solid #30363d;">
|
||||
@@ -413,7 +485,7 @@
|
||||
<div style="background:#0d1117;border:1px solid var(--border-color);border-radius:6px;padding:16px;margin-bottom:16px">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
|
||||
<span style="font-weight:600;font-size:15px">Resurssien hallinta</span>
|
||||
<span id="node-status" style="font-size:12px;color:var(--success-color)">Aktiivinen</span>
|
||||
<span id="node-status" style="font-size:12px;color:#8b949e">Ei yhdistetty</span>
|
||||
</div>
|
||||
|
||||
<!-- Kuormitussäädin -->
|
||||
@@ -449,6 +521,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="user-input-box" class="hidden" style="background:#0d1117;border:1px solid var(--border-color);border-radius:6px;padding:12px;margin-bottom:12px">
|
||||
<div style="font-size:13px;color:#8b949e;margin-bottom:8px">Kokeile omaa tekstiä:</div>
|
||||
<div style="display:flex;gap:8px">
|
||||
<input type="text" id="user-text" placeholder="Kirjoita teksti tokenisoitavaksi tai promptiksi..." style="flex:1;background:var(--panel-bg);border:1px solid var(--border-color);border-radius:4px;padding:8px 12px;color:var(--text-color);font-size:14px;outline:none">
|
||||
<button id="send-btn" style="background:#238636;color:#fff;border:1px solid rgba(240,246,252,0.1);border-radius:4px;padding:8px 16px;font-size:14px;cursor:pointer;white-space:nowrap">Tokenisoi</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="chat-box" class="chat-box hidden">
|
||||
<div style="color: #8b949e; text-align: center; margin-top: 80px;">Odotetaan Generointitehtäviä Hubilta...</div>
|
||||
</div>
|
||||
@@ -457,11 +537,127 @@
|
||||
<p>> Odotetaan uusia tehtäviä Hubulta...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /panel-network -->
|
||||
|
||||
<!-- PANEELI 2: Koodilaboratorio -->
|
||||
<div id="panel-codelab" class="main-panel">
|
||||
<div style="background:#0d1117;border:1px solid var(--border-color);border-radius:6px;padding:16px;margin-bottom:16px">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
||||
<span style="font-weight:600;font-size:15px">Qwen2.5-Coder-0.5B-Instruct</span>
|
||||
<span id="coder-status" style="font-size:12px;color:#8b949e">Ei yhdistetty</span>
|
||||
</div>
|
||||
<p style="font-size:12px;color:#8b949e;line-height:1.5;margin-bottom:12px">
|
||||
Code-specialized language model trained on 5.5T tokens of source code.
|
||||
Generates Python code in your browser via WebAssembly. Choose model size and write your own prompt.
|
||||
</p>
|
||||
<!-- Model size selector -->
|
||||
<div style="display:flex;gap:8px;margin-bottom:10px">
|
||||
<label style="flex:1;display:flex;align-items:center;gap:6px;background:var(--panel-bg);border:2px solid var(--accent-color);border-radius:4px;padding:8px 12px;cursor:pointer;font-size:13px" id="coder-opt-05b">
|
||||
<input type="radio" name="coder-size" value="05b" checked style="accent-color:var(--accent-color)">
|
||||
<div>
|
||||
<strong style="color:var(--text-color)">0.5B</strong>
|
||||
<span style="color:#8b949e"> — 990 MB, ~0.4 tok/s</span>
|
||||
</div>
|
||||
</label>
|
||||
<label style="flex:1;display:flex;align-items:center;gap:6px;background:var(--panel-bg);border:2px solid var(--border-color);border-radius:4px;padding:8px 12px;cursor:pointer;font-size:13px" id="coder-opt-3b">
|
||||
<input type="radio" name="coder-size" value="3b" style="accent-color:var(--accent-color)">
|
||||
<div>
|
||||
<strong style="color:var(--text-color)">3B</strong>
|
||||
<span style="color:#8b949e"> — 6.2 GB, better quality, slower</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px">
|
||||
<input type="text" id="code-input" placeholder="e.g. Write a Python function that checks if a number is prime" style="flex:1;background:var(--panel-bg);border:1px solid var(--border-color);border-radius:4px;padding:8px 12px;color:var(--text-color);font-size:14px;outline:none">
|
||||
<button id="code-send-btn" style="background:#238636;color:#fff;border:1px solid rgba(240,246,252,0.1);border-radius:4px;padding:8px 16px;font-size:14px;cursor:pointer">Generate</button>
|
||||
</div>
|
||||
<div id="code-loading" style="display:none;margin-top:8px;font-size:12px;color:#d29922">Starting Coder model...</div>
|
||||
</div>
|
||||
|
||||
<!-- Koodilaboratorion metriikat -->
|
||||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:16px">
|
||||
<div class="metric-card">
|
||||
<div class="metric-val" id="code-m-tasks">0</div>
|
||||
<div class="metric-label">Tehtäviä</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-val" id="code-m-tokens">0</div>
|
||||
<div class="metric-label">Tokeneita</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-val" id="code-m-speed">-</div>
|
||||
<div class="metric-label">tok/s</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Latausvaiheet -->
|
||||
<div id="code-pipeline" style="background:#0d1117;border:1px solid var(--border-color);border-radius:6px;padding:16px;margin-bottom:16px;display:none">
|
||||
<div style="font-size:13px;font-weight:600;margin-bottom:12px">Valmistautuminen</div>
|
||||
<div id="code-steps" style="display:flex;flex-direction:column;gap:8px">
|
||||
<div class="code-step" id="step-wasm">
|
||||
<span class="step-icon">◯</span>
|
||||
<span>WebAssembly-ytimen lataus</span>
|
||||
</div>
|
||||
<div class="code-step" id="step-tokenizer">
|
||||
<span class="step-icon">◯</span>
|
||||
<span>Tokenizer (7 MB)</span>
|
||||
</div>
|
||||
<div class="code-step" id="step-model">
|
||||
<span class="step-icon">◯</span>
|
||||
<span>Qwen2.5-Coder-0.5B painot (990 MB)</span>
|
||||
<span id="step-model-pct" style="color:var(--accent-color);margin-left:auto;font-size:12px"></span>
|
||||
</div>
|
||||
<div class="code-step" id="step-build">
|
||||
<span class="step-icon">◯</span>
|
||||
<span>Mallin rakentaminen muistiin</span>
|
||||
</div>
|
||||
<div class="code-step" id="step-ready">
|
||||
<span class="step-icon">◯</span>
|
||||
<span>Valmis generoimaan</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kooditulokset -->
|
||||
<div id="code-results" style="display:flex;flex-direction:column;gap:12px">
|
||||
<div data-placeholder style="color:#8b949e;text-align:center;padding:40px">Kirjoita ohjelmointitehtävä ja paina Koodaa</div>
|
||||
</div>
|
||||
</div><!-- /panel-codelab -->
|
||||
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import init, { start_agent_node, set_gpu_load } from './pkg/node.js';
|
||||
|
||||
// Päävälilehtien vaihto
|
||||
window.switchMainTab = function(tab) {
|
||||
document.querySelectorAll('.main-panel').forEach(p => p.classList.remove('active'));
|
||||
document.querySelectorAll('.main-tab').forEach(t => t.classList.remove('active'));
|
||||
document.getElementById('panel-' + tab).classList.add('active');
|
||||
event.target.classList.add('active');
|
||||
};
|
||||
|
||||
// Koodilaboratorion tila
|
||||
const codeMetrics = { tasks: 0, tokens: 0, lastSpeed: 0 };
|
||||
let coderJoined = false;
|
||||
let coderSize = '05b'; // '05b' tai '3b'
|
||||
|
||||
// Mallivalinnan radio-napit
|
||||
document.querySelectorAll('input[name="coder-size"]').forEach(radio => {
|
||||
radio.addEventListener('change', (e) => {
|
||||
coderSize = e.target.value;
|
||||
// Visuaalinen korostus
|
||||
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)';
|
||||
// Jos jo liittynyt, pitää liittyä uudelleen toisella mallilla
|
||||
if (coderJoined) {
|
||||
coderJoined = false;
|
||||
document.getElementById('coder-status').textContent = 'Model changed — rejoin on next generate';
|
||||
document.getElementById('coder-status').style.color = '#d29922';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const btn = document.getElementById('start-btn');
|
||||
const logBox = document.getElementById('log-box');
|
||||
const loadSlider = document.getElementById('gpu-load');
|
||||
@@ -506,6 +702,20 @@
|
||||
}
|
||||
setInterval(updateMetrics, 1000);
|
||||
|
||||
// Laskentaverkko: status Connected (keltainen) ↔ Computing (vihreä)
|
||||
let computingTimer = null;
|
||||
function flashComputing() {
|
||||
const el = document.getElementById('node-status');
|
||||
if (!el || !window.wasm_active) return;
|
||||
el.textContent = 'Computing';
|
||||
el.style.color = 'var(--success-color)';
|
||||
clearTimeout(computingTimer);
|
||||
computingTimer = setTimeout(() => {
|
||||
el.textContent = 'Connected';
|
||||
el.style.color = '#d29922';
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Ylikirjoitetaan console.log uppoamaan lokilaatikkoon
|
||||
const originalLog = console.log;
|
||||
console.log = function(...args) {
|
||||
@@ -546,6 +756,28 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Käyttäjän oma tekstisyöte
|
||||
const userInput = document.getElementById('user-text');
|
||||
const sendBtn = document.getElementById('send-btn');
|
||||
|
||||
function sendUserText() {
|
||||
const text = userInput.value.trim();
|
||||
if (!text || !uiSocket || uiSocket.readyState !== 1) return;
|
||||
const msg = JSON.stringify({
|
||||
type: 'user_text',
|
||||
text: text,
|
||||
task_type: selectedTask,
|
||||
});
|
||||
uiSocket.send(msg);
|
||||
userInput.value = '';
|
||||
console.log(`Lähetetty: "${text}" (${selectedTask})`);
|
||||
}
|
||||
|
||||
sendBtn?.addEventListener('click', sendUserText);
|
||||
userInput?.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') sendUserText();
|
||||
});
|
||||
|
||||
// Kytkemme sivuston UI-puolen (JS) omaan passiiviseen WebSocket-kuuntelijaan.
|
||||
const uiSocket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`);
|
||||
uiSocket.onmessage = (event) => {
|
||||
@@ -608,6 +840,7 @@
|
||||
metrics.totalTokens += (en.token_count || 0) + (fi.token_count || 0);
|
||||
metrics.totalTimeMs += ms;
|
||||
updateMetrics();
|
||||
flashComputing();
|
||||
|
||||
// Lokiboksiin yhteenveto
|
||||
console.log(`EN: ${en.token_count} tokenia (${(en.chars_per_token||0).toFixed(2)} m/t) vs FI: ${fi.token_count} tokenia (${(fi.chars_per_token||0).toFixed(2)} m/t) | ylikustannus: ${overhead}% | ${typeof ms === 'number' ? ms.toFixed(2) : ms}ms`);
|
||||
@@ -688,8 +921,8 @@
|
||||
<div style="font-size:13px;color:#8b949e;margin-bottom:6px">
|
||||
Prompt: <span style="color:#d29922">"${data.prompt || ''}"</span>
|
||||
</div>
|
||||
<div style="font-size:14px;color:var(--text-color);line-height:1.5">
|
||||
${data.response || '<em>tyhjä vastaus</em>'}
|
||||
<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, '>')}
|
||||
</div>
|
||||
<div style="margin-top:8px;font-size:12px;color:#8b949e">
|
||||
${tokGen} tokenia generoitu | malli ladattu: ${typeof loadMs === 'number' ? loadMs.toFixed(0) : loadMs}ms
|
||||
@@ -701,6 +934,7 @@
|
||||
metrics.tasks++;
|
||||
metrics.totalTokens += tokGen;
|
||||
metrics.totalTimeMs += durMs;
|
||||
flashComputing();
|
||||
updateMetrics();
|
||||
|
||||
console.log(`[${model}] ${tokGen} tokenia | ${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms | ${tokS} tok/s | "${(data.response || '').substring(0, 60)}..."`);
|
||||
@@ -812,26 +1046,210 @@
|
||||
|
||||
document.getElementById('initial-state').classList.add('hidden');
|
||||
document.getElementById('active-state').classList.remove('hidden');
|
||||
document.getElementById('user-input-box').classList.remove('hidden');
|
||||
btn.style.display = 'none';
|
||||
|
||||
// Nappin teksti ja placeholder tehtävän mukaan
|
||||
const sendBtnEl = document.getElementById('send-btn');
|
||||
if (selectedTask === 'tokenize') {
|
||||
sendBtnEl.textContent = 'Tokenisoi';
|
||||
document.getElementById('user-text').placeholder = 'Kirjoita teksti tokenisoitavaksi...';
|
||||
} else if (selectedTask === 'qwen-coder') {
|
||||
sendBtnEl.textContent = 'Koodaa';
|
||||
document.getElementById('user-text').placeholder = 'Kuvaile Python-ohjelmointitehtävä...';
|
||||
} else {
|
||||
sendBtnEl.textContent = 'Generoi';
|
||||
document.getElementById('user-text').placeholder = 'Kirjoita prompti kielimallille...';
|
||||
}
|
||||
|
||||
try {
|
||||
console.log("Ladataan Burn Wasm -binääriä...");
|
||||
await init();
|
||||
window.wasm_active = true;
|
||||
metrics.startTime = Date.now();
|
||||
|
||||
// Asetetaan Connected-tila (keltainen) — vihreäksi vasta kun laskentaa tapahtuu
|
||||
const nodeStatusEl = document.getElementById('node-status');
|
||||
nodeStatusEl.textContent = 'Connected';
|
||||
nodeStatusEl.style.color = '#d29922';
|
||||
|
||||
// Varmistetaan, että Wasm saa nykyisen sliderin arvon heti kärkeen
|
||||
set_gpu_load(parseInt(loadSlider.value));
|
||||
|
||||
// WebAssembly yhdistää oikeaksi Agent Nodeksi
|
||||
const wsUrl = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`;
|
||||
const taskIds = {'tokenize': 0, 'smollm-135m': 1, 'qwen-05b': 2, 'phi3-mini': 3};
|
||||
const taskIds = {'tokenize': 0, 'smollm-135m': 1, 'qwen-05b': 2, 'phi3-mini': 3, 'qwen-coder-05b': 4, 'qwen-coder-3b': 5};
|
||||
const taskId = taskIds[selectedTask] || 0;
|
||||
await start_agent_node(wsUrl, hasWebGPU, JSON.stringify(deviceInfo), taskId);
|
||||
} catch(e) {
|
||||
console.log("Virhe GPU-käynnistyksessä: " + e);
|
||||
}
|
||||
});
|
||||
|
||||
// === Koodilaboratorio ===
|
||||
const codeInput = document.getElementById('code-input');
|
||||
const codeSendBtn = document.getElementById('code-send-btn');
|
||||
const codeResults = document.getElementById('code-results');
|
||||
const codeLoading = document.getElementById('code-loading');
|
||||
let coderWsReady = false;
|
||||
let coderWs = null; // Erillinen WS coder-nodelle
|
||||
let pendingCodePrompt = null;
|
||||
|
||||
function addCodeResult(data) {
|
||||
const model = data.model || 'Coder';
|
||||
const tokGen = data.tokens_generated || 0;
|
||||
const durMs = data.duration_ms || 0;
|
||||
const tokS = data.tokens_per_sec || 0;
|
||||
const response = (data.response || '').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
codeMetrics.tasks++;
|
||||
codeMetrics.tokens += tokGen;
|
||||
codeMetrics.lastSpeed = tokS;
|
||||
document.getElementById('code-m-tasks').textContent = codeMetrics.tasks;
|
||||
document.getElementById('code-m-tokens').textContent = codeMetrics.tokens.toLocaleString('fi-FI');
|
||||
document.getElementById('code-m-speed').textContent = tokS + ' tok/s';
|
||||
|
||||
if (codeResults.querySelector('[data-placeholder]')) {
|
||||
codeResults.innerHTML = '';
|
||||
}
|
||||
codeLoading.style.display = 'none';
|
||||
codeSendBtn.disabled = false;
|
||||
codeSendBtn.textContent = 'Generate';
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'code-task-card';
|
||||
card.innerHTML = `
|
||||
<div class="prompt">${data.prompt || ''}</div>
|
||||
<div class="code-output">${response}</div>
|
||||
<div class="meta">
|
||||
${model} · ${tokGen} tokenia · ${typeof durMs === 'number' ? durMs.toFixed(0) : durMs}ms · ${tokS} tok/s
|
||||
</div>`;
|
||||
codeResults.insertBefore(card, codeResults.firstChild);
|
||||
if (codeResults.children.length > 10) codeResults.removeChild(codeResults.lastChild);
|
||||
}
|
||||
|
||||
// Kuuntele coder-tuloksia UI WebSocketista
|
||||
uiSocket.addEventListener('message', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'llm_done' && (data.model || '').includes('Coder')) {
|
||||
addCodeResult(data);
|
||||
}
|
||||
} catch(e) {}
|
||||
});
|
||||
|
||||
// Pipeline-vaiheiden päivitys
|
||||
function setStep(id, state, extra) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
el.className = 'code-step ' + state;
|
||||
const icon = el.querySelector('.step-icon');
|
||||
if (state === 'active') icon.textContent = '\u25F7'; // spinning
|
||||
else if (state === 'done') icon.textContent = '\u2713';
|
||||
else if (state === 'error') icon.textContent = '\u2717';
|
||||
if (extra) {
|
||||
const pct = document.getElementById(id + '-pct');
|
||||
if (pct) pct.textContent = extra;
|
||||
}
|
||||
}
|
||||
|
||||
// Kuuntele console.log-viestejä pipeline-vaiheiden seuraamiseksi
|
||||
const origCodeLog = console.log;
|
||||
const codeLogListener = (...args) => {
|
||||
const msg = args.join(' ');
|
||||
if (msg.includes('[Coder]') || msg.includes('Burn Wasm') || msg.includes('Kipinä Agent Node')) {
|
||||
if (msg.includes('Burn Wasm')) setStep('step-wasm', 'active');
|
||||
if (msg.includes('Agent Node käynnistyy')) { setStep('step-wasm', 'done'); }
|
||||
if (msg.includes('[Coder]') && msg.includes('tokenizer') && msg.includes('löytyi')) { setStep('step-tokenizer', 'done'); }
|
||||
if (msg.includes('[Coder]') && msg.includes('Ladataan') && msg.includes('tokenizer')) { setStep('step-tokenizer', 'active'); }
|
||||
if (msg.includes('[Coder]') && msg.includes('tokenizer') && msg.includes('tallennettu')) { setStep('step-tokenizer', 'done'); }
|
||||
if (msg.includes('[Coder]') && msg.includes('model') && msg.includes('lataus:')) {
|
||||
setStep('step-model', 'active');
|
||||
const match = msg.match(/lataus: (\d+)%/);
|
||||
if (match) setStep('step-model', 'active', match[1] + '%');
|
||||
}
|
||||
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('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('Syöte:')) {
|
||||
// Pipeline piiloon kun generointi alkaa
|
||||
setTimeout(() => { document.getElementById('code-pipeline').style.display = 'none'; }, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Lisätään kuuntelija alkuperäisen console.log ylikirjoituksen päälle
|
||||
const _prevConsoleLog = console.log;
|
||||
console.log = function(...args) { _prevConsoleLog.apply(console, args); codeLogListener(...args); };
|
||||
|
||||
// Käynnistä Coder-node automaattisesti ensimmäisellä kerralla
|
||||
async function ensureCoderNode() {
|
||||
if (coderJoined) return;
|
||||
coderJoined = true;
|
||||
document.getElementById('coder-status').textContent = 'Käynnistyy...';
|
||||
document.getElementById('coder-status').style.color = '#d29922';
|
||||
document.getElementById('code-pipeline').style.display = 'block';
|
||||
setStep('step-wasm', 'active');
|
||||
|
||||
try {
|
||||
await init();
|
||||
setStep('step-wasm', 'done');
|
||||
setStep('step-tokenizer', 'active');
|
||||
|
||||
const wsUrl = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`;
|
||||
const deviceInfo = {
|
||||
allocated_gb: 4,
|
||||
cpu_cores: navigator.hardwareConcurrency || 0,
|
||||
device_memory_gb: navigator.deviceMemory || 0,
|
||||
platform: navigator.platform || "",
|
||||
gpu: null,
|
||||
selected_task: coderSize === '3b' ? 'qwen-coder-3b' : 'qwen-coder-05b'
|
||||
};
|
||||
const taskId = coderSize === '3b' ? 5 : 4;
|
||||
await start_agent_node(wsUrl, false, JSON.stringify(deviceInfo), taskId);
|
||||
document.getElementById('coder-status').textContent = 'Connected';
|
||||
document.getElementById('coder-status').style.color = '#d29922';
|
||||
coderWsReady = true;
|
||||
|
||||
if (pendingCodePrompt) {
|
||||
sendCodeToHub(pendingCodePrompt);
|
||||
pendingCodePrompt = null;
|
||||
}
|
||||
} catch(e) {
|
||||
console.log("Coder-virhe: " + e);
|
||||
document.getElementById('coder-status').textContent = 'Virhe';
|
||||
document.getElementById('coder-status').style.color = '#f85149';
|
||||
coderJoined = false;
|
||||
}
|
||||
}
|
||||
|
||||
function sendCodeToHub(text) {
|
||||
if (uiSocket && uiSocket.readyState === 1) {
|
||||
uiSocket.send(JSON.stringify({ type: 'user_text', text: text, task_type: 'qwen-coder' }));
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCodeSubmit() {
|
||||
const text = codeInput.value.trim();
|
||||
if (!text) return;
|
||||
codeInput.value = '';
|
||||
codeSendBtn.disabled = true;
|
||||
codeSendBtn.textContent = 'Generating...';
|
||||
codeLoading.style.display = 'block';
|
||||
|
||||
if (!coderJoined) {
|
||||
pendingCodePrompt = text;
|
||||
const dlSize = coderSize === '3b' ? '~6.2 GB' : '~990 MB';
|
||||
codeLoading.textContent = `Loading Qwen2.5-Coder-${coderSize === '3b' ? '3B' : '0.5B'} (${dlSize} on first run)...`;
|
||||
await ensureCoderNode();
|
||||
} else {
|
||||
codeLoading.textContent = 'Generating code...';
|
||||
sendCodeToHub(text);
|
||||
}
|
||||
}
|
||||
|
||||
codeSendBtn?.addEventListener('click', handleCodeSubmit);
|
||||
codeInput?.addEventListener('keydown', (e) => { if (e.key === 'Enter') handleCodeSubmit(); });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user