Skip to content

Commit

Permalink
Add settings modal to web ui
Browse files Browse the repository at this point in the history
  • Loading branch information
jart committed Nov 24, 2024
1 parent a494bd3 commit bc82424
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 2 deletions.
1 change: 1 addition & 0 deletions llamafile/server/flagz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Client::flagz()
json["no_display_prompt"] = FLAG_no_display_prompt;
json["nologo"] = FLAG_nologo;
json["temperature"] = FLAG_temperature;
json["top_p"] = FLAG_top_p;
json["presence_penalty"] = FLAG_presence_penalty;
json["frequency_penalty"] = FLAG_frequency_penalty;
if (FLAG_seed == LLAMA_DEFAULT_SEED) {
Expand Down
152 changes: 152 additions & 0 deletions llamafile/server/www/chatbot.css
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,158 @@ ul li:first-child {
display: block;
}

.settings-button {
padding: 0.75rem 1rem;
background: #6c757d;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.2s;
}

.settings-button:hover {
background: #5c636a;
}

.settings-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}

.settings-panel {
background: white;
padding: 20px;
border-radius: 8px;
max-width: 500px;
width: 90%;
}

.settings-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}

.settings-header button {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}

.setting-item {
margin-bottom: 20px;
}

.setting-item label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}

.setting-item input {
width: 100%;
margin-bottom: 8px;
}

.setting-description {
font-size: 0.9em;
color: #666;
margin-top: 4px;
}

.setting-item.disabled label,
.setting-item.disabled input,
.setting-item.disabled .setting-description {
opacity: 0.5;
cursor: not-allowed;
}

.setting-item.disabled input {
pointer-events: none;
}

.setting-item.disabled {
pointer-events: none;
opacity: 0.8;
}

.setting-item.disabled input[type="range"] {
background: #ccc;
}

.setting-item.disabled label,
.setting-item.disabled .setting-description {
color: #999;
}

/* Styling for penalty range inputs */
.penalty-range {
-webkit-appearance: none;
appearance: none;
height: 6px;
border-radius: 8px;
}

/* Chrome track styling */
.penalty-range::-webkit-slider-runnable-track {
width: 100%;
height: 6px;
border-radius: 8px;
background: linear-gradient(to right,
#ff000088 0%,
#00000022 30%,
#00000022 70%,
#ff000088 100%
);
}

/* Firefox track styling */
.penalty-range::-moz-range-track {
width: 100%;
height: 6px;
border-radius: 8px;
background: linear-gradient(to right,
#ff000088 0%,
#00000022 30%,
#00000022 70%,
#ff000088 100%
);
}

/* Chrome thumb styling */
.penalty-range::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #666;
cursor: pointer;
margin-top: -5px; /* Centers the thumb on the track */
}

/* Firefox thumb styling */
.penalty-range::-moz-range-thumb {
width: 16px;
height: 16px;
border: none;
border-radius: 50%;
background: #666;
cursor: pointer;
}

@media print {

html,
Expand Down
94 changes: 93 additions & 1 deletion llamafile/server/www/chatbot.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ const DEFAULT_FLAGZ = {
"frequency_penalty": 0,
"presence_penalty": 0,
"temperature": 0.8,
"top_p": 0.95,
"seed": null
};

const chatMessages = document.getElementById("chat-messages");
const chatInput = document.getElementById("chat-input");
const sendButton = document.getElementById("send-button");
const stopButton = document.getElementById("stop-button");
const settingsButton = document.getElementById("settings-button");
const settingsModal = document.getElementById("settings-modal");
const closeSettings = document.getElementById("close-settings");

let abortController = null;
let disableAutoScroll = false;
Expand Down Expand Up @@ -168,6 +172,7 @@ async function sendMessage() {
// update chat history
chatHistory.push({ role: "user", content: message });

const settings = loadSettings();
try {
const response = await fetch(API_ENDPOINT, {
method: "POST",
Expand All @@ -178,7 +183,10 @@ async function sendMessage() {
body: JSON.stringify({
model: "gpt-3.5-turbo",
messages: chatHistory,
temperature: 0.0,
temperature: settings.temperature,
top_p: settings.top_p,
presence_penalty: settings.presence_penalty,
frequency_penalty: settings.frequency_penalty,
stream: true
}),
signal: abortController.signal
Expand Down Expand Up @@ -361,9 +369,93 @@ function startChat(history) {
scrollToBottom();
}

function loadSettings() {
const stored = localStorage.getItem('v1.modelSettings');
if (stored) {
return JSON.parse(stored);
}
return {
temperature: flagz.temperature,
top_p: flagz.top_p,
presence_penalty: flagz.presence_penalty,
frequency_penalty: flagz.frequency_penalty,
};
}

function saveSettings(settings) {
localStorage.setItem('v1.modelSettings', JSON.stringify(settings));
}

function formatDoubleWithPlus(x) {
return (x >= 0 ? "+" : "") + x.toFixed(2);
}

function updateSettingsDisplay(settings) {
document.getElementById("temp-value").textContent = settings.temperature ? settings.temperature.toFixed(2) : "0.00 (deterministic)";
document.getElementById("top-p-value").textContent = settings.top_p.toFixed(2);
document.getElementById("presence-value").textContent = formatDoubleWithPlus(settings.presence_penalty);
document.getElementById("frequency-value").textContent = formatDoubleWithPlus(settings.frequency_penalty);
document.getElementById("temperature").value = settings.temperature;
document.getElementById("top-p").value = settings.top_p;
document.getElementById("presence-penalty").value = settings.presence_penalty;
document.getElementById("frequency-penalty").value = settings.frequency_penalty;

// Handle top-p disabling - using a more reliable selector
const topPSettingItem = document.querySelector('.setting-item:has(#top-p)');
if (settings.temperature === 0) {
topPSettingItem.classList.add('disabled');
} else {
topPSettingItem.classList.remove('disabled');
}

// Update top-p description with percentage
const topPDescription = topPSettingItem.querySelector('.setting-description');
if (settings.top_p >= 1) {
topPDescription.textContent = "Disabled. All tokens will be considered by the sampler.";
} else if (settings.top_p > .5) {
const percentage = Math.round((1 - settings.top_p) * 100);
topPDescription.textContent = `The bottom ${percentage}% tokens will be ignored by the sampler.`;
} else {
const percentage = Math.round(settings.top_p * 100);
topPDescription.textContent = `Only the top ${percentage}% tokens will be considered by the sampler.`;
}
}

function setupSettings() {
settingsButton.addEventListener("click", () => {
settingsModal.style.display = "flex";
updateSettingsDisplay(loadSettings());
});
closeSettings.addEventListener("click", () => {
settingsModal.style.display = "none";
});
["temperature", "top-p", "presence-penalty", "frequency-penalty"].forEach(id => {
const element = document.getElementById(id);
element.addEventListener("input", (e) => {
const settings = loadSettings();
const value = parseFloat(e.target.value);
const key = id.replace(/-/g, '_');
settings[key] = value;
saveSettings(settings);
updateSettingsDisplay(settings);
});
});
settingsModal.addEventListener("mousedown", (e) => {
if (e.target === settingsModal) {
settingsModal.style.display = "none";
}
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
settingsModal.style.display = "none";
}
});
}

async function chatbot() {
flagz = await fetchFlagz();
updateModelInfo();
setupSettings();
startChat([{ role: "system", content: getSystemPrompt() }]);
sendButton.addEventListener("click", sendMessage);
stopButton.addEventListener("click", stopMessage);
Expand Down
2 changes: 1 addition & 1 deletion llamafile/server/www/highlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class HighlightDom extends Highlight {
feed(s) {
for (let i = 0; i < s.length; ++i) {
this.text += s[i];
if (isspace(s[i])) {
if (isspace(s[i]) || this.text.length > 50) {
this.flushText();
}
}
Expand Down
30 changes: 30 additions & 0 deletions llamafile/server/www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,36 @@ <h1>
<textarea class="chat-input" id="chat-input" placeholder="Type your message..." rows="1" autocomplete="off"></textarea>
<button class="send-button" id="send-button">Send</button>
<button class="stop-button" id="stop-button" style="display:none">Stop</button>
<button class="settings-button" id="settings-button" title="Settings">⚙️</button>
</div>
</div>

<div id="settings-modal" class="settings-modal" style="display:none">
<div class="settings-panel">
<div class="settings-header">
<h2>Model Settings</h2>
<button id="close-settings">×</button>
</div>
<div class="setting-item">
<label>temperature = <span id="temp-value">0.8</span></label>
<input type="range" id="temperature" min="0" max="2" step="0.01" value="0.8">
<p class="setting-description">How much randomness to use during sampling. Higher values improve improvisation, and lower values result in determinism.</p>
</div>
<div class="setting-item">
<label>top_p = <span id="top-p-value">0.95</span></label>
<input type="range" id="top-p" min="0.01" max="1" step="0.01" value="0.95">
<p class="setting-description">Controls diversity via nucleus sampling. Reduces the probability mass from which to sample.</p>
</div>
<div class="setting-item">
<label>presence_penalty = <span id="presence-value">0</span></label>
<input type="range" id="presence-penalty" class="penalty-range" min="-2" max="2" step="0.1" value="0">
<p class="setting-description">Positive values discourage repetition; negative values encourage it.</p>
</div>
<div class="setting-item">
<label>frequency_penalty = <span id="frequency-value">0</span></label>
<input type="range" id="frequency-penalty" class="penalty-range" min="-2" max="2" step="0.1" value="0">
<p class="setting-description">Penalizes new tokens based on their frequency in the text so far.</p>
</div>
</div>
</div>
<div id="bottom"></div>
Expand Down

0 comments on commit bc82424

Please sign in to comment.