diff --git a/network-poc/frontend/src/components/StatusBar.astro b/network-poc/frontend/src/components/StatusBar.astro
index 6755938..cda5791 100644
--- a/network-poc/frontend/src/components/StatusBar.astro
+++ b/network-poc/frontend/src/components/StatusBar.astro
@@ -41,7 +41,7 @@
2. Käynnistä Kipinä-node
curl -sSL "https://kipina.studio/kipina-node?v=$(date +%s)" -o kipina-node && chmod +x kipina-node && ./kipina-node
-
+
Lataa kielimallin (~2GB) automaattisesti ensimmäisellä kerralla. Ctrl+C pysäyttää.
diff --git a/network-poc/frontend/src/pages/index.astro b/network-poc/frontend/src/pages/index.astro
index 49476ea..52226ad 100644
--- a/network-poc/frontend/src/pages/index.astro
+++ b/network-poc/frontend/src/pages/index.astro
@@ -71,7 +71,25 @@ import Settings from "../components/Settings.astro";
// === Globaalit tilat ===
const defaultAgents = {
- manager: { name: 'Manageri', avatar: '/avatars/karhunpentu.webp', model: 'qwen-coder', order: 0,
+ client: { name: 'Asiakas', avatar: '/avatars/kettu_notext.webp', model: 'qwen-coder', order: 0,
+ temperature: 0.6, topK: 40, repeatPenalty: 1.15, maxTokens: 512,
+ prompt: `You are a product owner who turns vague ideas into clear, actionable software requirements.
+
+GIVEN a short project description from the user, produce a structured brief:
+
+1. PROJECT NAME: a short, descriptive name
+2. GOAL: one sentence explaining what the software does and who it's for
+3. CORE FEATURES: numbered list of 3-5 concrete features (not vague wishes)
+4. DATA MODEL: list the main entities and their key fields
+5. API ENDPOINTS: list the essential REST endpoints (method + path + purpose)
+6. CONSTRAINTS: any technical constraints (e.g. "must use SQLite", "no auth needed for MVP")
+
+RULES:
+- Be specific: "User can filter todos by status" not "todo management"
+- Keep scope small — MVP only, no nice-to-haves
+- Use plain English, no code
+- Maximum 200 words total` },
+ manager: { name: 'Manageri', avatar: '/avatars/karhunpentu.webp', model: 'qwen-coder', order: 1,
temperature: 0.5, topK: 40, repeatPenalty: 1.15, maxTokens: 512,
prompt: `You are a senior project manager and software architect. Your job is to plan the file structure of a software project.
@@ -88,7 +106,7 @@ models.py: SQLAlchemy database models and engine setup
schemas.py: Pydantic request/response schemas
main.py: FastAPI application with CRUD endpoints
pyproject.toml: project dependencies` },
- coder: { name: 'Koodari', avatar: '/avatars/kipina_notext.webp', model: 'qwen-coder', order: 1,
+ coder: { name: 'Koodari', avatar: '/avatars/kipina_notext.webp', model: 'qwen-coder', order: 2,
temperature: 0.7, topK: 40, repeatPenalty: 1.15, maxTokens: 1024,
prompt: `You are an expert Python developer. Write complete, production-ready code.
@@ -109,7 +127,7 @@ NEVER:
- Forget to import from other project files
- Use requirements.txt or Poetry — always use pyproject.toml with [project] format (PEP 621)
- Use pip install — use uv (e.g. uv run uvicorn main:app --reload)` },
- data: { name: 'Data', avatar: '/avatars/pesukarhu_notext.webp', model: 'qwen-coder', order: 2,
+ data: { name: 'Data', avatar: '/avatars/pesukarhu_notext.webp', model: 'qwen-coder', order: 3,
temperature: 0.5, topK: 40, repeatPenalty: 1.15, maxTokens: 1024,
prompt: `You are a database architect specializing in SQLAlchemy and relational databases.
@@ -126,7 +144,7 @@ ALWAYS INCLUDE:
- from sqlalchemy.ext.declarative import declarative_base
- from sqlalchemy.orm import sessionmaker
- DATABASE_URL, engine, SessionLocal, Base` },
- qa: { name: 'QA', avatar: '/avatars/susi_notext.webp', model: 'qwen-coder', order: 3,
+ qa: { name: 'QA', avatar: '/avatars/susi_notext.webp', model: 'qwen-coder', order: 4,
temperature: 0.4, topK: 40, repeatPenalty: 1.15, maxTokens: 1024,
prompt: `You are a QA engineer writing automated tests.
@@ -143,7 +161,7 @@ TEST STRUCTURE:
5. test_delete: DELETE → 204, verify GET returns 404 after
ALWAYS: from fastapi.testclient import TestClient` },
- tester: { name: 'DevOps', avatar: '/avatars/laiskiainen_notext.webp', model: 'qwen-coder', order: 4,
+ tester: { name: 'DevOps', avatar: '/avatars/laiskiainen_notext.webp', model: 'qwen-coder', order: 5,
temperature: 0.3, topK: 40, repeatPenalty: 1.1, maxTokens: 512,
prompt: `You are a strict code reviewer and static analysis expert. Analyze the code line by line.
@@ -162,7 +180,7 @@ RESPOND:
- If all checks pass: "LGTM"
- If issues found: list each as "ISSUE: filename.py: description"
- Be specific and actionable, not vague` },
- observer: { name: 'Tarkkailija', avatar: '/avatars/aikuinen_susi.webp', model: 'qwen-coder', order: 5,
+ observer: { name: 'Tarkkailija', avatar: '/avatars/aikuinen_susi.webp', model: 'qwen-coder', order: 6,
temperature: 0.6, topK: 40, repeatPenalty: 1.15, maxTokens: 512,
prompt: `You are an independent technical observer and risk analyst.
@@ -178,7 +196,7 @@ OUTPUT FORMAT:
- End with overall assessment: "SHIP IT" or "NEEDS WORK: reason"` },
};
// Versio: kasvata kun oletuspromptit muuttuvat
- const AGENTS_VERSION = 2;
+ const AGENTS_VERSION = 3;
let agents;
const savedVersion = parseInt(localStorage.getItem('kpn-agents-version') || '0');
if (savedVersion < AGENTS_VERSION && localStorage.getItem('kpn-agents')) {
@@ -750,7 +768,8 @@ OUTPUT FORMAT:
}
async function kpnProject(task) {
- const cdr = agents.coder || Object.values(agents)[1];
+ const cli = agents.client || Object.values(agents)[0];
+ const cdr = agents.coder || Object.values(agents)[2];
// Etsitään sopivin mallipohja
const template = Object.values(templates)[0]; // Toistaiseksi vain FastAPI CRUD
@@ -760,6 +779,15 @@ OUTPUT FORMAT:
}
termLog(`━━━ ${esc(template.name)} — ${esc(task)} ━━━`);
+
+ // Asiakas: jalostaa vaatimukset
+ termLog(`\n[0] ${esc(cli.name)} — vaatimusmäärittely`);
+ highlightAgent('client');
+ explainStep('Vaatimusmäärittely', `${cli.name} muotoilee idean selkeiksi vaatimuksiksi: ominaisuudet, datamallit, rajapinnat.`);
+ const brief = await kpnRun(cli.model, `${task}`, false, cli);
+ if (!brief) { termLog(' ✗ Vaatimusmäärittely epäonnistui', '#f85149'); return; }
+ termLog(` Vaatimukset valmiit → Manageri`);
+
explainStep('Mallipohja', `Käytetään "${template.name}" -mallipohjaa jossa ${template.order.length} tiedostoa: ${template.order.join(', ')}. Jokainen tiedosto generoidaan järjestyksessä, ja aiemmat tiedostot annetaan kontekstina seuraavalle.`);
const files = {};
@@ -772,7 +800,7 @@ OUTPUT FORMAT:
const step = i + 1;
// Valitaan oikea agentti tiedostotyypin mukaan
const isDbFile = fileName === 'models.py' || fileName === 'database.py';
- const dataAgent = agents.data || Object.values(agents)[2];
+ const dataAgent = agents.data || Object.values(agents)[3];
const fileAgent = isDbFile && dataAgent ? dataAgent : cdr;
const fileAgentKey = isDbFile && dataAgent ? 'data' : 'coder';
@@ -801,6 +829,9 @@ OUTPUT FORMAT:
}
}
+ // Asiakkaan vaatimusmäärittely
+ prompt += `PROJECT REQUIREMENTS (from product owner):\n${brief}\n\n`;
+
// Tehtävä
prompt += `NOW write "${fileName}" for THIS project: ${task}\n`;
prompt += fileDef.instructions + '\n';
@@ -818,7 +849,7 @@ OUTPUT FORMAT:
let stepN = template.order.length + 1;
// Review-korjausluuppi: max 2 kierrosta
- const tst = agents.tester || Object.values(agents)[4];
+ const tst = agents.tester || Object.values(agents)[5];
const MAX_REVIEW_ROUNDS = 3;
for (let round = 0; round < MAX_REVIEW_ROUNDS; round++) {
@@ -866,7 +897,7 @@ OUTPUT FORMAT:
const updatedCode = Object.entries(files).map(([n,c]) => `--- ${n} ---\n${c}`).join('\n\n');
// QA: testit (saa korjatut tiedostot)
- const qaAgent = agents.qa || Object.values(agents)[3];
+ const qaAgent = agents.qa || Object.values(agents)[4];
if (qaAgent) {
termLog(`\n[${stepN}] ${esc(qaAgent.name)} — testit`);
highlightAgent('qa');
@@ -897,7 +928,7 @@ OUTPUT FORMAT:
stepN++;
// Tarkkailija: yhteenveto + raportti + arvosana
- const obs = agents.observer || Object.values(agents)[5];
+ const obs = agents.observer || Object.values(agents)[6];
if (obs) {
termLog(`\n[${stepN}] ${esc(obs.name)} — projektin yhteenveto`);
highlightAgent('observer');
@@ -958,15 +989,24 @@ OUTPUT FORMAT:
}
async function kpnPipelineSimple(task) {
+ const cli = agents.client || Object.values(agents)[0];
termLog(`━━━ Pipeline ━━━`);
- termLog(`\n[1/3] Manageri`);
- const plan = await kpnRun('qwen-coder', `Analyse briefly, write a spec:\n${task}`);
+ termLog(`\n[1/4] ${esc(cli.name)} — vaatimukset`);
+ highlightAgent('client');
+ const brief = await kpnRun(cli.model, `${task}`, false, cli);
+ if (!brief) return;
+ termLog(`\n[2/4] Manageri`);
+ highlightAgent('manager');
+ const plan = await kpnRun('qwen-coder', `Requirements:\n${brief}\n\nAnalyse briefly, write a spec:\n${task}`);
if (!plan) return;
- termLog(`\n[2/3] Koodari`);
+ termLog(`\n[3/4] Koodari`);
+ highlightAgent('coder');
const code = await kpnRun('qwen-coder', `${plan}\n\nWrite the code.`);
if (!code) return;
- termLog(`\n[3/3] Testaaja`);
+ termLog(`\n[4/4] Testaaja`);
+ highlightAgent('tester');
await kpnRun('qwen-coder', `Review briefly:\n${code}`);
+ highlightAgent(null);
termLog(`\n━━━ Valmis ━━━`);
}
diff --git a/network-poc/hub-local.log b/network-poc/hub-local.log
index dba8b1c..7d62265 100644
--- a/network-poc/hub-local.log
+++ b/network-poc/hub-local.log
@@ -1,7 +1,34 @@
- Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
+ Compiling hub v0.3.1 (/Users/jaakko/code/kipina-codes/playground/agentic-studio/network-poc/hub)
+ Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.95s
Running `target/debug/hub`
-[2m2026-04-12T03:21:35.058711Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Tietokanta alustettu
-[2m2026-04-12T03:21:35.061635Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Kipinä Agent Hub v0.3.1 käynnistyy osoitteessa http://localhost:3000
-[2m2026-04-12T03:21:49.577628Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 1 yhdistyi osoitteesta 127.0.0.1
-[2m2026-04-12T03:21:49.600021Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 1 (natiivi) | 127.0.0.1 | Mac | Darwin 26.3.1 | 12 ydintä | 32768 MB RAM | varaus: 4 GB
-[2m2026-04-12T03:21:49.600099Z[0m [32m INFO[0m [2mhub[0m[2m:[0m GPU 0: Apple M2 Max | VRAM: 0/24576 MB | 0°C | 0%
+[2m2026-04-12T04:56:09.723604Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Tietokanta alustettu
+[2m2026-04-12T04:56:09.725088Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Kipinä Agent Hub v0.3.1 käynnistyy osoitteessa http://localhost:3000
+[2m2026-04-12T04:56:18.997935Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 1 yhdistyi osoitteesta 127.0.0.1
+[2m2026-04-12T04:56:19.027478Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 1 (natiivi) | 127.0.0.1 | Mac | Darwin 26.3.1 | 12 ydintä | 32768 MB RAM | varaus: 4 GB
+[2m2026-04-12T04:56:19.029931Z[0m [32m INFO[0m [2mhub[0m[2m:[0m GPU 0: Apple M2 Max | VRAM: 0/24576 MB | 0°C | 0%
+[2m2026-04-12T04:56:31.260470Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 2 yhdistyi osoitteesta 127.0.0.1
+[2m2026-04-12T04:56:31.281759Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 2 (selain) | 127.0.0.1 | MacIntel | 11 ydintä | ~8 GB RAM | GPU: ei GPU:ta | tehtävä: viewer | varaus: 0 GB
+[2m2026-04-12T04:56:31.283313Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Reititettiin API-pyyntö solmulle 1 (Malli: qwen-coder)
+
+[35m━━━ Solmu 1 ━━━ qwen2.5-coder:7b-instruct-q4_K_M (Ollama) ━━━[0m
+ Prompt: [33m"ping"[0m
+ Vastaus: [32mPong! How can I assist you today?[0m
+ 11 tokenia | 4502ms | [36m56.3 tok/s[0m
+[2m2026-04-12T04:56:36.419646Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 2 (127.0.0.1) poistui verkosta.
+[2m2026-04-12T04:56:36.433155Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 3 yhdistyi osoitteesta 127.0.0.1
+[2m2026-04-12T04:56:36.445127Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 3 (selain) | 127.0.0.1 | MacIntel | 11 ydintä | ~8 GB RAM | GPU: ei GPU:ta | tehtävä: viewer | varaus: 0 GB
+[2m2026-04-12T04:56:36.445818Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Reititettiin API-pyyntö solmulle 1 (Malli: qwen-coder)
+
+[35m━━━ Solmu 1 ━━━ qwen2.5-coder:7b-instruct-q4_K_M (Ollama) ━━━[0m
+ Prompt: [33m"ping"[0m
+ Vastaus: [32mPong! How can I assist you today? If you have any questions or need information on a specific topic, feel free to let me know.[0m
+ 31 tokenia | 679ms | [36m57.5 tok/s[0m
+[2m2026-04-12T04:56:39.466711Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 3 (127.0.0.1) poistui verkosta.
+[2m2026-04-12T04:56:43.881216Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 4 yhdistyi osoitteesta 127.0.0.1
+[2m2026-04-12T04:56:43.894385Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Solmu 4 (selain) | 127.0.0.1 | MacIntel | 3 ydintä | ~16 GB RAM | GPU: ei GPU:ta | tehtävä: viewer | varaus: 0 GB
+[2m2026-04-12T04:56:43.894960Z[0m [32m INFO[0m [2mhub[0m[2m:[0m Reititettiin API-pyyntö solmulle 1 (Malli: qwen-coder)
+
+[35m━━━ Solmu 1 ━━━ qwen2.5-coder:7b-instruct-q4_K_M (Ollama) ━━━[0m
+ Prompt: [33m"ping"[0m
+ Vastaus: [32mPong! How can I assist you today?[0m
+ 11 tokenia | 333ms | [36m58.7 tok/s[0m
diff --git a/network-poc/nodes.db b/network-poc/nodes.db
index 8a24237..4739a4a 100644
Binary files a/network-poc/nodes.db and b/network-poc/nodes.db differ