Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>ONNX WebGPU Download & Streaming Text Generation</title> | |
| <script type="module"> | |
| import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.4.1/dist/transformers.min.js'; | |
| // Helper: download a file with progress | |
| async function downloadWithProgress(url, onProgress) { | |
| const response = await fetch(url); | |
| if (!response.ok) throw new Error("Network response was not ok"); | |
| const contentLength = +response.headers.get("Content-Length"); | |
| if (!contentLength) throw new Error("Content-Length header missing"); | |
| const reader = response.body.getReader(); | |
| let received = 0; | |
| const chunks = []; | |
| while(true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| chunks.push(value); | |
| received += value.length; | |
| onProgress(received / contentLength); | |
| } | |
| const blob = new Blob(chunks); | |
| return blob; | |
| } | |
| async function runPrompt(prompt) { | |
| const progressBar = document.getElementById("progress"); | |
| const progressText = document.getElementById("progressText"); | |
| const outputElem = document.getElementById("output"); | |
| // 1️⃣ Download model ONNX file manually | |
| const modelUrl = "https://huggingface.co/onnx-community/gemma-3-1b-it-ONNX/resolve/main/onnx/model_q4.onnx"; // example file | |
| console.log("%cDownloading model...", "color: orange; font-weight: bold;"); | |
| outputElem.textContent = "Downloading model...\n"; | |
| progressBar.style.width = "0%"; | |
| progressText.textContent = "0%"; | |
| await downloadWithProgress(modelUrl, (ratio) => { | |
| const pct = Math.floor(ratio * 100); | |
| progressBar.style.width = pct + "%"; | |
| progressText.textContent = pct + "%"; | |
| }); | |
| console.log("%cModel downloaded!", "color: green; font-weight: bold;"); | |
| outputElem.textContent += "Model downloaded.\n"; | |
| // 2️⃣ Initialize generator using WebGPU | |
| outputElem.textContent += "Initializing pipeline...\n"; | |
| const generator = await pipeline( | |
| "text-generation", | |
| "onnx-community/gemma-3-1b-it-ONNX", | |
| { dtype: "q4", providers: ["webgpu"] } | |
| ); | |
| outputElem.textContent += "Pipeline ready. Generating text...\n"; | |
| // 3️⃣ Stream output | |
| const messages = [ | |
| { role: "system", content: "You are a helpful assistant." }, | |
| { role: "user", content: prompt }, | |
| ]; | |
| let generatedTokens = 0; | |
| const maxTokens = 512; | |
| for await (const token of generator.stream(messages, { max_new_tokens: maxTokens, do_sample: false })) { | |
| outputElem.textContent += token.content; | |
| outputElem.scrollTop = outputElem.scrollHeight; | |
| generatedTokens++; | |
| const progress = Math.min((generatedTokens / maxTokens) * 100, 100); | |
| progressBar.style.width = progress + "%"; | |
| progressText.textContent = Math.floor(progress) + "%"; | |
| } | |
| progressBar.style.width = "100%"; | |
| progressText.textContent = "100%"; | |
| console.log("%cGeneration complete!", "color: blue; font-weight: bold;"); | |
| } | |
| window.onload = () => { | |
| const form = document.getElementById("promptForm"); | |
| form.onsubmit = (e) => { | |
| e.preventDefault(); | |
| const prompt = document.getElementById("promptInput").value; | |
| document.getElementById("output").textContent = ""; | |
| runPrompt(prompt); | |
| } | |
| }; | |
| </script> | |
| <style> | |
| body { font-family: 'Segoe UI', sans-serif; background: #1e1e2f; color: #f0f0f0; display: flex; flex-direction: column; align-items: center; padding: 2rem; } | |
| h1 { color: #ffcc00; margin-bottom: 1rem; } | |
| #promptForm { display: flex; gap: 0.5rem; margin-bottom: 1rem; } | |
| #promptInput { padding: 0.5rem; width: 400px; border-radius: 5px; border: none; font-size: 1rem; } | |
| #generateBtn { padding: 0.5rem 1rem; background: #ffcc00; border: none; border-radius: 5px; font-weight: bold; cursor: pointer; color: #1e1e2f; } | |
| #generateBtn:hover { background: #ffdd33; } | |
| #progressContainer { width: 400px; height: 20px; background: #333; border-radius: 5px; overflow: hidden; margin-bottom: 1rem; position: relative; } | |
| #progress { width: 0%; height: 100%; background: #ffcc00; transition: width 0.05s; } | |
| #progressText { position: absolute; right: 10px; top: 0; bottom: 0; display: flex; align-items: center; justify-content: flex-end; font-size: 0.8rem; color: #1e1e2f; font-weight: bold; } | |
| pre#output { width: 400px; background: #2e2e44; padding: 1rem; border-radius: 8px; white-space: pre-wrap; word-wrap: break-word; min-height: 150px; overflow-y: auto; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>ONNX WebGPU Streaming Text Generation</h1> | |
| <form id="promptForm"> | |
| <input id="promptInput" type="text" placeholder="Type your prompt..." required /> | |
| <button id="generateBtn">Generate</button> | |
| </form> | |
| <div id="progressContainer"> | |
| <div id="progress"></div> | |
| <div id="progressText">0%</div> | |
| </div> | |
| <pre id="output">Enter a prompt and hit Generate...</pre> | |
| </body> | |
| </html> | |