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');