diff --git a/network-poc/static/index.html b/network-poc/static/index.html index 9e50e81..be2d1fb 100644 --- a/network-poc/static/index.html +++ b/network-poc/static/index.html @@ -1157,7 +1157,7 @@ coder: { name: 'Koodari — System Prompt', model: 'qwen-coder', default: 'Olet kokenut ohjelmistokehittäjä. Kirjoita selkeää, testattavaa koodia ja vastaa aina koodilla.' }, data: { name: 'Data-Agentti — System Prompt', model: 'qwen-coder', default: 'Olet tietokanta-asiantuntija. Vastaat skeemojen suunnittelusta, SQL-kyselyiden optimoinnista ja datamalleista.' }, qa: { name: 'QA — System Prompt', model: 'qwen-coder', default: 'Olet laadunvarmistaja (QA). Kirjoitat testejä, etsit virheitä ja varmistat, että kaikki reunatapaukset on huomioitu.' }, - tester: { name: 'DevOps — System Prompt', model: 'qwen-coder', default: 'Olet DevOps-insinööri. Vastaat koodin julkaisuputkista, serveri-infrastruktuurista ja ympäristön suorituskyvystä.' }, + tester: { name: 'DevOps — System Prompt', model: 'qwen-coder', default: 'Olet DevOps-insinööri. Kirjoitat Dockerfile- ja docker-compose.yml-tiedostot, README:t ja käynnistysohjeet. Käytä aina multi-stage Docker buildia ja docker compose -orkestrointia.' }, }; const selectedAgents = new Set(); let sharedPrompt = localStorage.getItem('kpn-shared-prompt') || ''; @@ -2208,21 +2208,53 @@ ${Object.entries(generatedFiles).map(([n, c]) => `--- ${n} ---\n${c}`).join('\n\ if (tests) generatedFiles['test_app.py'] = tests; pipelineStep('qa', 'Testit', 'done', 'test_app.py', tests); - // Vaihe 6: DevOps — käynnistysohjeet + // Vaihe 6: DevOps — Dockerfile const step6 = step5 + 1; - termLog(`\n[${step6}] DevOps — käynnistys`); - pipelineStep('tester', 'DevOps', 'active', 'Käynnistysohjeet'); - const devopsPrompt = `Write a minimal README.md for this project. Include ONLY: -1. One-line description -2. How to install (pip/uv) -3. How to run -4. How to test -Max 15 lines. No badges, no license, no contributing section. + termLog(`\n[${step6}] DevOps — Dockerfile`); + pipelineStep('tester', 'Dockerfile', 'active', 'Dockerfile'); + const dockerPrompt = `Write a Dockerfile for this project. Use multi-stage build: +- Stage 1 (builder): install dependencies +- Stage 2 (runtime): copy only what's needed, minimal image +Use python:3.12-slim as base. EXPOSE the correct port. CMD to start the app. +Only output the Dockerfile content, nothing else. + +Files: ${Object.keys(generatedFiles).join(', ')} +Main app entry: ${Object.keys(generatedFiles).find(f => f.includes('main') || f.includes('app')) || Object.keys(generatedFiles)[0]}`; + const dockerfile = await kpnRun(agentPrompts.tester.model, dockerPrompt, false, 256); + if (dockerfile) generatedFiles['Dockerfile'] = dockerfile; + pipelineStep('tester', 'Dockerfile', 'done', 'Dockerfile', dockerfile); + + // Vaihe 7: DevOps — docker-compose.yml + const step7 = step6 + 1; + termLog(`\n[${step7}] DevOps — docker-compose.yml`); + pipelineStep('tester', 'Compose', 'active', 'docker-compose.yml'); + const composePrompt = `Write a docker-compose.yml for this project. Include: +- app service (build from Dockerfile, port mapping, restart: unless-stopped) +- db service if SQLite/PostgreSQL is used (volume for data persistence) +- Named volumes for persistent data +Only output the YAML content, nothing else. Files: ${Object.keys(generatedFiles).join(', ')}`; - const readme = await kpnRun(agentPrompts.tester.model, devopsPrompt, false, 256); + const compose = await kpnRun(agentPrompts.tester.model, composePrompt, false, 256); + if (compose) generatedFiles['docker-compose.yml'] = compose; + pipelineStep('tester', 'Compose', 'done', 'docker-compose.yml', compose); + + // Vaihe 8: DevOps — README + const step8 = step7 + 1; + termLog(`\n[${step8}] DevOps — README`); + pipelineStep('tester', 'README', 'active', 'README.md'); + const readmePrompt = `Write a minimal README.md. Include ONLY: +1. One-line description +2. Quick start: docker compose up +3. Development: how to run without Docker +4. API endpoints (if applicable) +5. How to test +Max 20 lines. + +Files: ${Object.keys(generatedFiles).join(', ')}`; + const readme = await kpnRun(agentPrompts.tester.model, readmePrompt, false, 256); if (readme) generatedFiles['README.md'] = readme; - pipelineStep('tester', 'DevOps', 'done', 'README.md', readme); + pipelineStep('tester', 'README', 'done', 'README.md', readme); termLog(`\n━━━ Pipeline valmis (${Object.keys(generatedFiles).length} tiedostoa) ━━━`); renderProjectCard(generatedFiles, task);