diff --git a/network-poc/static/index.html b/network-poc/static/index.html index 3f75b4e..0551000 100644 --- a/network-poc/static/index.html +++ b/network-poc/static/index.html @@ -1255,8 +1255,9 @@ const lastPromptText = document.getElementById('agent-last-prompt-text'); if (selectedAgents.size === 1) { const agent = [...selectedAgents][0]; - const lastStep = [...pipelineSteps].reverse().find(s => s.agent === agent && s.status === 'done' && s.input); - lastPromptDiv.style.display = lastStep ? 'block' : 'none'; + // Näytetään aina jos agentilla on oletusprompt tai pipeline-historia + const hasPrompt = defaultPipelinePrompts[agent] || pipelineSteps.some(s => s.agent === agent); + lastPromptDiv.style.display = hasPrompt ? 'block' : 'none'; } else { lastPromptDiv.style.display = 'none'; } @@ -1376,6 +1377,21 @@ window.openPromptModal = openPromptModal; function closePromptModal() { + // Tallennetaan muokattu prompti localStorageen + if (modalAgent) { + const fields = document.getElementById('prompt-modal-fields'); + const textareas = fields.querySelectorAll('textarea'); + const parts = []; + textareas.forEach((ta, i) => { + const key = modalPromptParts[i]?.key || ''; + parts.push(`${key}: ${ta.value.trim()}`); + }); + const assembled = parts.join('\n\n'); + if (defaultPipelinePrompts[modalAgent]) { + defaultPipelinePrompts[modalAgent].prompt = assembled; + localStorage.setItem('kpn-pipeline-prompt-' + modalAgent, assembled); + } + } document.getElementById('prompt-modal').style.display = 'none'; } window.closePromptModal = closePromptModal; @@ -1399,11 +1415,93 @@ window.rerunFromModal = rerunFromModal; // "Näytä prompti" -nappi avaa modalin + // Oletuspromptit jokaiselle agentille — näkyvät aina, muokattavissa + const defaultPipelinePrompts = { + manager: { label: 'Suunnittelu', prompt: `List the source files needed for this project. One file per line, format: +filename.py: one-line description + +CONSTRAINTS: the coder can only generate ~400 tokens per file +- Max 3 files (keep it minimal) +- Each file must be SHORT: one clear responsibility, no boilerplate +- Only .py and pyproject.toml files + +EXAMPLE: for "FastAPI todo app with SQLite" +pyproject.toml: project metadata and dependencies +models.py: SQLAlchemy models and database setup +main.py: FastAPI app with CRUD endpoints + +Project: (käyttäjän kuvaus)` }, + coder: { label: 'Koodaus', prompt: `Project: (managerin suunnitelma) +Write ONLY the file "filename.py": description + +EXAMPLE: output for a main.py +from fastapi import FastAPI, Depends +from sqlalchemy.orm import Session +from models import get_db, User + +app = FastAPI() + +@app.get("/users") +def list_users(db: Session = Depends(get_db)): + return db.query(User).all() + +IMPORTANT: Keep the code SHORT. Max ~50 lines. No comments, no docstrings. Output ONLY code.` }, + qa: { label: 'Testit + Validointi', prompt: `Write test_app.py using pytest and FastAPI TestClient. Max 3 tests. Output ONLY code. + +EXAMPLE: +from fastapi.testclient import TestClient +from main import app +client = TestClient(app) + +def test_create(): + r = client.post("/users", params={"name": "Test"}) + assert r.status_code == 200 + +IMPORTANT: Check consistency +1. Dockerfile COPY: references files that exist +2. Dockerfile deps vs imports: all imports covered +3. docker-compose ports: match EXPOSE +4. Test imports: match actual module names +5. pyproject.toml deps: cover all imports` }, + tester: { label: 'DevOps', prompt: `docker-compose.yml: services, volumes, port mappings + +EXAMPLE: +services: + app: + build: . + ports: + - "8000:8000" + restart: unless-stopped + +README.md: Quick start, development, API endpoints, testing +IMPORTANT: Use uv for package management (uv sync, uv run)` }, + data: { label: 'Data', prompt: `SQLAlchemy models and database setup. + +EXAMPLE: +from sqlalchemy import create_engine, Column, Integer, String +from sqlalchemy.orm import sessionmaker, declarative_base + +engine = create_engine("sqlite:///app.db") +Base = declarative_base() + +IMPORTANT: Include get_db() dependency for FastAPI` }, + }; + + // Ladataan muokatut promptit localStoragesta + for (const [agent, def] of Object.entries(defaultPipelinePrompts)) { + const saved = localStorage.getItem('kpn-pipeline-prompt-' + agent); + if (saved) def.prompt = saved; + } + document.getElementById('agent-open-modal-btn')?.addEventListener('click', () => { if (selectedAgents.size !== 1) return; const agent = [...selectedAgents][0]; + // Viimeisin pipeline-prompti tai oletusprompt const lastStep = [...pipelineSteps].reverse().find(s => s.agent === agent && s.status === 'done' && s.input); - if (lastStep) openPromptModal(agent, lastStep.label, lastStep.input); + const def = defaultPipelinePrompts[agent]; + const prompt = lastStep?.input || def?.prompt || agentPrompts[agent]?.prompt || ''; + const label = lastStep?.label || def?.label || 'Prompti'; + openPromptModal(agent, label, prompt); }); function checkAgentConfusion() { @@ -2034,11 +2132,18 @@ const iconColor = s.status === 'done' ? '#3fb950' : s.status === 'active' ? '#d29922' : '#8b949e'; const arrow = i < pipelineSteps.length - 1 ? ' ' : ''; // 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 `${icon} ${esc(s.label)}${arrow}`; + return `${icon} ${esc(s.label)}${arrow}`; }).join(''); } + window.openPipelineStepModal = function(idx) { + const s = pipelineSteps[idx]; + if (!s) return; + // Näytetään modal promptilla (input) ja tuloksella (output) + const combined = (s.input ? s.input : '') + (s.output ? '\n\n--- Tulos ---\n' + s.output : ''); + openPromptModal(s.agent, s.label, combined); + }; + function pipelineClear() { pipelineSteps.length = 0; const container = document.getElementById('pipeline-steps');