Pipeline-vaiheiden visuaalinen seuranta agenttinäkymässä
Terminaalin yläpuolelle ilmestyy pipeline-progress-palkki: ✓ Suunnittelu → ✓ models.py → ◷ main.py → ◯ Review Jokainen vaihe on hover-tooltip joka näyttää: - Vaiheen nimi ja agentti (värikoodattu) - Input: mitä agentti sai syötteeksi - Output: mitä agentti tuotti (esikatselu 150 merkkiä) Myös agenttien avatar-korttien tooltip päivittyy reaaliaikaisesti näyttämään viimeisimmän vaiheen input/output. Palkki tyhjenee automaattisesti uuden pipelinen alkaessa. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1103,6 +1103,7 @@
|
||||
<button id="agent-compute-btn" style="margin-left:4px;padding:2px 10px;border-radius:4px;border:1px solid #30363d;background:#161b22;color:#58a6ff;font-size:12px;font-family:inherit;cursor:pointer" title="Käynnistä kielimalli omalla koneellasi laskentaa varten">Alusta laskentasolmu</button>
|
||||
</span>
|
||||
</div>
|
||||
<div id="pipeline-steps" style="display:none;background:#0d1117;border:1px solid var(--border-color);border-top:none;padding:8px 14px;font-family:'Courier New',monospace;font-size:12px;overflow-x:auto;white-space:nowrap"></div>
|
||||
<div class="terminal-panel" id="agent-terminal" style="margin-top:0;border-top:none;border-radius:0">
|
||||
</div>
|
||||
<div style="position:relative;display:flex;align-items:center;background:#010409;border:1px solid var(--border-color);border-top:none;border-radius:0 0 6px 6px;padding:8px 12px;font-family:'Courier New',monospace;font-size:14px">
|
||||
@@ -1819,12 +1820,64 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Pipeline-vaiheiden seuranta ja visualisointi
|
||||
const pipelineSteps = [];
|
||||
function pipelineStep(agent, label, status, input, output) {
|
||||
const step = { agent, label, status, input: input || '', output: output || '' };
|
||||
// Päivitetään olemassaoleva tai lisätään uusi
|
||||
const existing = pipelineSteps.find(s => s.label === label && s.status !== 'done');
|
||||
if (existing && status !== 'done') {
|
||||
Object.assign(existing, step);
|
||||
} else if (status === 'done' && existing) {
|
||||
existing.status = 'done';
|
||||
existing.output = output || existing.output;
|
||||
} else {
|
||||
pipelineSteps.push(step);
|
||||
}
|
||||
renderPipelineSteps();
|
||||
// Päivitetään agentin avatar tooltip
|
||||
const avatarMap = { manager: 'avatar-kpn', coder: 'avatar-coder', tester: 'avatar-tester', qa: 'avatar-qa', data: 'avatar-data' };
|
||||
const avatarId = avatarMap[agent];
|
||||
if (avatarId) {
|
||||
const el = document.getElementById(avatarId);
|
||||
if (el) {
|
||||
const truncOut = (output || '').substring(0, 200).replace(/\n/g, ' ');
|
||||
el.title = `${label}\n${status === 'active' ? '⏳ Käsittelee...' : '✓ Valmis'}\n\nInput: ${(input || '').substring(0, 100)}...\nOutput: ${truncOut}...`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderPipelineSteps() {
|
||||
const container = document.getElementById('pipeline-steps');
|
||||
if (!container) return;
|
||||
if (pipelineSteps.length === 0) { container.style.display = 'none'; return; }
|
||||
container.style.display = 'block';
|
||||
container.innerHTML = pipelineSteps.map((s, i) => {
|
||||
const colors = { manager: '#d29922', coder: '#3fb950', tester: '#58a6ff', qa: '#a371f7', data: '#d2a8ff' };
|
||||
const color = colors[s.agent] || '#8b949e';
|
||||
const icon = s.status === 'done' ? '✓' : s.status === 'active' ? '◷' : '◯';
|
||||
const iconColor = s.status === 'done' ? '#3fb950' : s.status === 'active' ? '#d29922' : '#8b949e';
|
||||
const arrow = i < pipelineSteps.length - 1 ? ' <span style="color:#30363d">→</span> ' : '';
|
||||
// Tooltip: input/output esikatselu
|
||||
const tip = esc(`${s.label}\nInput: ${(s.input || '').substring(0, 150)}\nOutput: ${(s.output || '').substring(0, 150)}`).replace(/\n/g, ' ');
|
||||
return `<span title="${tip}" style="cursor:help"><span style="color:${iconColor}">${icon}</span> <span style="color:${color}">${esc(s.label)}</span></span>${arrow}`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function pipelineClear() {
|
||||
pipelineSteps.length = 0;
|
||||
const container = document.getElementById('pipeline-steps');
|
||||
if (container) container.style.display = 'none';
|
||||
}
|
||||
|
||||
// Pipeline: manageri → koodari (per tiedosto) → testaaja → korjausluuppi
|
||||
async function kpnPipeline(task) {
|
||||
pipelineClear();
|
||||
termLog(`<span style="color:#a371f7;font-weight:bold">━━━ Pipeline käynnistyy ━━━</span>`);
|
||||
|
||||
// Vaihe 1: Manageri pilkkoo projektin tiedostoiksi
|
||||
termLog(`\n<span style="color:#d29922;font-weight:bold">[1] Manageri</span> — projektin suunnittelu`);
|
||||
pipelineStep('manager', 'Suunnittelu', 'active', task);
|
||||
const managerPrompt = `You are a project manager. Break this task into individual source files.
|
||||
For each file, write one line: FILENAME: description
|
||||
List only the essential files (max 5). No explanations.
|
||||
@@ -1832,6 +1885,7 @@ List only the essential files (max 5). No explanations.
|
||||
Task: ${task}`;
|
||||
const plan = await kpnRun(agentPrompts.manager.model, managerPrompt);
|
||||
if (!plan) { termLog(' ✗ Pipeline keskeytyi (manageri)', '#f85149'); return; }
|
||||
pipelineStep('manager', 'Suunnittelu', 'done', task, plan);
|
||||
|
||||
// Parsitaan tiedostolista: "FILENAME: description" -rivit
|
||||
const fileList = plan.split('\n')
|
||||
@@ -1861,6 +1915,7 @@ Task: ${task}`;
|
||||
for (let i = 0; i < fileList.length; i++) {
|
||||
const file = fileList[i];
|
||||
termLog(`\n<span style="color:#3fb950;font-weight:bold">[${i + 2}] Koodari</span> — ${esc(file.name)}`);
|
||||
pipelineStep('coder', file.name, 'active', file.desc);
|
||||
|
||||
// Rakennetaan konteksti: aiemmin generoidut tiedostot
|
||||
let context = '';
|
||||
@@ -1879,6 +1934,7 @@ Project: ${task}`;
|
||||
return;
|
||||
}
|
||||
generatedFiles[file.name] = code;
|
||||
pipelineStep('coder', file.name, 'done', file.desc, code);
|
||||
}
|
||||
|
||||
// Vaihe 3: Testaaja arvioi koko projektin
|
||||
@@ -1887,15 +1943,18 @@ Project: ${task}`;
|
||||
.join('\n\n');
|
||||
|
||||
termLog(`\n<span style="color:#58a6ff;font-weight:bold">[${fileList.length + 2}] Testaaja</span> — arviointi`);
|
||||
pipelineStep('tester', 'Review', 'active', `${Object.keys(generatedFiles).length} tiedostoa`);
|
||||
const reviewPrompt = `Review this project. List bugs or issues. Be brief.
|
||||
If the code is correct, say "LGTM".
|
||||
|
||||
${allCode}`;
|
||||
const review = await kpnRun(agentPrompts.tester.model, reviewPrompt);
|
||||
pipelineStep('tester', 'Review', 'done', `${Object.keys(generatedFiles).length} tiedostoa`, review);
|
||||
|
||||
// Vaihe 4: Korjausluuppi — jos testaaja löysi ongelmia
|
||||
if (review && !review.toLowerCase().includes('lgtm') && !review.toLowerCase().includes('looks good')) {
|
||||
termLog(`\n<span style="color:#d29922;font-weight:bold">[${fileList.length + 3}] Koodari</span> — korjaukset`);
|
||||
pipelineStep('coder', 'Korjaukset', 'active', review);
|
||||
const fixPrompt = `Fix the issues found in the review.
|
||||
Review feedback: ${review}
|
||||
|
||||
@@ -1904,9 +1963,12 @@ ${allCode}
|
||||
|
||||
Write the corrected code.`;
|
||||
const fixedCode = await kpnRun(agentPrompts.coder.model, fixPrompt);
|
||||
pipelineStep('coder', 'Korjaukset', 'done', review, fixedCode);
|
||||
if (fixedCode) {
|
||||
termLog(`\n<span style="color:#58a6ff;font-weight:bold">[${fileList.length + 4}] Testaaja</span> — uudelleenarviointi`);
|
||||
await kpnRun(agentPrompts.tester.model, `Review the corrected code briefly:\n${fixedCode}`);
|
||||
pipelineStep('tester', 'Re-review', 'active', fixedCode);
|
||||
const reReview = await kpnRun(agentPrompts.tester.model, `Review the corrected code briefly:\n${fixedCode}`);
|
||||
pipelineStep('tester', 'Re-review', 'done', fixedCode, reReview);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user