agentic office
This commit is contained in:
@@ -368,6 +368,53 @@
|
||||
color: #8b949e;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.terminal-panel {
|
||||
background:#010409;
|
||||
border:1px solid var(--border-color);
|
||||
border-radius:6px;
|
||||
padding:15px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size:14px;
|
||||
color:var(--success-color);
|
||||
height:500px;
|
||||
overflow-y:auto;
|
||||
text-align:left;
|
||||
}
|
||||
.terminal-line { margin: 4px 0; }
|
||||
.terminal-prompt { color: #d29922; }
|
||||
.avatar-grid {
|
||||
display:flex;
|
||||
gap:15px;
|
||||
justify-content:center;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
.avatar-card {
|
||||
background:var(--panel-bg);
|
||||
border:1px solid var(--border-color);
|
||||
border-radius:8px;
|
||||
padding:10px;
|
||||
text-align:center;
|
||||
width:120px;
|
||||
opacity: 0.6;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.avatar-card img {
|
||||
width:80px;
|
||||
height:80px;
|
||||
border-radius:50%;
|
||||
margin-bottom:10px;
|
||||
border:2px solid var(--border-color);
|
||||
}
|
||||
.avatar-card.active {
|
||||
opacity: 1;
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
.avatar-card.active img {
|
||||
border-color:var(--accent-color);
|
||||
box-shadow: 0 0 15px var(--accent-color);
|
||||
}
|
||||
.avatar-name { font-weight: bold; font-size: 13px; color: var(--text-color); }
|
||||
.avatar-role { font-size: 11px; color: #8b949e; margin-top: 2px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -379,6 +426,7 @@
|
||||
<div class="main-tabs">
|
||||
<div class="main-tab active" onclick="switchMainTab('network')">Laskentaverkko</div>
|
||||
<div class="main-tab" onclick="switchMainTab('codelab')">Koodilaboratorio</div>
|
||||
<div class="main-tab" onclick="switchMainTab('agents')">Agents & CLI</div>
|
||||
</div>
|
||||
|
||||
<!-- PANEELI 1: Laskentaverkko -->
|
||||
@@ -663,6 +711,45 @@
|
||||
</div>
|
||||
</div><!-- /panel-codelab -->
|
||||
|
||||
<!-- PANEELI 3: Agents & CLI -->
|
||||
<div id="panel-agents" class="main-panel" style="position: relative; border-radius: 6px;">
|
||||
<div style="position: absolute; top:0; left:0; width:100%; height:100%; background: url('/avatars/forge_hero.png') no-repeat center center; background-size: cover; opacity: 0.15; z-index: 0; pointer-events: none; border-radius: 6px;"></div>
|
||||
<div style="background:rgba(13, 17, 23, 0.7); backdrop-filter: blur(4px); border:1px solid var(--border-color); border-radius:6px; padding:16px; margin-bottom:16px; position: relative; z-index: 1;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px">
|
||||
<span style="font-weight:600;font-size:15px;color:var(--accent-color)">Kipinä Agent Workspace</span>
|
||||
<span id="agent-status" style="font-size:12px;color:var(--success-color)">Monitoring Active</span>
|
||||
</div>
|
||||
|
||||
<div class="avatar-grid">
|
||||
<div class="avatar-card active" id="avatar-kpn">
|
||||
<img src="/avatars/forge_hero.png" alt="Forge">
|
||||
<div class="avatar-name">KPN CLI</div>
|
||||
<div class="avatar-role">Paikallinen Ohjaus</div>
|
||||
</div>
|
||||
<div class="avatar-card" id="avatar-coder">
|
||||
<img src="/avatars/gecko_hero.png" alt="Gecko">
|
||||
<div class="avatar-name">Qwen-Coder</div>
|
||||
<div class="avatar-role">Koodiagentti</div>
|
||||
</div>
|
||||
<div class="avatar-card" id="avatar-smol">
|
||||
<img src="/avatars/serpent_hero.png" alt="Serpent">
|
||||
<div class="avatar-name">SmolLM</div>
|
||||
<div class="avatar-role">Logiikka</div>
|
||||
</div>
|
||||
<div class="avatar-card" id="avatar-discord">
|
||||
<img src="/avatars/discord_1.png" alt="Discord">
|
||||
<div class="avatar-name">Swarm</div>
|
||||
<div class="avatar-role">WebGPU Solmu</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="terminal-panel" id="agent-terminal">
|
||||
<div class="terminal-line"><span class="terminal-prompt">$</span> kpn hub connect wss://localhost</div>
|
||||
<div class="terminal-line" style="color:#a5d6ff"> ✓ Yhdistetty Kipinä Hubiin</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /panel-agents -->
|
||||
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
@@ -794,21 +881,33 @@
|
||||
|
||||
// Ylikirjoitetaan console.log uppoamaan lokilaatikkoon
|
||||
const originalLog = console.log;
|
||||
let logQueue = [];
|
||||
let logFlushPending = false;
|
||||
function flushLogs() {
|
||||
if (!logQueue.length) return;
|
||||
const frag = document.createDocumentFragment();
|
||||
for (const msg of logQueue) {
|
||||
const p = document.createElement('p');
|
||||
p.textContent = '> ' + msg;
|
||||
frag.appendChild(p);
|
||||
}
|
||||
logBox.appendChild(frag);
|
||||
while (logBox.children.length > 20) logBox.removeChild(logBox.firstChild);
|
||||
logBox.scrollTop = logBox.scrollHeight;
|
||||
logQueue = [];
|
||||
logFlushPending = false;
|
||||
}
|
||||
|
||||
console.log = function(...args) {
|
||||
originalLog.apply(console, args);
|
||||
// Älä tulosta teknisiä WGPU warningeja suoraan AI:n näytölle jos niitä on
|
||||
let msg = args.join(' ');
|
||||
if (msg.includes("wgpu") || msg.includes("vastaanotettu")) return; // Siistitään spämmäävät lokit näkymästä, koska niitä tulee nyt sata sekunnissa
|
||||
|
||||
const p = document.createElement('p');
|
||||
p.textContent = '> ' + msg;
|
||||
logBox.appendChild(p);
|
||||
|
||||
// Ehkäistään selaimen jumittuminen sadoista tuhansista lokiriveistä pitkässä GPU-ajossa
|
||||
if (logBox.children.length > 30) {
|
||||
logBox.removeChild(logBox.firstChild);
|
||||
if (msg.includes("wgpu") || msg.includes("vastaanotettu") || msg.includes("Tehtävä vastaanotettu")) return;
|
||||
|
||||
logQueue.push(msg);
|
||||
if (!logFlushPending) {
|
||||
logFlushPending = true;
|
||||
requestAnimationFrame(flushLogs);
|
||||
}
|
||||
logBox.scrollTop = logBox.scrollHeight;
|
||||
};
|
||||
|
||||
// UI Slider Listener -> Lähettää arvon suoraan WebAssemblyn ytimeen!
|
||||
@@ -923,7 +1022,10 @@
|
||||
};
|
||||
uiSocket.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
const raw = event.data;
|
||||
if (raw.includes('"single_tokenize"') || raw.includes('"download_progress"')) return;
|
||||
|
||||
const data = JSON.parse(raw);
|
||||
if (data.type === "stats") {
|
||||
statNodes.textContent = data.nodes;
|
||||
statVram.textContent = data.vram_gb + " GB";
|
||||
@@ -935,13 +1037,6 @@
|
||||
}
|
||||
} else if (data.type === "node_joined") {
|
||||
chatBox.classList.remove('hidden');
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.className = 'chat-msg';
|
||||
msgDiv.style.borderLeftColor = 'var(--success-color)';
|
||||
msgDiv.innerHTML = `<span style="color:var(--success-color)">[Järjestelmä] Uusi solmu (ID: ${data.node_id}) liittyi verkon työjohdon piiriin!</span>`;
|
||||
chatBox.appendChild(msgDiv);
|
||||
if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
} else if (data.type === "download_progress") {
|
||||
const dlBar = document.getElementById('download-bar');
|
||||
if (data.pct < 100) {
|
||||
@@ -987,10 +1082,10 @@
|
||||
<strong style="color:#58a6ff;font-size:12px">(${r.token_count || 0})</strong> ${tokHtml}
|
||||
</div>`;
|
||||
chatBox.appendChild(msgDiv);
|
||||
if (chatBox.children.length > 8) chatBox.removeChild(chatBox.firstChild);
|
||||
if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
flashComputing();
|
||||
} else if (data.type === "pair_task") {
|
||||
} else if (data.type === "pair_task" && selectedTask === 'tokenize') {
|
||||
chatBox.classList.remove('hidden');
|
||||
if (chatBox.children.length === 1 && chatBox.children[0].textContent.includes('Odotetaan')) {
|
||||
chatBox.innerHTML = '';
|
||||
@@ -1003,7 +1098,7 @@
|
||||
<div><strong style="color:#d29922">FI</strong> "${data.fi}"</div>
|
||||
</div>`;
|
||||
chatBox.appendChild(msgDiv);
|
||||
if (chatBox.children.length > 8) chatBox.removeChild(chatBox.firstChild);
|
||||
if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
} else if (data.type === "pair_done") {
|
||||
chatBox.classList.remove('hidden');
|
||||
@@ -1076,9 +1171,27 @@
|
||||
</div>`;
|
||||
|
||||
if (!msgDiv.parentNode) chatBox.appendChild(msgDiv);
|
||||
if (chatBox.children.length > 8) chatBox.removeChild(chatBox.firstChild);
|
||||
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';
|
||||
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} <span style="color:#8b949e">${tokGen} tok | ${durMs}ms | ${tokS} tok/s</span>`;
|
||||
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');
|
||||
}
|
||||
|
||||
// Poistetaan streaming-kortti
|
||||
chatBox.querySelector('.streaming-card')?.remove();
|
||||
chatBox.classList.remove('hidden');
|
||||
@@ -1107,7 +1220,7 @@
|
||||
${tokGen} tokenia generoitu | malli ladattu: ${typeof loadMs === 'number' ? loadMs.toFixed(0) : loadMs}ms
|
||||
</div>`;
|
||||
chatBox.appendChild(msgDiv);
|
||||
if (chatBox.children.length > 8) chatBox.removeChild(chatBox.firstChild);
|
||||
if (chatBox.children.length > 5) chatBox.removeChild(chatBox.firstChild);
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
|
||||
metrics.tasks++;
|
||||
@@ -1152,6 +1265,25 @@
|
||||
if (counterEl) counterEl.textContent = tokCount + ' tok';
|
||||
targetBox.scrollTop = targetBox.scrollHeight;
|
||||
}
|
||||
} else if (data.type === "llm_prompt") {
|
||||
if (data.task_id) {
|
||||
const term = document.getElementById('agent-terminal');
|
||||
if (term) {
|
||||
const model = data.model || 'llm';
|
||||
const promptShort = (data.prompt || '').substring(0, 50).replace(/</g,'<');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'terminal-line';
|
||||
div.innerHTML = `<span class="terminal-prompt">$</span> kpn run ${model} <span style="color:#8b949e">"${promptShort}"</span>`;
|
||||
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'));
|
||||
const model = data.model || '';
|
||||
if (model.includes('coder')) document.getElementById('avatar-coder')?.classList.add('active');
|
||||
else document.getElementById('avatar-smol')?.classList.add('active');
|
||||
document.getElementById('avatar-discord')?.classList.add('active');
|
||||
}
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user