Spaces:
Running
Running
| import snarkdown from "https://cdn.skypack.dev/snarkdown"; | |
| import hljs from "https://cdn.skypack.dev/highlight.js"; | |
| // models base url | |
| const MODELS = { | |
| moondream2_q4k: { | |
| base_url: | |
| "https://huggingface.co/santiagomed/candle-moondream/resolve/main/", | |
| model: "model-q4_0.gguf", | |
| tokenizer: "tokenizer.json", | |
| quantized: true, | |
| size: "1.51 GB", | |
| }, | |
| }; | |
| const moodreamWorker = new Worker("./moondreamWorker.js", { | |
| type: "module", | |
| }); | |
| async function generateSequence(controller) { | |
| const getValue = (id) => document.querySelector(`#${id}`).value; | |
| const modelID = getValue("model"); | |
| const model = MODELS[modelID]; | |
| const weightsURL = | |
| model.model instanceof Array | |
| ? model.model.map((m) => model.base_url + m) | |
| : model.base_url + model.model; | |
| const tokenizerURL = model.base_url + model.tokenizer; | |
| const prompt = getValue("prompt").trim(); | |
| const temperature = getValue("temperature"); | |
| const topP = getValue("top-p"); | |
| const repeatPenalty = getValue("repeat_penalty"); | |
| const seed = getValue("seed"); | |
| const maxSeqLen = getValue("max-seq"); | |
| if (prompt?.value?.trim() === "") { | |
| return; | |
| } | |
| function updateStatus(data) { | |
| const outStatus = document.querySelector("#output-status"); | |
| const outGen = document.querySelector("#output-generation"); | |
| const outCounter = document.querySelector("#output-counter"); | |
| switch (data.status) { | |
| case "loading": | |
| outStatus.hidden = false; | |
| outStatus.textContent = data.message; | |
| outGen.hidden = true; | |
| outCounter.hidden = true; | |
| break; | |
| case "generating": | |
| const { message, prompt, sentence, tokensSec, totalTime } = data; | |
| outStatus.hidden = true; | |
| outCounter.hidden = false; | |
| outGen.hidden = false; | |
| outGen.innerHTML = snarkdown(prompt + sentence); | |
| outCounter.innerHTML = `${(totalTime / 1000).toFixed( | |
| 2 | |
| )}s (${tokensSec.toFixed(2)} tok/s)`; | |
| hljs.highlightAll(); | |
| break; | |
| case "complete": | |
| outStatus.hidden = true; | |
| outGen.hidden = false; | |
| break; | |
| } | |
| } | |
| return new Promise((resolve, reject) => { | |
| moodreamWorker.postMessage({ | |
| weightsURL, | |
| modelID, | |
| tokenizerURL, | |
| quantized: model.quantized, | |
| imageURL: currentImageURL, | |
| prompt, | |
| temp: temperature, | |
| top_p: topP, | |
| repeatPenalty, | |
| seed: seed, | |
| maxSeqLen, | |
| verbose_prompt: false, | |
| command: "start", | |
| }); | |
| const handleAbort = () => { | |
| moodreamWorker.postMessage({ command: "abort" }); | |
| }; | |
| const handleMessage = (event) => { | |
| const { status, error, message, prompt, sentence } = event.data; | |
| if (status) updateStatus(event.data); | |
| if (error) { | |
| moodreamWorker.removeEventListener("message", handleMessage); | |
| reject(new Error(error)); | |
| } | |
| if (status === "aborted") { | |
| moodreamWorker.removeEventListener("message", handleMessage); | |
| resolve(event.data); | |
| } | |
| if (status === "complete") { | |
| moodreamWorker.removeEventListener("message", handleMessage); | |
| resolve(event.data); | |
| } | |
| }; | |
| controller.signal.addEventListener("abort", handleAbort); | |
| moodreamWorker.addEventListener("message", handleMessage); | |
| }); | |
| } | |
| const form = document.querySelector("#form"); | |
| const prompt = document.querySelector("#prompt"); | |
| const runBtn = document.querySelector("#run"); | |
| const modelSelect = document.querySelector("#model"); | |
| const dropArea = document.querySelector("#drop-area"); | |
| const canvas = document.querySelector("#canvas"); | |
| const ctxCanvas = canvas.getContext("2d"); | |
| const fileUpload = document.querySelector("#file-upload"); | |
| const clearImgBtn = document.querySelector("#clear-img-btn"); | |
| const imagesExamples = document.querySelector("#image-select"); | |
| let currentImageURL = null; | |
| let runController = new AbortController(); | |
| let isRunning = false; | |
| document.addEventListener("DOMContentLoaded", () => { | |
| for (const [id, model] of Object.entries(MODELS)) { | |
| const option = document.createElement("option"); | |
| option.value = id; | |
| option.innerText = `${id} (${model.size})`; | |
| modelSelect.appendChild(option); | |
| } | |
| const query = new URLSearchParams(window.location.search); | |
| const modelID = query.get("model"); | |
| if (modelID) { | |
| modelSelect.value = modelID; | |
| } else { | |
| modelSelect.value = "moondream2_q4k"; | |
| } | |
| }); | |
| imagesExamples.addEventListener("click", (e) => { | |
| // if (isEmbedding || isSegmenting) { | |
| // return; | |
| // } | |
| const target = e.target; | |
| if (target.nodeName === "IMG") { | |
| const href = target.src; | |
| clearImageCanvas(); | |
| currentImageURL = href; | |
| drawImageCanvas(href); | |
| } | |
| }); | |
| modelSelect.addEventListener("change", (e) => { | |
| const query = new URLSearchParams(window.location.search); | |
| query.set("model", e.target.value); | |
| window.history.replaceState({}, "", `${window.location.pathname}?${query}`); | |
| window.parent.postMessage({ queryString: "?" + query }, "*"); | |
| const model = MODELS[e.target.value]; | |
| document.querySelector("#max-seq").max = model.seq_len; | |
| document.querySelector("#max-seq").nextElementSibling.value = 200; | |
| }); | |
| clearImgBtn.addEventListener("click", () => { | |
| clearImageCanvas(); | |
| }); | |
| //add event listener to file input | |
| fileUpload.addEventListener("input", async (e) => { | |
| const target = e.target; | |
| if (target.files.length > 0 && !target.files[0].type.includes("svg")) { | |
| const href = URL.createObjectURL(target.files[0]); | |
| clearImageCanvas(); | |
| await drawImageCanvas(href); | |
| } | |
| }); | |
| // add event listener to drop-area | |
| dropArea.addEventListener("dragenter", (e) => { | |
| e.preventDefault(); | |
| dropArea.classList.add("border-blue-700"); | |
| }); | |
| dropArea.addEventListener("dragleave", (e) => { | |
| e.preventDefault(); | |
| dropArea.classList.remove("border-blue-700"); | |
| }); | |
| dropArea.addEventListener("dragover", (e) => { | |
| e.preventDefault(); | |
| }); | |
| dropArea.addEventListener("drop", async (e) => { | |
| e.preventDefault(); | |
| dropArea.classList.remove("border-blue-700"); | |
| const url = e.dataTransfer.getData("text/uri-list"); | |
| const files = e.dataTransfer.files; | |
| if (files.length > 0) { | |
| const href = URL.createObjectURL(files[0]); | |
| clearImageCanvas(); | |
| await drawImageCanvas(href); | |
| } else if (url) { | |
| clearImageCanvas(); | |
| await drawImageCanvas(url); | |
| } | |
| }); | |
| form.addEventListener("submit", async (e) => { | |
| e.preventDefault(); | |
| if (isRunning) { | |
| stopRunning(); | |
| } else { | |
| startRunning(); | |
| await generateSequence(runController); | |
| stopRunning(); | |
| } | |
| }); | |
| async function drawImageCanvas(imgURL) { | |
| if (!imgURL) { | |
| throw new Error("No image URL provided"); | |
| } | |
| return new Promise((resolve, reject) => { | |
| ctxCanvas.clearRect(0, 0, canvas.width, canvas.height); | |
| ctxCanvas.clearRect(0, 0, canvas.width, canvas.height); | |
| const img = new Image(); | |
| img.crossOrigin = "anonymous"; | |
| img.onload = () => { | |
| canvas.width = img.width; | |
| canvas.height = img.height; | |
| ctxCanvas.drawImage(img, 0, 0); | |
| clearImgBtn.disabled = false; | |
| resolve(img); | |
| }; | |
| img.src = imgURL; | |
| currentImageURL = imgURL; | |
| }); | |
| } | |
| function clearImageCanvas() { | |
| ctxCanvas.clearRect(0, 0, canvas.width, canvas.height); | |
| clearImgBtn.disabled = true; | |
| canvas.parentElement.style.height = "auto"; | |
| currentImageURL = null; | |
| canvas.width = 0; | |
| canvas.height = 0; | |
| } | |
| function startRunning() { | |
| isRunning = true; | |
| runBtn.textContent = "Stop"; | |
| prompt.disabled = true; | |
| } | |
| function stopRunning() { | |
| runController.abort(); | |
| runController = new AbortController(); | |
| runBtn.textContent = "Run"; | |
| isRunning = false; | |
| prompt.disabled = false; | |
| } | |
| prompt.addEventListener("input", (e) => { | |
| runBtn.disabled = false; | |
| }); | |