summaryrefslogtreecommitdiff
path: root/examples/server/public
diff options
context:
space:
mode:
Diffstat (limited to 'examples/server/public')
-rw-r--r--examples/server/public/index.html115
1 files changed, 81 insertions, 34 deletions
diff --git a/examples/server/public/index.html b/examples/server/public/index.html
index f539884e..39d7bb93 100644
--- a/examples/server/public/index.html
+++ b/examples/server/public/index.html
@@ -125,6 +125,7 @@
background-color: #222;
color: #ddd;
}
+
code {
font-family: monospace;
padding: 0.1em 0.3em;
@@ -141,7 +142,8 @@
display: inline;
}
- header, footer {
+ header,
+ footer {
text-align: center;
}
@@ -163,6 +165,7 @@
0% {
background-position: 0%;
}
+
100% {
background-position: 100%;
}
@@ -181,6 +184,7 @@
--loading-color-1: #22222200;
--loading-color-2: #222222ff;
}
+
.popover-content {
background-color: black;
}
@@ -194,6 +198,8 @@
import { llama } from '/completion.js';
import { SchemaConverter } from '/json-schema-to-grammar.mjs';
+ let selected_image = false;
+ var slot_id = -1;
const session = signal({
prompt: "This is a conversation between User and Llama, a friendly chatbot. Llama is helpful, kind, honest, good at writing, and never fails to answer any requests immediately and with precision.",
@@ -203,6 +209,7 @@
type: "chat", // "chat" | "completion"
char: "Llama",
user: "User",
+ image_selected: ''
})
const params = signal({
@@ -220,7 +227,9 @@
mirostat_tau: 5, // target entropy
mirostat_eta: 0.1, // learning rate
grammar: '',
- n_probs: 0, // no completion_probabilities
+ n_probs: 0, // no completion_probabilities,
+ image_data: [],
+ cache_prompt: true
})
/* START: Support for storing prompt templates and parameters in borwser LocalStorage */
@@ -270,6 +279,7 @@
// saved templates were successfuly imported.
console.log('Processing saved templates and updating default template')
+ params.value = { ...params.value, image_data: [] };
//console.log(importedTemplates);
savedUserTemplates.value = importedTemplates;
@@ -294,7 +304,9 @@
function userTemplateApply(t) {
session.value = t.data.session;
+ session.value = { ...session.value, image_selected: '' };
params.value = t.data.params;
+ params.value = { ...params.value, image_data: [] };
}
function userTemplateResetToDefaultAndApply() {
@@ -385,20 +397,25 @@
throw new Error("already running");
}
controller.value = new AbortController();
- for await (const chunk of llama(prompt, llamaParams, {controller: controller.value})) {
+ for await (const chunk of llama(prompt, llamaParams, { controller: controller.value })) {
const data = chunk.data;
if (data.stop) {
while (
currentMessages.length > 0 &&
currentMessages[currentMessages.length - 1].content.match(/\n$/) != null
- ) {
+ ) {
currentMessages.pop();
}
transcriptUpdate([...history, [char, currentMessages]])
console.log("Completion finished: '", currentMessages.map(msg => msg.content).join(''), "', summary: ", data);
} else {
currentMessages.push(data);
+ slot_id = data.slot_id;
+ if (selected_image && !data.multimodal) {
+ alert("The server was not compiled for multimodal or the model projector can't be loaded.");
+ return;
+ }
transcriptUpdate([...history, [char, currentMessages]])
}
@@ -419,7 +436,7 @@
transcriptUpdate([...session.value.transcript, ["{{user}}", msg]])
- const prompt = template(session.value.template, {
+ let prompt = template(session.value.template, {
message: msg,
history: session.value.transcript.flatMap(
([name, data]) =>
@@ -434,9 +451,12 @@
)
).join("\n"),
});
-
+ if (selected_image) {
+ prompt = `A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions.\nUSER:[img-10]${msg}\nASSISTANT:`;
+ }
await runLlama(prompt, {
...params.value,
+ slot_id: slot_id,
stop: ["</s>", template("{{char}}:"), template("{{user}}:")],
}, "{{char}}");
}
@@ -446,10 +466,11 @@
console.log('already running...');
return;
}
- const {prompt} = session.value;
+ const { prompt } = session.value;
transcriptUpdate([...session.value.transcript, ["", prompt]]);
await runLlama(prompt, {
...params.value,
+ slot_id: slot_id,
stop: [],
}, "");
}
@@ -467,6 +488,27 @@
transcriptUpdate([]);
}
+ const uploadImage = (e) => {
+ e.preventDefault();
+ document.getElementById("fileInput").click();
+ document.getElementById("fileInput").addEventListener("change", function (event) {
+ const selectedFile = event.target.files[0];
+ if (selectedFile) {
+ const reader = new FileReader();
+ reader.onload = function () {
+ const image_data = reader.result;
+ session.value = { ...session.value, image_selected: image_data };
+ params.value = {
+ ...params.value, image_data: [
+ { data: image_data.replace(/data:image\/[^;]+;base64,/, ''), id: 10 }]
+ }
+ };
+ selected_image = true;
+ reader.readAsDataURL(selectedFile);
+ }
+ });
+ }
+
function MessageInput() {
const message = useSignal("")
@@ -497,6 +539,7 @@
</div>
<div class="right">
<button type="submit" disabled=${generating.value}>Send</button>
+ <button onclick=${uploadImage}>Upload Image</button>
<button onclick=${stop} disabled=${!generating.value}>Stop</button>
<button onclick=${reset}>Reset</button>
</div>
@@ -540,7 +583,7 @@
data;
message = html`<${Markdownish} text=${template(text)} />`
}
- if(user) {
+ if (user) {
return html`<p key=${index}><strong>${template(user)}:</strong> ${message}</p>`
} else {
return html`<p key=${index}>${message}</p>`
@@ -549,6 +592,7 @@
return html`
<section id="chat" ref=${container}>
+ <img style="width: 60%;${!session.value.image_selected ? `display: none;` : ``}" src="${session.value.image_selected}"/>
${messages.flatMap(chatLine)}
</section>`;
};
@@ -567,7 +611,7 @@
const converter = new SchemaConverter(
grammarJsonSchemaPropOrder.value
.split(',')
- .reduce((acc, cur, i) => ({...acc, [cur.trim()]: i}), {})
+ .reduce((acc, cur, i) => ({ ...acc, [cur.trim()]: i }), {})
)
converter.visit(schema, '')
params.value = {
@@ -579,7 +623,7 @@
}
}
- const FloatField = ({label, max, min, name, step, value}) => {
+ const FloatField = ({ label, max, min, name, step, value }) => {
return html`
<div>
<label for="${name}">${label}</label>
@@ -589,7 +633,7 @@
`
};
- const IntField = ({label, max, min, name, value}) => {
+ const IntField = ({ label, max, min, name, value }) => {
return html`
<div>
<label for="${name}">${label}</label>
@@ -672,7 +716,7 @@
${GrammarControl()}
</fieldset>
`
- );
+ );
const CompletionConfigForm = () => (
html`
@@ -694,20 +738,20 @@
${session.value.type === 'chat' ? ChatConfigForm() : CompletionConfigForm()}
<fieldset class="two">
- ${IntField({label: "Predictions", max: 2048, min: -1, name: "n_predict", value: params.value.n_predict})}
- ${FloatField({label: "Temperature", max: 1.5, min: 0.0, name: "temperature", step: 0.01, value: params.value.temperature})}
- ${FloatField({label: "Penalize repeat sequence", max: 2.0, min: 0.0, name: "repeat_penalty", step: 0.01, value: params.value.repeat_penalty})}
- ${IntField({label: "Consider N tokens for penalize", max: 2048, min: 0, name: "repeat_last_n", value: params.value.repeat_last_n})}
- ${IntField({label: "Top-K sampling", max: 100, min: -1, name: "top_k", value: params.value.top_k})}
- ${FloatField({label: "Top-P sampling", max: 1.0, min: 0.0, name: "top_p", step: 0.01, value: params.value.top_p})}
+ ${IntField({ label: "Predictions", max: 2048, min: -1, name: "n_predict", value: params.value.n_predict })}
+ ${FloatField({ label: "Temperature", max: 1.5, min: 0.0, name: "temperature", step: 0.01, value: params.value.temperature })}
+ ${FloatField({ label: "Penalize repeat sequence", max: 2.0, min: 0.0, name: "repeat_penalty", step: 0.01, value: params.value.repeat_penalty })}
+ ${IntField({ label: "Consider N tokens for penalize", max: 2048, min: 0, name: "repeat_last_n", value: params.value.repeat_last_n })}
+ ${IntField({ label: "Top-K sampling", max: 100, min: -1, name: "top_k", value: params.value.top_k })}
+ ${FloatField({ label: "Top-P sampling", max: 1.0, min: 0.0, name: "top_p", step: 0.01, value: params.value.top_p })}
</fieldset>
<details>
<summary>More options</summary>
<fieldset class="two">
- ${FloatField({label: "TFS-Z", max: 1.0, min: 0.0, name: "tfs_z", step: 0.01, value: params.value.tfs_z})}
- ${FloatField({label: "Typical P", max: 1.0, min: 0.0, name: "typical_p", step: 0.01, value: params.value.typical_p})}
- ${FloatField({label: "Presence penalty", max: 1.0, min: 0.0, name: "presence_penalty", step: 0.01, value: params.value.presence_penalty})}
- ${FloatField({label: "Frequency penalty", max: 1.0, min: 0.0, name: "frequency_penalty", step: 0.01, value: params.value.frequency_penalty})}
+ ${FloatField({ label: "TFS-Z", max: 1.0, min: 0.0, name: "tfs_z", step: 0.01, value: params.value.tfs_z })}
+ ${FloatField({ label: "Typical P", max: 1.0, min: 0.0, name: "typical_p", step: 0.01, value: params.value.typical_p })}
+ ${FloatField({ label: "Presence penalty", max: 1.0, min: 0.0, name: "presence_penalty", step: 0.01, value: params.value.presence_penalty })}
+ ${FloatField({ label: "Frequency penalty", max: 1.0, min: 0.0, name: "frequency_penalty", step: 0.01, value: params.value.frequency_penalty })}
</fieldset>
<hr />
<fieldset class="three">
@@ -716,11 +760,11 @@
<label><input type="radio" name="mirostat" value="1" checked=${params.value.mirostat == 1} oninput=${updateParamsInt} /> Mirostat v1</label>
<label><input type="radio" name="mirostat" value="2" checked=${params.value.mirostat == 2} oninput=${updateParamsInt} /> Mirostat v2</label>
</div>
- ${FloatField({label: "Mirostat tau", max: 10.0, min: 0.0, name: "mirostat_tau", step: 0.01, value: params.value.mirostat_tau})}
- ${FloatField({label: "Mirostat eta", max: 1.0, min: 0.0, name: "mirostat_eta", step: 0.01, value: params.value.mirostat_eta})}
+ ${FloatField({ label: "Mirostat tau", max: 10.0, min: 0.0, name: "mirostat_tau", step: 0.01, value: params.value.mirostat_tau })}
+ ${FloatField({ label: "Mirostat eta", max: 1.0, min: 0.0, name: "mirostat_eta", step: 0.01, value: params.value.mirostat_eta })}
</fieldset>
<fieldset>
- ${IntField({label: "Show Probabilities", max: 10, min: 0, name: "n_probs", value: params.value.n_probs})}
+ ${IntField({ label: "Show Probabilities", max: 10, min: 0, name: "n_probs", value: params.value.n_probs })}
</fieldset>
</details>
</form>
@@ -759,20 +803,20 @@
const popoverChildren = html`
<div class="prob-set">
${probs.map((p, index) => {
- return html`
+ return html`
<div
key=${index}
title=${`prob: ${p.prob}`}
style=${{
- padding: '0.3em',
- backgroundColor: p.tok_str === content ? probColor(p.prob) : 'transparent'
- }}
+ padding: '0.3em',
+ backgroundColor: p.tok_str === content ? probColor(p.prob) : 'transparent'
+ }}
>
<span>${p.tok_str}: </span>
<span>${Math.floor(p.prob * 100)}%</span>
</div>
`
- })}
+ })}
</div>
`
@@ -851,9 +895,9 @@
ref=${popoverRef}
class="popover-content"
style=${{
- top: position.value.top,
- left: position.value.left,
- }}
+ top: position.value.top,
+ left: position.value.left,
+ }}
>
${props.popoverChildren}
</div>
@@ -952,8 +996,11 @@
</head>
<body>
- <div id="container"></div>
+ <div id="container">
+ <input type="file" id="fileInput" accept="image/*" style="display: none;">
+ </div>
<div id="portal"></div>
</body>
</html>
+