plug commited on
Commit
ee6d5fb
·
1 Parent(s): 2c12345

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +344 -543
index.html CHANGED
@@ -1,576 +1,377 @@
1
- <html>
2
- <head>
3
- <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
4
- <title>Candle Phi 1.5 / Phi 2.0 Rust/WASM</title>
5
- </head>
6
- <body></body>
7
- </html>
8
-
9
  <!DOCTYPE html>
10
  <html>
11
- <head>
 
 
 
 
 
 
12
  <meta charset="UTF-8" />
13
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
14
- <link
15
- rel="stylesheet"
16
- href="https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/styles/default.min.css"
17
- />
18
  <style>
19
- @import url("https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@200;300;400&family=Source+Sans+3:wght@100;200;300;400;500;600;700;800;900&display=swap");
20
- html,
21
- body {
22
- font-family: "Source Sans 3", sans-serif;
23
- }
24
- code,
25
- output,
26
- select,
27
- pre {
28
- font-family: "Source Code Pro", monospace;
29
- }
 
 
30
  </style>
31
  <style type="text/tailwindcss">
32
- .link {
33
- @apply underline hover:text-blue-500 hover:no-underline;
34
- }
35
- </style>
36
  <script src="https://cdn.tailwindcss.com"></script>
37
  <script type="module">
38
- import snarkdown from "https://cdn.skypack.dev/snarkdown";
39
- import hljs from "https://cdn.skypack.dev/highlight.js";
40
- // models base url
41
- const MODELS = {
42
- phi_1_5_q4k: {
43
- base_url:
44
- "https://huggingface.co/lmz/candle-quantized-phi/resolve/main/",
45
- model: "model-q4k.gguf",
46
- tokenizer: "tokenizer.json",
47
- config: "phi-1_5.json",
48
- quantized: true,
49
- seq_len: 2048,
50
- size: "800 MB",
51
- },
52
- phi_1_5_q80: {
53
- base_url:
54
- "https://huggingface.co/lmz/candle-quantized-phi/resolve/main/",
55
- model: "model-q80.gguf",
56
- tokenizer: "tokenizer.json",
57
- config: "phi-1_5.json",
58
- quantized: true,
59
- seq_len: 2048,
60
- size: "1.51 GB",
61
- },
62
- phi_2_0_q4k: {
63
- base_url:
64
- "https://huggingface.co/radames/phi-2-quantized/resolve/main/",
65
- model: [
66
- "model-v2-q4k.gguf_aa.part",
67
- "model-v2-q4k.gguf_ab.part",
68
- "model-v2-q4k.gguf_ac.part",
69
- ],
70
- tokenizer: "tokenizer.json",
71
- config: "config.json",
72
- quantized: true,
73
- seq_len: 2048,
74
- size: "1.57GB",
75
- },
76
- puffin_phi_v2_q4k: {
77
- base_url:
78
- "https://huggingface.co/lmz/candle-quantized-phi/resolve/main/",
79
- model: "model-puffin-phi-v2-q4k.gguf",
80
- tokenizer: "tokenizer-puffin-phi-v2.json",
81
- config: "puffin-phi-v2.json",
82
- quantized: true,
83
- seq_len: 2048,
84
- size: "798 MB",
85
- },
86
- puffin_phi_v2_q80: {
87
- base_url:
88
- "https://huggingface.co/lmz/candle-quantized-phi/resolve/main/",
89
- model: "model-puffin-phi-v2-q80.gguf",
90
- tokenizer: "tokenizer-puffin-phi-v2.json",
91
- config: "puffin-phi-v2.json",
92
- quantized: true,
93
- seq_len: 2048,
94
- size: "1.50 GB",
95
- },
96
- };
97
-
98
- const TEMPLATES = [
99
- {
100
- title: "Simple prompt",
101
- prompt: `Sebastien is in London today, it’s the middle of July yet it’s raining, so Sebastien is feeling gloomy. He`,
102
- },
103
- {
104
- title: "Think step by step",
105
- prompt: `Suppose Alice originally had 3 apples, then Bob gave Alice 7 apples, then Alice gave Cook 5 apples, and then Tim gave Alice 3x the amount of apples Alice had. How many apples does Alice have now?
106
- Let’s think step by step.`,
107
- },
108
- {
109
- title: "Explaing a code snippet",
110
- prompt: `What does this script do?
111
- \`\`\`python
112
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
113
- s.bind(('', 0))
114
- s.listen(1)
115
- conn, addr = s.accept()
116
- print('Connected by', addr)
117
- return conn.getsockname()[1]
118
- \`\`\`
119
- Let’s think step by step.`,
120
- },
121
- {
122
- title: "Question answering",
123
- prompt: `Instruct: What is the capital of France?
124
- Output:`,
125
- },
126
- {
127
- title: "Chat mode",
128
- prompt: `Alice: Can you tell me how to create a python application to go through all the files
129
- in one directory where the file’s name DOES NOT end with '.json'?
130
- Bob:`,
131
- },
132
- {
133
- title: "Python code completion",
134
- prompt: `"""write a python function called batch(function, list) which call function(x) for x in
135
- list in parallel"""
136
- Solution:`,
137
- },
138
- {
139
- title: "Python Sample",
140
- prompt: `"""Can you make sure those histograms appear side by side on the same plot:
141
- \`\`\`python
142
- plt.hist(intreps_retrained[0][1].view(64,-1).norm(dim=1).detach().cpu().numpy(), bins = 20)
143
- plt.hist(intreps_pretrained[0][1].view(64,-1).norm(dim=1).detach().cpu().numpy(), bins = 20)
144
- \`\`\`
145
- """`,
146
- },
147
- {
148
- title: "Write a Twitter post",
149
- prompt: `Write a twitter post for the discovery of gravitational wave.
150
- Twitter Post:`,
151
- },
152
- {
153
- title: "Write a review",
154
- prompt: `Write a polite review complaining that the video game 'Random Game' was too badly optimized and it burned my laptop.
155
- Very polite review:`,
156
- },
157
- ];
158
- const phiWorker = new Worker("./phiWorker.js", {
159
- type: "module",
160
- });
161
- async function generateSequence(controller) {
162
- const getValue = (id) => document.querySelector(`#${id}`).value;
163
- const modelID = getValue("model");
164
- const model = MODELS[modelID];
165
- const weightsURL =
166
- model.model instanceof Array
167
- ? model.model.map((m) => model.base_url + m)
168
- : model.base_url + model.model;
169
- const tokenizerURL = model.base_url + model.tokenizer;
170
- const configURL = model.base_url + model.config;
171
-
172
- const prompt = getValue("prompt").trim();
173
- const temperature = getValue("temperature");
174
- const topP = getValue("top-p");
175
- const repeatPenalty = getValue("repeat_penalty");
176
- const seed = getValue("seed");
177
- const maxSeqLen = getValue("max-seq");
178
 
179
- function updateStatus(data) {
180
- const outStatus = document.querySelector("#output-status");
181
- const outGen = document.querySelector("#output-generation");
182
- const outCounter = document.querySelector("#output-counter");
 
 
 
 
 
 
 
 
 
183
 
184
- switch (data.status) {
185
- case "loading":
186
- outStatus.hidden = false;
187
- outStatus.innerHTML = data.message.replaceAll('\n', '<br>\n');
188
- outGen.hidden = true;
189
- outCounter.hidden = true;
190
- break;
191
- case "generating":
192
- const { message, prompt, sentence, tokensSec, totalTime } = data;
193
- outStatus.hidden = true;
194
- outCounter.hidden = false;
195
- outGen.hidden = false;
196
- outGen.innerHTML = snarkdown(prompt + sentence);
197
- outCounter.innerHTML = `${(totalTime / 1000).toFixed(
198
- 2
199
- )}s (${tokensSec.toFixed(2)} tok/s)`;
200
- hljs.highlightAll();
201
- break;
202
- case "complete":
203
- outStatus.hidden = true;
204
- outGen.hidden = false;
205
- break;
206
- }
207
- }
208
 
209
- return new Promise((resolve, reject) => {
210
- phiWorker.postMessage({
211
- weightsURL,
212
- modelID,
213
- tokenizerURL,
214
- configURL,
215
- quantized: model.quantized,
216
- prompt,
217
- temp: temperature,
218
- top_p: topP,
219
- repeatPenalty,
220
- seed: seed,
221
- maxSeqLen,
222
- command: "start",
223
- });
224
 
225
- const handleAbort = () => {
226
- phiWorker.postMessage({ command: "abort" });
227
- };
228
- const handleMessage = (event) => {
229
- const { status, error, message, prompt, sentence } = event.data;
230
- if (status) updateStatus(event.data);
231
- if (error) {
232
- phiWorker.removeEventListener("message", handleMessage);
233
- reject(new Error(error));
234
- }
235
- if (status === "aborted") {
236
- phiWorker.removeEventListener("message", handleMessage);
237
- resolve(event.data);
 
 
 
 
 
 
 
 
 
 
238
  }
239
- if (status === "complete") {
240
- phiWorker.removeEventListener("message", handleMessage);
241
- resolve(event.data);
 
 
242
  }
243
- };
244
 
245
- controller.signal.addEventListener("abort", handleAbort);
246
- phiWorker.addEventListener("message", handleMessage);
247
- });
248
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
- const form = document.querySelector("#form");
251
- const prompt = document.querySelector("#prompt");
252
- const clearBtn = document.querySelector("#clear-btn");
253
- const runBtn = document.querySelector("#run");
254
- const modelSelect = document.querySelector("#model");
255
- const promptTemplates = document.querySelector("#prompt-templates");
256
- let runController = new AbortController();
257
- let isRunning = false;
 
 
 
 
 
 
 
 
 
 
 
258
 
259
- document.addEventListener("DOMContentLoaded", () => {
260
- for (const [id, model] of Object.entries(MODELS)) {
261
- const option = document.createElement("option");
262
- option.value = id;
263
- option.innerText = `${id} (${model.size})`;
264
- modelSelect.appendChild(option);
265
- }
266
- const query = new URLSearchParams(window.location.search);
267
- const modelID = query.get("model");
268
- if (modelID) {
269
- modelSelect.value = modelID;
270
- } else {
271
- modelSelect.value = "phi_1_5_q4k";
272
  }
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  for (const [i, { title, prompt }] of TEMPLATES.entries()) {
275
- const div = document.createElement("div");
276
- const input = document.createElement("input");
277
- input.type = "radio";
278
- input.name = "task";
279
- input.id = `templates-${i}`;
280
- input.classList.add("font-light", "cursor-pointer");
281
- input.value = prompt;
282
- const label = document.createElement("label");
283
- label.htmlFor = `templates-${i}`;
284
- label.classList.add("cursor-pointer");
285
- label.innerText = title;
286
- div.appendChild(input);
287
- div.appendChild(label);
288
- promptTemplates.appendChild(div);
289
  }
290
- });
291
 
292
- promptTemplates.addEventListener("change", (e) => {
293
- const template = e.target.value;
294
- prompt.value = template;
295
- prompt.style.height = "auto";
296
- prompt.style.height = prompt.scrollHeight + "px";
297
- runBtn.disabled = false;
298
- clearBtn.classList.remove("invisible");
299
- });
300
- modelSelect.addEventListener("change", (e) => {
301
- const query = new URLSearchParams(window.location.search);
302
- query.set("model", e.target.value);
303
- window.history.replaceState(
304
- {},
305
- "",
306
- `${window.location.pathname}?${query}`
307
- );
308
- window.parent.postMessage({ queryString: "?" + query }, "*");
309
- const model = MODELS[e.target.value];
310
- document.querySelector("#max-seq").max = model.seq_len;
311
- document.querySelector("#max-seq").nextElementSibling.value = 200;
312
- });
313
 
314
- form.addEventListener("submit", async (e) => {
315
- e.preventDefault();
316
- if (isRunning) {
317
- stopRunning();
318
- } else {
319
- startRunning();
320
- await generateSequence(runController);
321
- stopRunning();
322
- }
323
- });
324
 
325
- function startRunning() {
326
- isRunning = true;
327
- runBtn.textContent = "Stop";
328
- }
329
 
330
- function stopRunning() {
331
- runController.abort();
332
- runController = new AbortController();
333
- runBtn.textContent = "Run";
334
- isRunning = false;
335
- }
336
- clearBtn.addEventListener("click", (e) => {
337
- e.preventDefault();
338
- prompt.value = "";
339
- clearBtn.classList.add("invisible");
340
- runBtn.disabled = true;
341
- stopRunning();
342
- });
343
- prompt.addEventListener("input", (e) => {
344
- runBtn.disabled = false;
345
- if (e.target.value.length > 0) {
346
- clearBtn.classList.remove("invisible");
347
- } else {
348
- clearBtn.classList.add("invisible");
349
  }
350
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  </script>
352
- </head>
353
- <body class="container max-w-4xl mx-auto p-4 text-gray-800">
 
354
  <main class="grid grid-cols-1 gap-8 relative">
355
- <span class="absolute text-5xl -ml-[1em]"> 🕯️ </span>
356
- <div>
357
- <h1 class="text-5xl font-bold">Candle Phi 1.5 / Phi 2.0</h1>
358
- <h2 class="text-2xl font-bold">Rust/WASM Demo</h2>
359
- <p class="max-w-lg">
360
- The
361
- <a
362
- href="https://huggingface.co/microsoft/phi-1_5"
363
- class="link"
364
- target="_blank"
365
- >Phi-1.5</a
366
- >
367
- and
368
- <a
369
- href="https://huggingface.co/microsoft/phi-2"
370
- class="link"
371
- target="_blank"
372
- >Phi-2</a
373
- >
374
- models achieve state-of-the-art performance with only 1.3 billion and
375
- 2.7 billion parameters, compared to larger models with up to 13
376
- billion parameters. Here you can try the quantized versions.
377
- Additional prompt examples are available in the
378
- <a
379
- href="https://arxiv.org/pdf/2309.05463.pdf#page=8"
380
- class="link"
381
- target="_blank"
382
- >
383
- technical report </a
384
- >.
385
- </p>
386
- <p class="max-w-lg">
387
- You can also try
388
- <a
389
- href="https://huggingface.co/teknium/Puffin-Phi-v2"
390
- class="link"
391
- target="_blank"
392
- >Puffin-Phi V2
393
- </a>
394
- quantized version, a fine-tuned version of Phi-1.5 on the
395
- <a
396
- href="https://huggingface.co/datasets/LDJnr/Puffin"
397
- class="link"
398
- target="_blank"
399
- >Puffin dataset
400
- </a>
401
- </p>
402
- </div>
403
- <div>
404
- <p class="text-xs italic max-w-lg">
405
- <b>Note:</b>
406
- When first run, the app will download and cache the model, which could
407
- take a few minutes. The models are <b>~800MB</b> or <b>~1.57GB</b> in
408
- size.
409
- </p>
410
- </div>
411
- <div>
412
- <label for="model" class="font-medium">Models Options: </label>
413
- <select
414
- id="model"
415
- class="border-2 border-gray-500 rounded-md font-light"
416
- ></select>
417
- </div>
418
- <div>
419
  <details>
420
- <summary class="font-medium cursor-pointer">Prompt Templates</summary>
421
- <form
422
- id="prompt-templates"
423
- class="grid grid-cols-1 sm:grid-cols-2 gap-1 my-2"
424
- ></form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  </details>
426
- </div>
427
- <form
428
- id="form"
429
- class="flex text-normal px-1 py-1 border border-gray-700 rounded-md items-center"
430
- >
431
- <input type="submit" hidden />
432
- <textarea
433
- type="text"
434
- id="prompt"
435
- class="font-light text-lg w-full px-3 py-2 mx-1 resize-none outline-none"
436
- oninput="this.style.height = 0;this.style.height = this.scrollHeight + 'px'"
437
- placeholder="Add your prompt here..."
438
- >
439
- Instruct: Write a detailed analogy between mathematics and a lighthouse.
440
- Output:</textarea
441
- >
442
- <button id="clear-btn">
443
- <svg
444
- fill="none"
445
- xmlns="http://www.w3.org/2000/svg"
446
- width="40"
447
- viewBox="0 0 70 40"
448
- >
449
- <path opacity=".5" d="M39 .2v40.2" stroke="#1F2937" />
450
- <path
451
- d="M1.5 11.5 19 29.1m0-17.6L1.5 29.1"
452
- opacity=".5"
453
- stroke="#1F2937"
454
- stroke-width="2"
455
- />
456
- </svg>
457
- </button>
458
- <button
459
- id="run"
460
- class="bg-gray-700 hover:bg-gray-800 text-white font-normal py-2 w-16 rounded disabled:bg-gray-300 disabled:cursor-not-allowed"
461
- >
462
- Run
463
- </button>
464
- </form>
465
- <details>
466
- <summary class="font-medium cursor-pointer">Advanced Options</summary>
467
-
468
- <div class="grid grid-cols-3 max-w-md items-center gap-3 py-3">
469
- <label class="text-sm font-medium" for="max-seq"
470
- >Maximum length
471
- </label>
472
- <input
473
- type="range"
474
- id="max-seq"
475
- name="max-seq"
476
- min="1"
477
- max="2048"
478
- step="1"
479
- value="200"
480
- oninput="this.nextElementSibling.value = Number(this.value)"
481
- />
482
- <output
483
- class="text-xs w-[50px] text-center font-light px-1 py-1 border border-gray-700 rounded-md"
484
- >
485
- 200</output
486
- >
487
- <label class="text-sm font-medium" for="temperature"
488
- >Temperature</label
489
- >
490
- <input
491
- type="range"
492
- id="temperature"
493
- name="temperature"
494
- min="0"
495
- max="2"
496
- step="0.01"
497
- value="0.00"
498
- oninput="this.nextElementSibling.value = Number(this.value).toFixed(2)"
499
- />
500
- <output
501
- class="text-xs w-[50px] text-center font-light px-1 py-1 border border-gray-700 rounded-md"
502
- >
503
- 0.00</output
504
- >
505
- <label class="text-sm font-medium" for="top-p">Top-p</label>
506
- <input
507
- type="range"
508
- id="top-p"
509
- name="top-p"
510
- min="0"
511
- max="1"
512
- step="0.01"
513
- value="1.00"
514
- oninput="this.nextElementSibling.value = Number(this.value).toFixed(2)"
515
- />
516
- <output
517
- class="text-xs w-[50px] text-center font-light px-1 py-1 border border-gray-700 rounded-md"
518
- >
519
- 1.00</output
520
- >
521
-
522
- <label class="text-sm font-medium" for="repeat_penalty"
523
- >Repeat Penalty</label
524
- >
525
-
526
- <input
527
- type="range"
528
- id="repeat_penalty"
529
- name="repeat_penalty"
530
- min="1"
531
- max="2"
532
- step="0.01"
533
- value="1.10"
534
- oninput="this.nextElementSibling.value = Number(this.value).toFixed(2)"
535
- />
536
- <output
537
- class="text-xs w-[50px] text-center font-light px-1 py-1 border border-gray-700 rounded-md"
538
- >1.10</output
539
- >
540
- <label class="text-sm font-medium" for="seed">Seed</label>
541
- <input
542
- type="number"
543
- id="seed"
544
- name="seed"
545
- value="299792458"
546
- class="font-light border border-gray-700 text-right rounded-md p-2"
547
- />
548
- <button
549
- id="run"
550
- onclick="document.querySelector('#seed').value = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)"
551
- class="bg-gray-700 hover:bg-gray-800 text-white font-normal py-1 w-[50px] rounded disabled:bg-gray-300 disabled:cursor-not-allowed text-sm"
552
- >
553
- Rand
554
- </button>
555
- </div>
556
- </details>
557
-
558
- <div>
559
- <h3 class="font-medium">Generation:</h3>
560
- <div
561
- class="min-h-[250px] bg-slate-100 text-gray-500 p-4 rounded-md flex flex-col gap-2"
562
- >
563
- <div
564
- id="output-counter"
565
- hidden
566
- class="ml-auto font-semibold grid-rows-1"
567
- ></div>
568
- <p hidden id="output-generation" class="grid-rows-2 text-lg"></p>
569
- <span id="output-status" class="m-auto font-light"
570
- >No output yet</span
571
- >
572
  </div>
573
- </div>
574
  </main>
575
- </body>
576
- </html>
 
 
 
 
 
 
 
 
 
 
1
  <!DOCTYPE html>
2
  <html>
3
+
4
+ <head>
5
+ <meta content="text/htmlcharset=utf-8" http-equiv="Content-Type" />
6
+ <meta name="apple-mobile-web-app-capable" content="yes">
7
+ <link rel="shortcut icon" href="candle.png">
8
+ <link rel="apple-touch-icon" href="candle.png">
9
+ <title>Candle Phi Rust/WASM</title>
10
  <meta charset="UTF-8" />
11
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
12
+ <link rel="stylesheet"
13
+ href="https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/styles/default.min.css" />
 
 
14
  <style>
15
+ @import url("https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@200300400&family=Source+Sans+3:wght@100200300400500600700800900&display=swap");
16
+
17
+ html,
18
+ body {
19
+ font-family: "Source Sans 3", sans-serif;
20
+ }
21
+
22
+ code,
23
+ output,
24
+ select,
25
+ pre {
26
+ font-family: "Source Code Pro", monospace;
27
+ }
28
  </style>
29
  <style type="text/tailwindcss">
30
+ .link { @apply underline hover:text-blue-500 hover:no-underline; }
31
+ </style>
 
 
32
  <script src="https://cdn.tailwindcss.com"></script>
33
  <script type="module">
34
+ import snarkdown from "https://cdn.skypack.dev/snarkdown"
35
+ import hljs from "https://cdn.skypack.dev/highlight.js"
36
+ // models base url
37
+ const MODELS = {
38
+ phi_1_5_q4k: {
39
+ base_url:
40
+ "https://huggingface.co/lmz/candle-quantized-phi/resolve/main/",
41
+ model: "model-q4k.gguf",
42
+ tokenizer: "tokenizer.json",
43
+ config: "phi-1_5.json",
44
+ quantized: true,
45
+ seq_len: 2048,
46
+ size: "800 MB",
47
+ },
48
+ phi_1_5_q80: {
49
+ base_url:
50
+ "https://huggingface.co/lmz/candle-quantized-phi/resolve/main/",
51
+ model: "model-q80.gguf",
52
+ tokenizer: "tokenizer.json",
53
+ config: "phi-1_5.json",
54
+ quantized: true,
55
+ seq_len: 2048,
56
+ size: "1.51 GB",
57
+ },
58
+ phi_2_0_q4k: {
59
+ base_url:
60
+ "https://huggingface.co/radames/phi-2-quantized/resolve/main/",
61
+ model: [
62
+ "model-v2-q4k.gguf_aa.part",
63
+ "model-v2-q4k.gguf_ab.part",
64
+ "model-v2-q4k.gguf_ac.part",
65
+ ],
66
+ tokenizer: "tokenizer.json",
67
+ config: "config.json",
68
+ quantized: true,
69
+ seq_len: 2048,
70
+ size: "1.57GB",
71
+ },
72
+ puffin_phi_v2_q4k: {
73
+ base_url:
74
+ "https://huggingface.co/lmz/candle-quantized-phi/resolve/main/",
75
+ model: "model-puffin-phi-v2-q4k.gguf",
76
+ tokenizer: "tokenizer-puffin-phi-v2.json",
77
+ config: "puffin-phi-v2.json",
78
+ quantized: true,
79
+ seq_len: 2048,
80
+ size: "798 MB",
81
+ },
82
+ puffin_phi_v2_q80: {
83
+ base_url:
84
+ "https://huggingface.co/lmz/candle-quantized-phi/resolve/main/",
85
+ model: "model-puffin-phi-v2-q80.gguf",
86
+ tokenizer: "tokenizer-puffin-phi-v2.json",
87
+ config: "puffin-phi-v2.json",
88
+ quantized: true,
89
+ seq_len: 2048,
90
+ size: "1.50 GB",
91
+ },
92
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
+ const phiWorker = new Worker("./phiWorker.js", {
95
+ type: "module",
96
+ })
97
+ async function generateSequence(prompt, controller) {
98
+ const getValue = (id) => document.querySelector(`#${id}`).value
99
+ const modelID = getValue("model")
100
+ const model = MODELS[modelID]
101
+ const weightsURL =
102
+ model.model instanceof Array
103
+ ? model.model.map((m) => model.base_url + m)
104
+ : model.base_url + model.model
105
+ const tokenizerURL = model.base_url + model.tokenizer
106
+ const configURL = model.base_url + model.config
107
 
108
+ // const prompt = getValue("prompt").trim()
109
+ const temperature = getValue("temperature")
110
+ const topP = getValue("top-p")
111
+ const repeatPenalty = getValue("repeat_penalty")
112
+ const seed = getValue("seed")
113
+ const maxSeqLen = getValue("max-seq")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
+ function updateStatus(data) {
116
+ const outStatus = document.querySelector("#output-status")
117
+ const outGen = document.querySelector("#output-generation")
118
+ const outCounter = document.querySelector("#output-counter")
 
 
 
 
 
 
 
 
 
 
 
119
 
120
+ switch (data.status) {
121
+ case "loading":
122
+ outStatus.hidden = false
123
+ outStatus.innerHTML = data.message.replaceAll("\n", "<br>\n")
124
+ outGen.hidden = true
125
+ outCounter.hidden = true
126
+ break
127
+ case "generating":
128
+ const { message, prompt, sentence, tokensSec, totalTime } = data
129
+ outStatus.hidden = true
130
+ outCounter.hidden = false
131
+ outGen.hidden = false
132
+ outGen.innerHTML = snarkdown(prompt + sentence).replaceAll('\n', '<br>\n')
133
+ outCounter.innerHTML = `${(totalTime / 1000).toFixed(
134
+ 2
135
+ )}s (${tokensSec.toFixed(2)} tok/s)`
136
+ hljs.highlightAll()
137
+ break
138
+ case "complete":
139
+ outStatus.hidden = true
140
+ outGen.hidden = false
141
+ break
142
+ }
143
  }
144
+
145
+ function decodeHtml(html) {
146
+ var txt = document.createElement('textarea')
147
+ txt.innerHTML = html
148
+ return txt.value
149
  }
 
150
 
151
+ return new Promise((resolve, reject) => {
152
+ let TEXT = document.querySelector('textarea#terminate')?.innerText
153
+ if (TEXT === '') TEXT = decodeHtml(document.querySelector('textarea#terminate')?.innerHTML)
154
+ phiWorker.postMessage({
155
+ weightsURL,
156
+ modelID,
157
+ tokenizerURL,
158
+ configURL,
159
+ quantized: model.quantized,
160
+ prompt,
161
+ temp: temperature,
162
+ top_p: topP,
163
+ repeatPenalty,
164
+ seed: seed,
165
+ maxSeqLen,
166
+ command: "start",
167
+ stuff: TEXT.split(',').map(e => e.trim())
168
+ })
169
 
170
+ const handleAbort = () => {
171
+ phiWorker.postMessage({ command: "abort" })
172
+ }
173
+ const handleMessage = (event) => {
174
+ const { status, error, message, prompt, sentence } = event.data
175
+ if (status) updateStatus(event.data)
176
+ if (error) {
177
+ phiWorker.removeEventListener("message", handleMessage)
178
+ reject(new Error(error))
179
+ }
180
+ if (status === "aborted") {
181
+ phiWorker.removeEventListener("message", handleMessage)
182
+ resolve(event.data)
183
+ }
184
+ if (status === "complete") {
185
+ phiWorker.removeEventListener("message", handleMessage)
186
+ resolve(event.data)
187
+ }
188
+ }
189
 
190
+ controller.signal.addEventListener("abort", handleAbort)
191
+ phiWorker.addEventListener("message", handleMessage)
192
+ })
 
 
 
 
 
 
 
 
 
 
193
  }
194
 
195
+ const form = document.querySelector("#form")
196
+ const prompt = document.querySelector("#prompt")
197
+ const clearBtn = document.querySelector("#clear-btn")
198
+ const runBtn = document.querySelector("#run")
199
+ const modelSelect = document.querySelector("#model")
200
+ let runController = new AbortController()
201
+ let isRunning = false
202
+
203
+ document.addEventListener("DOMContentLoaded", () => {
204
+ for (const [id, model] of Object.entries(MODELS)) {
205
+ const option = document.createElement("option")
206
+ option.value = id
207
+ option.innerText = `${id} (${model.size})`
208
+ modelSelect.appendChild(option)
209
+ }
210
+ const query = new URLSearchParams(window.location.search)
211
+ const modelID = query.get("model")
212
+ if (modelID) {
213
+ modelSelect.value = modelID
214
+ } else {
215
+ modelSelect.value = "phi_1_5_q4k"
216
+ }
217
+ })
218
+
219
+ const TEMPLATES = { entries: () => [] }
220
+
221
  for (const [i, { title, prompt }] of TEMPLATES.entries()) {
222
+ const div = document.createElement("div")
223
+ const input = document.createElement("input")
224
+ input.type = "radio"
225
+ input.name = "task"
226
+ input.id = `templates-${i}`
227
+ input.classList.add("font-light", "cursor-pointer")
228
+ input.value = prompt
229
+ const label = document.createElement("label")
230
+ label.htmlFor = `templates-${i}`
231
+ label.classList.add("cursor-pointer")
232
+ label.innerText = title
233
+ div.appendChild(input)
234
+ div.appendChild(label)
235
+ promptTemplates.appendChild(div)
236
  }
 
237
 
238
+ modelSelect.addEventListener("change", (e) => {
239
+ const query = new URLSearchParams(window.location.search)
240
+ query.set("model", e.target.value)
241
+ window.history.replaceState(
242
+ {},
243
+ "",
244
+ `${window.location.pathname}?${query}`
245
+ )
246
+ window.parent.postMessage({ queryString: "?" + query }, "*")
247
+ const model = MODELS[e.target.value]
248
+ document.querySelector("#max-seq").max = model.seq_len
249
+ document.querySelector("#max-seq").nextElementSibling.value = 200
250
+ })
 
 
 
 
 
 
 
 
251
 
252
+ form.addEventListener("submit", async (e) => {
253
+ e.preventDefault()
254
+ if (isRunning) {
255
+ stopRunning()
256
+ } else {
257
+ startRunning()
258
+ await generateSequence(document.querySelector(`#prompt`).value, runController)
259
+ stopRunning()
260
+ }
261
+ })
262
 
263
+ function startRunning() {
264
+ isRunning = true
265
+ runBtn.textContent = "Stop"
266
+ }
267
 
268
+ function stopRunning() {
269
+ runController.abort()
270
+ runController = new AbortController()
271
+ runBtn.textContent = "Run"
272
+ isRunning = false
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  }
274
+ clearBtn.addEventListener("click", (e) => {
275
+ e.preventDefault()
276
+ prompt.value = ""
277
+ clearBtn.classList.add("invisible")
278
+ runBtn.disabled = true
279
+ stopRunning()
280
+ })
281
+ prompt.addEventListener("input", (e) => {
282
+ runBtn.disabled = false
283
+ if (e.target.value.length > 0) {
284
+ clearBtn.classList.remove("invisible")
285
+ } else {
286
+ clearBtn.classList.add("invisible")
287
+ }
288
+ })
289
  </script>
290
+ </head>
291
+
292
+ <body class="container max-w-4xl mx-auto p-4 text-gray-800">
293
  <main class="grid grid-cols-1 gap-8 relative">
294
+ <span class="absolute text-5xl -ml-[1em]"> 🕯️ </span>
295
+ <div>
296
+ <h1 class="text-5xl font-bold">Candle Phi 1.5 / Phi 2.0</h1>
297
+ </div>
298
+ <div>
299
+ <p class="text-m max-w-lg">
300
+ <b>Note:</b>
301
+ When first run, the app will download and cache the model, which could
302
+ take a few minutes. The models are <b>~800MB</b> or <b>~1.57GB</b> in
303
+ size.
304
+ </p>
305
+ </div>
306
+ <div>
307
+ <label for="model" class="font-medium">Models Options: </label>
308
+ <select id="model" class="border-2 border-gray-500 rounded-md font-light"></select>
309
+ </div>
310
+ <form id="form" class="flex text-normal px-1 py-1 border border-gray-700 rounded-md items-center">
311
+ <input type="submit" hidden />
312
+ <textarea type="text" id="prompt" class="font-light text-lg w-full px-3 py-2 mx-1 resize-none outline-none"
313
+ oninput="this.style.height = 0; this.style.height = this.scrollHeight + 'px'"
314
+ placeholder="Add your prompt here..."></textarea>
315
+ <button id="clear-btn">
316
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" width="40" viewBox="0 0 70 40">
317
+ <path opacity=".5" d="M39 .2v40.2" stroke="#1F2937" />
318
+ <path d="M1.5 11.5 19 29.1m0-17.6L1.5 29.1" opacity=".5" stroke="#1F2937" stroke-width="2" />
319
+ </svg>
320
+ </button>
321
+ <button id="run"
322
+ class="bg-gray-700 hover:bg-gray-800 text-white font-normal py-2 w-16 rounded disabled:bg-gray-300 disabled:cursor-not-allowed">
323
+ Run
324
+ </button>
325
+ </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  <details>
327
+ <summary class="font-medium cursor-pointer">Advanced Options</summary>
328
+ <div class="grid grid-cols-3 max-w-md items-center gap-3 py-3">
329
+ <label class="text-sm font-medium" for="max-seq">Maximum length
330
+ </label>
331
+ <input type="range" id="max-seq" name="max-seq" min="1" max="2048" step="1" value="200"
332
+ oninput="this.nextElementSibling.value = Number(this.value)" />
333
+ <output class="text-xs w-[50px] text-center font-light px-1 py-1 border border-gray-700 rounded-md">
334
+ 200</output>
335
+ <label class="text-sm font-medium" for="temperature">Temperature</label>
336
+ <input type="range" id="temperature" name="temperature" min="0" max="2" step="0.01" value="0.50"
337
+ oninput="this.nextElementSibling.value = Number(this.value).toFixed(2)" />
338
+ <output class="text-xs w-[50px] text-center font-light px-1 py-1 border border-gray-700 rounded-md">
339
+ 0.50</output>
340
+ <label class="text-sm font-medium" for="top-p">Top-p</label>
341
+ <input type="range" id="top-p" name="top-p" min="0" max="1" step="0.01" value="1.00"
342
+ oninput="this.nextElementSibling.value = Number(this.value).toFixed(2)" />
343
+ <output class="text-xs w-[50px] text-center font-light px-1 py-1 border border-gray-700 rounded-md">
344
+ 1.00</output>
345
+ <label class="text-sm font-medium" for="repeat_penalty">Repeat Penalty</label>
346
+ <input type="range" id="repeat_penalty" name="repeat_penalty" min="1" max="2" step="0.01" value="1.10"
347
+ oninput="this.nextElementSibling.value = Number(this.value).toFixed(2)" />
348
+ <output
349
+ class="text-xs w-[50px] text-center font-light px-1 py-1 border border-gray-700 rounded-md">1.10</output>
350
+ <label class="text-sm font-medium" for="seed">Seed</label>
351
+ <input type="number" id="seed" name="seed" value="299792458"
352
+ class="font-light border border-gray-700 text-right rounded-md p-2" />
353
+ <button id="run"
354
+ onclick="document.querySelector('#seed').value = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)"
355
+ class="bg-gray-700 hover:bg-gray-800 text-white font-normal py-1 w-[50px] rounded disabled:bg-gray-300 disabled:cursor-not-allowed text-sm">
356
+ Rand
357
+ </button>
358
+ <label hidden class="text-sm font-medium" for="terminate">End tokens</label>
359
+ <textarea hidden type="text" id="terminate"
360
+ class="font-light text-lg w-full px-3 py-2 mx-1 resize-none outline-none"
361
+ style="padding-left: -10px; border: 1px solid black; border-radius: 5px; width: 500px"
362
+ oninput="this.style.height = 0; this.style.height = this.scrollHeight + 'px'"
363
+ placeholder="Add your terminate tokens here, Separated by `, `"><|endoftext|>, <|user|>, <|system|>, <|assistant|></textarea>
364
+ </div>
365
  </details>
366
+ <div>
367
+ <h3 class="font-medium">Generation:</h3>
368
+ <div class="min-h-[250px] bg-slate-100 text-gray-500 p-4 rounded-md flex flex-col gap-2">
369
+ <div id="output-counter" hidden class="ml-auto font-semibold grid-rows-1"></div>
370
+ <p hidden id="output-generation" class="grid-rows-2 text-lg"></p>
371
+ <span id="output-status" class="m-auto font-light">No output yet</span>
372
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  </div>
 
374
  </main>
375
+ </body>
376
+
377
+ </html>