Asetukset-välilehti: kaikki LLM-parametrit muokattavissa UI:sta

Uusi "Asetukset"-tab jossa:
- System Prompt (tekstikenttä, Courier-fontti)
- Temperature (slider 0-1.5, reaaliaikainen arvo)
- Top-K (slider 1-100)
- Repetition Penalty (slider 1.0-2.0)
- Max Tokens (slider 64-4096)
- Stop-sekvenssit (yksi per rivi)
- Mallinvalinta (dropdown: 1.5B/3B/7B Q4/7B)
- "Palauta oletukset" -nappi

Kaikki tallentuvat localStorageen (kpn-settings).
Jokainen parametri selitetty hint-tekstillä.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaakko Vanhala
2026-04-09 22:44:03 +03:00
parent 1216e016c2
commit 90c9a7e4fa
3 changed files with 163 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
<!-- Asetukset-paneeli: kaikki LLM-parametrit muokattavissa -->
<div id="panel-settings" class="panel">
<div style="max-width:800px;margin:0 auto;padding:20px">
<h2 style="color:#e6edf3;margin-bottom:16px">Asetukset</h2>
<p style="color:#8b949e;margin-bottom:20px;font-size:14px">Kaikki kielimallin toimintaan vaikuttavat parametrit. Muutokset tallentuvat automaattisesti.</p>
<!-- System prompt -->
<div class="settings-section">
<h3 class="settings-title">System Prompt</h3>
<p class="settings-desc">Kielimallin perusohje joka lähetetään jokaisessa pyynnössä. Määrittää mallin käyttäytymisen.</p>
<textarea id="set-system-prompt" class="settings-textarea" rows="4"></textarea>
</div>
<!-- Sampling -->
<div class="settings-section">
<h3 class="settings-title">Sampling-parametrit</h3>
<p class="settings-desc">Kontrolloi miten malli valitsee seuraavan tokenin. <a href="#guide" onclick="switchTab('guide')" style="color:var(--accent)">Lue lisää oppaasta.</a></p>
<div class="settings-grid">
<div>
<label class="settings-label">Temperature <span id="set-temp-val" class="settings-val">0.7</span></label>
<input type="range" id="set-temperature" min="0" max="1.5" step="0.1" value="0.7" class="settings-slider">
<div class="settings-hint">0 = deterministic, 0.7 = balanced, 1.5 = creative</div>
</div>
<div>
<label class="settings-label">Top-K <span id="set-topk-val" class="settings-val">40</span></label>
<input type="range" id="set-topk" min="1" max="100" step="1" value="40" class="settings-slider">
<div class="settings-hint">Montako tokenia huomioidaan. 1 = greedy, 40 = oletus</div>
</div>
<div>
<label class="settings-label">Repetition Penalty <span id="set-rep-val" class="settings-val">1.15</span></label>
<input type="range" id="set-repeat" min="1.0" max="2.0" step="0.05" value="1.15" class="settings-slider">
<div class="settings-hint">Estää toistoa. 1.0 = ei rangaistusta, 1.15 = oletus</div>
</div>
<div>
<label class="settings-label">Max Tokens <span id="set-maxtok-val" class="settings-val">1024</span></label>
<input type="range" id="set-maxtokens" min="64" max="4096" step="64" value="1024" class="settings-slider">
<div class="settings-hint">Vastauksen maksimipituus tokeneina</div>
</div>
</div>
</div>
<!-- Stop-sekvenssit -->
<div class="settings-section">
<h3 class="settings-title">Stop-sekvenssit</h3>
<p class="settings-desc">Generointi katkeaa kun malli tuottaa jonkin näistä. Yksi per rivi.</p>
<textarea id="set-stop-sequences" class="settings-textarea" rows="4"></textarea>
</div>
<!-- Malli -->
<div class="settings-section">
<h3 class="settings-title">Malli (Ollama)</h3>
<p class="settings-desc">Natiivisolmun käyttämä kielimalli. Muutos vaatii native-noden uudelleenkäynnistyksen.</p>
<select id="set-model" class="settings-select">
<option value="qwen2.5-coder:1.5b">Qwen2.5-Coder:1.5B (~80 tok/s, ~1GB)</option>
<option value="qwen2.5-coder:3b">Qwen2.5-Coder:3B (~50 tok/s, ~2GB)</option>
<option value="qwen2.5-coder:7b-instruct-q4_K_M">Qwen2.5-Coder:7B Q4 (~30 tok/s, ~4GB)</option>
<option value="qwen2.5-coder:7b">Qwen2.5-Coder:7B (~20 tok/s, ~7GB)</option>
</select>
</div>
<!-- Reset -->
<div style="margin-top:24px;padding-top:16px;border-top:1px solid var(--border)">
<button class="btn btn-red" onclick="resetSettings()" style="padding:6px 16px">Palauta oletukset</button>
<span style="color:#8b949e;font-size:12px;margin-left:8px">Palauttaa kaikki parametrit oletusarvoihin</span>
</div>
</div>
</div>

View File

@@ -5,6 +5,7 @@ import Terminal from "../components/Terminal.astro";
import Editor from "../components/Editor.astro"; import Editor from "../components/Editor.astro";
import Guide from "../components/Guide.astro"; import Guide from "../components/Guide.astro";
import AgentBar from "../components/AgentBar.astro"; import AgentBar from "../components/AgentBar.astro";
import Settings from "../components/Settings.astro";
--- ---
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fi"> <html lang="fi">
@@ -30,6 +31,7 @@ import AgentBar from "../components/AgentBar.astro";
<div class="tab active" onclick="switchTab('agents')">Agentit</div> <div class="tab active" onclick="switchTab('agents')">Agentit</div>
<div class="tab" onclick="switchTab('editor')">Editor</div> <div class="tab" onclick="switchTab('editor')">Editor</div>
<div class="tab" onclick="switchTab('guide')">Opas</div> <div class="tab" onclick="switchTab('guide')">Opas</div>
<div class="tab" onclick="switchTab('settings')">Asetukset</div>
</div> </div>
<!-- Agents-paneeli --> <!-- Agents-paneeli -->
@@ -41,6 +43,7 @@ import AgentBar from "../components/AgentBar.astro";
<Editor /> <Editor />
<Guide /> <Guide />
<Settings />
</div> </div>
<script is:inline> <script is:inline>
@@ -95,6 +98,19 @@ Provide a brief risk assessment with severity (low/medium/high/critical).` },
function saveAgents() { localStorage.setItem('kpn-agents', JSON.stringify(agents)); } function saveAgents() { localStorage.setItem('kpn-agents', JSON.stringify(agents)); }
function getAgentModel(name) { const a = agents[name]; return a ? a.model : name; } function getAgentModel(name) { const a = agents[name]; return a ? a.model : name; }
// LLM-asetukset (localStorage-persistenssi)
const defaultSettings = {
systemPrompt: "You are a coding assistant. Respond with ONLY code. No explanations, no markdown fences, no 'Please note' text. Only working code with proper imports.",
temperature: 0.7,
topK: 40,
repeatPenalty: 1.15,
maxTokens: 1024,
stopSequences: "\\n###\\n\\nExplanation\\nNote:\\nPlease note\\nThis is a basic\\n```\\n\\n\\n// Example\\n# Example",
model: "qwen2.5-coder:3b",
};
let settings = JSON.parse(localStorage.getItem('kpn-settings') || 'null') || { ...defaultSettings };
function saveSettings() { localStorage.setItem('kpn-settings', JSON.stringify(settings)); }
// === Tab switching === // === Tab switching ===
window.switchTab = function(tab) { window.switchTab = function(tab) {
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active')); document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
@@ -800,6 +816,58 @@ Provide a brief risk assessment with severity (low/medium/high/critical).` },
} }
return html; return html;
} }
// === Settings panel ===
function initSettings() {
const els = {
systemPrompt: document.getElementById('set-system-prompt'),
temperature: document.getElementById('set-temperature'),
tempVal: document.getElementById('set-temp-val'),
topK: document.getElementById('set-topk'),
topkVal: document.getElementById('set-topk-val'),
repeat: document.getElementById('set-repeat'),
repVal: document.getElementById('set-rep-val'),
maxTokens: document.getElementById('set-maxtokens'),
maxtokVal: document.getElementById('set-maxtok-val'),
stopSeq: document.getElementById('set-stop-sequences'),
model: document.getElementById('set-model'),
};
if (!els.systemPrompt) return;
// Lataa arvot
els.systemPrompt.value = settings.systemPrompt;
els.temperature.value = settings.temperature;
els.tempVal.textContent = settings.temperature;
els.topK.value = settings.topK;
els.topkVal.textContent = settings.topK;
els.repeat.value = settings.repeatPenalty;
els.repVal.textContent = settings.repeatPenalty;
els.maxTokens.value = settings.maxTokens;
els.maxtokVal.textContent = settings.maxTokens;
els.stopSeq.value = settings.stopSequences.replace(/\\n/g, '\n');
els.model.value = settings.model;
// Tallenna muutokset
els.systemPrompt.oninput = () => { settings.systemPrompt = els.systemPrompt.value; saveSettings(); };
els.temperature.oninput = () => { settings.temperature = +els.temperature.value; els.tempVal.textContent = settings.temperature; saveSettings(); };
els.topK.oninput = () => { settings.topK = +els.topK.value; els.topkVal.textContent = settings.topK; saveSettings(); };
els.repeat.oninput = () => { settings.repeatPenalty = +els.repeat.value; els.repVal.textContent = settings.repeatPenalty; saveSettings(); };
els.maxTokens.oninput = () => { settings.maxTokens = +els.maxTokens.value; els.maxtokVal.textContent = settings.maxTokens; saveSettings(); };
els.stopSeq.oninput = () => { settings.stopSequences = els.stopSeq.value.replace(/\n/g, '\\n'); saveSettings(); };
els.model.onchange = () => { settings.model = els.model.value; saveSettings(); };
}
// Alustetaan kun settings-tab avataan
const origSwitchTab = window.switchTab;
window.switchTab = function(tab) {
origSwitchTab(tab);
if (tab === 'settings') initSettings();
};
window.resetSettings = function() {
if (!confirm('Palautetaanko kaikki asetukset oletuksiin?')) return;
settings = { ...defaultSettings };
saveSettings();
initSettings();
};
</script> </script>
</body> </body>
</html> </html>

View File

@@ -165,6 +165,34 @@ body {
box-shadow: 0 0 25px rgba(88,166,255,0.8); box-shadow: 0 0 25px rgba(88,166,255,0.8);
} }
/* Settings */
.settings-section {
margin-bottom: 24px; padding: 16px; background: var(--panel);
border: 1px solid var(--border); border-radius: 6px;
}
.settings-title { color: #e6edf3; font-size: 15px; margin-bottom: 4px; }
.settings-desc { color: #8b949e; font-size: 13px; margin-bottom: 12px; }
.settings-label { color: var(--text); font-size: 13px; display: block; margin-bottom: 4px; }
.settings-val { color: var(--accent); font-weight: 600; float: right; }
.settings-hint { color: #8b949e; font-size: 11px; margin-top: 2px; }
.settings-textarea {
width: 100%; background: var(--bg); color: var(--text);
border: 1px solid var(--border); border-radius: 4px;
padding: 8px; font-size: 13px; font-family: 'Courier New', monospace;
resize: vertical;
}
.settings-select {
width: 100%; background: var(--bg); color: var(--text);
border: 1px solid var(--border); border-radius: 4px;
padding: 8px; font-size: 13px;
}
.settings-slider {
width: 100%; accent-color: var(--accent);
}
.settings-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
}
/* Animations */ /* Animations */
@keyframes blink { 0%,100% { opacity:1 } 50% { opacity:0 } } @keyframes blink { 0%,100% { opacity:1 } 50% { opacity:0 } }
@keyframes spin { to { transform: rotate(360deg) } } @keyframes spin { to { transform: rotate(360deg) } }