diff --git a/network-poc/static/index.html b/network-poc/static/index.html index 5c39266..84cfac2 100644 --- a/network-poc/static/index.html +++ b/network-poc/static/index.html @@ -724,6 +724,7 @@
Laskentaverkko
Koodilaboratorio
Kipinä Agentic Playground
+
Agent Builder
Opas
@@ -1131,6 +1132,70 @@ +
+
+

Agent Builder

+

Luo ja muokkaa agentteja. Agentit tallentuvat palvelimelle ja ovat kaikkien käytettävissä.

+ + +
+ + + + + + +
+
+

Ladataan opasta...

@@ -1723,7 +1788,7 @@ IMPORTANT: Include get_db() dependency for FastAPI` }, // URL-hash navigointi const initHash = window.location.hash.replace('#', ''); - const hashMap = { 'laskentaverkko': 'network', 'network': 'network', 'codelab': 'codelab', 'agents': 'agents', 'guide': 'guide' }; + const hashMap = { 'laskentaverkko': 'network', 'network': 'network', 'codelab': 'codelab', 'agents': 'agents', 'builder': 'builder', 'guide': 'guide' }; if (hashMap[initHash]) { switchMainTab(hashMap[initHash]); } @@ -4521,6 +4586,142 @@ ${filesHtml} }, 100); }); + // ── Agent Builder ── + + const BUILDER_AVATARS = [ + '/avatars/kipina_notext.png', '/avatars/karhunpentu.png', '/avatars/kettu_notext.png', + '/avatars/pesukarhu_notext.png', '/avatars/susi_notext.png', '/avatars/laiskiainen_notext.png', + '/avatars/aikuinen_susi.png', '/avatars/gecko_notext.png' + ]; + + let builderAgents = []; + let builderEditing = null; // null = uusi, string = id + + async function builderLoad() { + try { + const res = await fetch('/api/v1/agents'); + if (res.ok) builderAgents = await res.json(); + } catch(e) {} + builderRenderList(); + } + + function builderRenderList() { + const list = document.getElementById('builder-agent-list'); + if (!list) return; + list.innerHTML = builderAgents.map(a => ` +
+ +
+
${esc(a.name)}
+
${esc(a.model)} · ${a.role}
+
+
+ `).join(''); + } + + function builderNew() { + builderEditing = null; + document.getElementById('builder-form').style.display = 'block'; + document.getElementById('builder-new-btn').style.display = 'none'; + document.getElementById('builder-delete-btn').style.display = 'none'; + document.getElementById('builder-id').value = ''; + document.getElementById('builder-id').disabled = false; + document.getElementById('builder-name').value = ''; + document.getElementById('builder-role').value = 'coder'; + document.getElementById('builder-model').value = 'qwen2.5-coder:7b'; + document.getElementById('builder-color').value = '#3fb950'; + document.getElementById('builder-docs').value = ''; + document.getElementById('builder-prompt').value = ''; + document.getElementById('builder-temp').value = '0.7'; + document.getElementById('builder-topk').value = '40'; + document.getElementById('builder-maxtokens').value = '512'; + document.getElementById('builder-avatar-preview').src = '/avatars/kipina_notext.png'; + builderRenderAvatarSelect(); + } + + function builderEdit(id) { + const a = builderAgents.find(x => x.id === id); + if (!a) return; + builderEditing = id; + document.getElementById('builder-form').style.display = 'block'; + document.getElementById('builder-new-btn').style.display = 'none'; + document.getElementById('builder-delete-btn').style.display = a.is_default ? 'none' : 'inline-block'; + document.getElementById('builder-id').value = a.id; + document.getElementById('builder-id').disabled = true; + document.getElementById('builder-name').value = a.name; + document.getElementById('builder-role').value = a.role; + document.getElementById('builder-model').value = a.model; + document.getElementById('builder-color').value = a.color; + document.getElementById('builder-docs').value = a.docs || ''; + document.getElementById('builder-prompt').value = a.prompt; + document.getElementById('builder-temp').value = a.temperature; + document.getElementById('builder-topk').value = a.top_k; + document.getElementById('builder-maxtokens').value = a.max_tokens; + document.getElementById('builder-avatar-preview').src = a.avatar; + builderRenderAvatarSelect(); + } + + function builderRenderAvatarSelect() { + const container = document.getElementById('builder-avatar-select'); + container.innerHTML = BUILDER_AVATARS.map(src => + `` + ).join(''); + } + + async function builderSave() { + const payload = { + id: document.getElementById('builder-id').value.trim().toLowerCase().replace(/[^a-z0-9_-]/g, ''), + name: document.getElementById('builder-name').value.trim(), + avatar: document.getElementById('builder-avatar-preview').src.replace(location.origin, ''), + role: document.getElementById('builder-role').value, + model: document.getElementById('builder-model').value.trim(), + color: document.getElementById('builder-color').value, + docs: document.getElementById('builder-docs').value.trim() || null, + prompt: document.getElementById('builder-prompt').value, + temperature: parseFloat(document.getElementById('builder-temp').value) || 0.7, + top_k: parseInt(document.getElementById('builder-topk').value) || 40, + max_tokens: parseInt(document.getElementById('builder-maxtokens').value) || 512, + repetition_penalty: 1.15, + }; + if (!payload.id || !payload.name) { alert('Tunniste ja nimi vaaditaan'); return; } + try { + const res = await fetch('/api/v1/agents', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + if (res.ok) { + builderCancel(); + await builderLoad(); + } else { + alert('Virhe: ' + await res.text()); + } + } catch(e) { alert('Virhe: ' + e.message); } + } + + async function builderDelete() { + if (!builderEditing) return; + if (!confirm('Poistetaanko agentti "' + builderEditing + '"?')) return; + try { + const res = await fetch('/api/v1/agents/' + builderEditing, { method: 'DELETE' }); + if (res.ok) { + builderCancel(); + await builderLoad(); + } else { + alert('Virhe: ' + await res.text()); + } + } catch(e) { alert('Virhe: ' + e.message); } + } + + function builderCancel() { + document.getElementById('builder-form').style.display = 'none'; + document.getElementById('builder-new-btn').style.display = 'inline-block'; + builderEditing = null; + } + + // Ladataan agentit kun builder-tabi avataan + builderLoad(); + // GUIDE.md:n lataus ja renderöinti (async function loadGuide() { const container = document.getElementById('guide-content');