Delete index.html
Browse files- index.html +0 -663
index.html
DELETED
|
@@ -1,663 +0,0 @@
|
|
| 1 |
-
<!doctype html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8" />
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
-
<title>Gemma4 E2B Coding Agent WebGPU</title>
|
| 7 |
-
<style>
|
| 8 |
-
:root {
|
| 9 |
-
color-scheme: light;
|
| 10 |
-
--bg: #f6f3ee;
|
| 11 |
-
--panel: #ffffff;
|
| 12 |
-
--line: #e7dfd4;
|
| 13 |
-
--text: #1f1a16;
|
| 14 |
-
--muted: #6d6256;
|
| 15 |
-
--accent: #b35a2b;
|
| 16 |
-
--accent-soft: #f7eee8;
|
| 17 |
-
--user: #f3ede5;
|
| 18 |
-
--assistant: #fcfaf7;
|
| 19 |
-
--error: #a53f2b;
|
| 20 |
-
--shadow: 0 16px 48px rgba(36, 24, 15, 0.08);
|
| 21 |
-
--radius: 22px;
|
| 22 |
-
--radius-sm: 16px;
|
| 23 |
-
--radius-pill: 999px;
|
| 24 |
-
--sans: "Avenir Next", "Segoe UI", sans-serif;
|
| 25 |
-
--mono: "SFMono-Regular", Consolas, monospace;
|
| 26 |
-
}
|
| 27 |
-
|
| 28 |
-
* {
|
| 29 |
-
box-sizing: border-box;
|
| 30 |
-
}
|
| 31 |
-
|
| 32 |
-
body {
|
| 33 |
-
margin: 0;
|
| 34 |
-
min-height: 100vh;
|
| 35 |
-
font-family: var(--sans);
|
| 36 |
-
background: linear-gradient(180deg, #faf7f2 0%, var(--bg) 100%);
|
| 37 |
-
color: var(--text);
|
| 38 |
-
display: grid;
|
| 39 |
-
place-items: center;
|
| 40 |
-
padding: 20px;
|
| 41 |
-
}
|
| 42 |
-
|
| 43 |
-
.app {
|
| 44 |
-
width: min(760px, 100%);
|
| 45 |
-
height: min(840px, calc(100vh - 40px));
|
| 46 |
-
background: var(--panel);
|
| 47 |
-
border: 1px solid var(--line);
|
| 48 |
-
border-radius: var(--radius);
|
| 49 |
-
box-shadow: var(--shadow);
|
| 50 |
-
display: grid;
|
| 51 |
-
grid-template-rows: auto 1fr auto;
|
| 52 |
-
overflow: hidden;
|
| 53 |
-
}
|
| 54 |
-
|
| 55 |
-
.header {
|
| 56 |
-
padding: 18px 20px;
|
| 57 |
-
border-bottom: 1px solid var(--line);
|
| 58 |
-
display: flex;
|
| 59 |
-
align-items: center;
|
| 60 |
-
justify-content: space-between;
|
| 61 |
-
gap: 16px;
|
| 62 |
-
}
|
| 63 |
-
|
| 64 |
-
.title {
|
| 65 |
-
margin: 0;
|
| 66 |
-
font-size: 1rem;
|
| 67 |
-
font-weight: 700;
|
| 68 |
-
}
|
| 69 |
-
|
| 70 |
-
.subtitle {
|
| 71 |
-
margin: 0;
|
| 72 |
-
color: var(--muted);
|
| 73 |
-
font-size: 0.92rem;
|
| 74 |
-
text-align: right;
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
.spinner {
|
| 78 |
-
width: 18px;
|
| 79 |
-
height: 18px;
|
| 80 |
-
border: 2px solid rgba(179, 90, 43, 0.2);
|
| 81 |
-
border-top-color: var(--accent);
|
| 82 |
-
border-radius: 50%;
|
| 83 |
-
animation: spin 0.8s linear infinite;
|
| 84 |
-
flex: 0 0 auto;
|
| 85 |
-
}
|
| 86 |
-
|
| 87 |
-
.spinner.hidden {
|
| 88 |
-
display: none;
|
| 89 |
-
}
|
| 90 |
-
|
| 91 |
-
.chat {
|
| 92 |
-
padding: 18px;
|
| 93 |
-
overflow: auto;
|
| 94 |
-
background: #fcfbf8;
|
| 95 |
-
display: grid;
|
| 96 |
-
align-content: start;
|
| 97 |
-
gap: 12px;
|
| 98 |
-
position: relative;
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
.message {
|
| 102 |
-
max-width: 85%;
|
| 103 |
-
padding: 14px 16px;
|
| 104 |
-
border-radius: 18px;
|
| 105 |
-
line-height: 1.55;
|
| 106 |
-
white-space: pre-wrap;
|
| 107 |
-
border: 1px solid var(--line);
|
| 108 |
-
}
|
| 109 |
-
|
| 110 |
-
.message.user {
|
| 111 |
-
margin-left: auto;
|
| 112 |
-
background: var(--user);
|
| 113 |
-
}
|
| 114 |
-
|
| 115 |
-
.message.assistant {
|
| 116 |
-
background: var(--assistant);
|
| 117 |
-
}
|
| 118 |
-
|
| 119 |
-
.message.placeholder {
|
| 120 |
-
max-width: 100%;
|
| 121 |
-
color: var(--muted);
|
| 122 |
-
background: #fffdfb;
|
| 123 |
-
text-align: left;
|
| 124 |
-
padding: 18px;
|
| 125 |
-
}
|
| 126 |
-
|
| 127 |
-
.message.placeholder strong {
|
| 128 |
-
display: block;
|
| 129 |
-
margin-bottom: 10px;
|
| 130 |
-
color: var(--text);
|
| 131 |
-
}
|
| 132 |
-
|
| 133 |
-
.examples {
|
| 134 |
-
display: grid;
|
| 135 |
-
gap: 10px;
|
| 136 |
-
}
|
| 137 |
-
|
| 138 |
-
.example {
|
| 139 |
-
width: 100%;
|
| 140 |
-
text-align: left;
|
| 141 |
-
min-width: 0;
|
| 142 |
-
padding: 12px 14px;
|
| 143 |
-
border-radius: 14px;
|
| 144 |
-
border: 1px solid var(--line);
|
| 145 |
-
background: #faf6f1;
|
| 146 |
-
color: var(--text);
|
| 147 |
-
font-weight: 500;
|
| 148 |
-
}
|
| 149 |
-
|
| 150 |
-
.example:hover:not(:disabled) {
|
| 151 |
-
background: var(--accent-soft);
|
| 152 |
-
}
|
| 153 |
-
|
| 154 |
-
.center-state {
|
| 155 |
-
position: absolute;
|
| 156 |
-
inset: 0;
|
| 157 |
-
display: grid;
|
| 158 |
-
place-items: center;
|
| 159 |
-
padding: 24px;
|
| 160 |
-
text-align: center;
|
| 161 |
-
color: var(--muted);
|
| 162 |
-
}
|
| 163 |
-
|
| 164 |
-
.center-card {
|
| 165 |
-
display: grid;
|
| 166 |
-
justify-items: center;
|
| 167 |
-
gap: 14px;
|
| 168 |
-
}
|
| 169 |
-
|
| 170 |
-
.center-card .spinner {
|
| 171 |
-
width: 28px;
|
| 172 |
-
height: 28px;
|
| 173 |
-
border-width: 3px;
|
| 174 |
-
}
|
| 175 |
-
|
| 176 |
-
.composer {
|
| 177 |
-
padding: 16px;
|
| 178 |
-
border-top: 1px solid var(--line);
|
| 179 |
-
background: var(--panel);
|
| 180 |
-
display: grid;
|
| 181 |
-
gap: 12px;
|
| 182 |
-
}
|
| 183 |
-
|
| 184 |
-
.toolbar {
|
| 185 |
-
display: flex;
|
| 186 |
-
align-items: center;
|
| 187 |
-
justify-content: space-between;
|
| 188 |
-
gap: 12px;
|
| 189 |
-
}
|
| 190 |
-
|
| 191 |
-
.metrics {
|
| 192 |
-
display: flex;
|
| 193 |
-
justify-content: flex-end;
|
| 194 |
-
color: var(--muted);
|
| 195 |
-
font-size: 0.82rem;
|
| 196 |
-
font-family: var(--mono);
|
| 197 |
-
}
|
| 198 |
-
|
| 199 |
-
.reset {
|
| 200 |
-
border: 1px solid var(--line);
|
| 201 |
-
background: #fffdfb;
|
| 202 |
-
color: var(--muted);
|
| 203 |
-
padding: 10px 14px;
|
| 204 |
-
min-width: 0;
|
| 205 |
-
}
|
| 206 |
-
|
| 207 |
-
.input-row {
|
| 208 |
-
position: relative;
|
| 209 |
-
}
|
| 210 |
-
|
| 211 |
-
textarea {
|
| 212 |
-
width: 100%;
|
| 213 |
-
min-height: 64px;
|
| 214 |
-
max-height: 180px;
|
| 215 |
-
resize: vertical;
|
| 216 |
-
border: 1px solid var(--line);
|
| 217 |
-
border-radius: var(--radius-sm);
|
| 218 |
-
padding: 16px 110px 16px 16px;
|
| 219 |
-
font: inherit;
|
| 220 |
-
color: var(--text);
|
| 221 |
-
background: #fffdfb;
|
| 222 |
-
outline: none;
|
| 223 |
-
}
|
| 224 |
-
|
| 225 |
-
textarea:focus {
|
| 226 |
-
border-color: #d8b49f;
|
| 227 |
-
box-shadow: 0 0 0 4px rgba(179, 90, 43, 0.08);
|
| 228 |
-
}
|
| 229 |
-
|
| 230 |
-
button {
|
| 231 |
-
appearance: none;
|
| 232 |
-
border: 0;
|
| 233 |
-
border-radius: var(--radius-pill);
|
| 234 |
-
padding: 12px 16px;
|
| 235 |
-
background: var(--accent);
|
| 236 |
-
color: white;
|
| 237 |
-
font: inherit;
|
| 238 |
-
font-weight: 400;
|
| 239 |
-
cursor: pointer;
|
| 240 |
-
min-width: 84px;
|
| 241 |
-
}
|
| 242 |
-
|
| 243 |
-
.send {
|
| 244 |
-
position: absolute;
|
| 245 |
-
right: 10px;
|
| 246 |
-
top: 50%;
|
| 247 |
-
transform: translateY(-50%);
|
| 248 |
-
z-index: 1;
|
| 249 |
-
width: 44px;
|
| 250 |
-
height: 44px;
|
| 251 |
-
min-width: 44px;
|
| 252 |
-
padding: 0;
|
| 253 |
-
display: grid;
|
| 254 |
-
place-items: center;
|
| 255 |
-
border-radius: 14px;
|
| 256 |
-
}
|
| 257 |
-
|
| 258 |
-
.send svg {
|
| 259 |
-
width: 18px;
|
| 260 |
-
height: 18px;
|
| 261 |
-
fill: currentColor;
|
| 262 |
-
}
|
| 263 |
-
|
| 264 |
-
.send.stop {
|
| 265 |
-
background: #8b4a28;
|
| 266 |
-
}
|
| 267 |
-
|
| 268 |
-
button:disabled {
|
| 269 |
-
cursor: not-allowed;
|
| 270 |
-
opacity: 0.6;
|
| 271 |
-
}
|
| 272 |
-
|
| 273 |
-
.error {
|
| 274 |
-
color: var(--error);
|
| 275 |
-
}
|
| 276 |
-
|
| 277 |
-
.cursor {
|
| 278 |
-
display: inline-block;
|
| 279 |
-
width: 0.75ch;
|
| 280 |
-
color: var(--accent);
|
| 281 |
-
animation: blink 1s step-end infinite;
|
| 282 |
-
}
|
| 283 |
-
|
| 284 |
-
@keyframes spin {
|
| 285 |
-
to {
|
| 286 |
-
transform: rotate(360deg);
|
| 287 |
-
}
|
| 288 |
-
}
|
| 289 |
-
|
| 290 |
-
@keyframes blink {
|
| 291 |
-
50% {
|
| 292 |
-
opacity: 0;
|
| 293 |
-
}
|
| 294 |
-
}
|
| 295 |
-
|
| 296 |
-
@media (max-width: 720px) {
|
| 297 |
-
.app {
|
| 298 |
-
height: calc(100vh - 20px);
|
| 299 |
-
}
|
| 300 |
-
|
| 301 |
-
.header,
|
| 302 |
-
.toolbar {
|
| 303 |
-
align-items: start;
|
| 304 |
-
flex-direction: column;
|
| 305 |
-
}
|
| 306 |
-
|
| 307 |
-
.subtitle {
|
| 308 |
-
text-align: left;
|
| 309 |
-
}
|
| 310 |
-
|
| 311 |
-
.message {
|
| 312 |
-
max-width: 92%;
|
| 313 |
-
}
|
| 314 |
-
}
|
| 315 |
-
</style>
|
| 316 |
-
</head>
|
| 317 |
-
<body>
|
| 318 |
-
<main class="app">
|
| 319 |
-
<header class="header">
|
| 320 |
-
<h1 class="title">Gemma4 E2B Coding Agent WebGPU</h1>
|
| 321 |
-
<p class="subtitle">Powered by 🤗 transformers.js v4.0.1</p>
|
| 322 |
-
</header>
|
| 323 |
-
|
| 324 |
-
<section id="chat" class="chat">
|
| 325 |
-
<div id="loadingState" class="center-state">
|
| 326 |
-
<div class="center-card">
|
| 327 |
-
<span class="spinner"></span>
|
| 328 |
-
<div id="statusText">Loading model...</div>
|
| 329 |
-
</div>
|
| 330 |
-
</div>
|
| 331 |
-
</section>
|
| 332 |
-
|
| 333 |
-
<section class="composer">
|
| 334 |
-
<div class="toolbar">
|
| 335 |
-
<button id="resetButton" class="reset" type="button" disabled>
|
| 336 |
-
Reset
|
| 337 |
-
</button>
|
| 338 |
-
<div class="metrics" id="metrics">tokens/sec: -</div>
|
| 339 |
-
</div>
|
| 340 |
-
<div class="input-row">
|
| 341 |
-
<textarea
|
| 342 |
-
id="prompt"
|
| 343 |
-
placeholder="Enter text here"
|
| 344 |
-
disabled
|
| 345 |
-
></textarea>
|
| 346 |
-
<button
|
| 347 |
-
id="sendButton"
|
| 348 |
-
class="send"
|
| 349 |
-
type="button"
|
| 350 |
-
disabled
|
| 351 |
-
aria-label="Send message"
|
| 352 |
-
title="Send message"
|
| 353 |
-
></button>
|
| 354 |
-
</div>
|
| 355 |
-
</section>
|
| 356 |
-
</main>
|
| 357 |
-
|
| 358 |
-
<script type="module">
|
| 359 |
-
import {
|
| 360 |
-
pipeline,
|
| 361 |
-
TextStreamer,
|
| 362 |
-
InterruptableStoppingCriteria,
|
| 363 |
-
} from "/assets/transformers.min.js";
|
| 364 |
-
|
| 365 |
-
const MODEL_ID = "huggingworld/gemma-4-E2B-it-ONNX";
|
| 366 |
-
const DTYPE = "q4f16";
|
| 367 |
-
const REVISION = "main";
|
| 368 |
-
const MAX_NEW_TOKENS = 32768;
|
| 369 |
-
const SYSTEM_PROMPT =
|
| 370 |
-
"<|think|> You are an expert autonomous software engineer and coding agent specializing in Python, JavaScript, and web technologies. Your purpose is to write, debug, and execute code within an E2B secure cloud sandbox environment. **Capabilities:** 1. **Code Execution:** You can execute Python/JS code to verify its functionality instantly. 2. **File Management:** You can create, read, update, and delete files in the sandbox. 3. **Terminal Access:** You can run bash commands (e.g., pip install, npm install, git) inside the sandbox. **Operational Guidelines:** - **Think First:** Before writing code, use the thinking process to analyze the requirement, outline the architectural approach, and plan the file structure. - **Sandboxed Execution:** Treat the E2B sandbox as your primary workspace. If code can be executed, execute it. - **Safety First:** Do not run destructive commands. - **Debugging Loop:** If code execution fails, read the error message, analyze it, and fix the code. - **Output:** For final answers, provide the code within markdown blocks and summarize the actions taken. **E2B Integration:** - Use `execute_python(code)` or `execute_command(cmd)` to interact with the environment. **Persona:** - Efficient, precise, and safety-conscious. - Focus on producing functional, production-ready code. **Current Task:** [Insert User Request Here]<|think|>";
|
| 371 |
-
|
| 372 |
-
const chatEl = document.getElementById("chat");
|
| 373 |
-
const promptEl = document.getElementById("prompt");
|
| 374 |
-
const sendButton = document.getElementById("sendButton");
|
| 375 |
-
const resetButton = document.getElementById("resetButton");
|
| 376 |
-
const statusTextEl = document.getElementById("statusText");
|
| 377 |
-
const metricsEl = document.getElementById("metrics");
|
| 378 |
-
const loadingStateEl = document.getElementById("loadingState");
|
| 379 |
-
const SEND_ICON = `
|
| 380 |
-
<svg viewBox="0 0 24 24" aria-hidden="true">
|
| 381 |
-
<path d="M3.4 20.6 21 13 3.4 5.4l.1 5.8 11 1.8-11 1.8z"></path>
|
| 382 |
-
</svg>
|
| 383 |
-
`;
|
| 384 |
-
const STOP_ICON = `
|
| 385 |
-
<svg viewBox="0 0 24 24" aria-hidden="true">
|
| 386 |
-
<path d="M7 7h10v10H7z"></path>
|
| 387 |
-
</svg>
|
| 388 |
-
`;
|
| 389 |
-
|
| 390 |
-
let generator = null;
|
| 391 |
-
let isGenerating = false;
|
| 392 |
-
let loadFailed = false;
|
| 393 |
-
const stoppingCriteria = new InterruptableStoppingCriteria();
|
| 394 |
-
const conversation = [{ role: "system", content: SYSTEM_PROMPT }];
|
| 395 |
-
const examplePrompts = [
|
| 396 |
-
"Implement a Thread-Safe Skip List data structure in C++17. The implementation must support concurrent insert, search, and delete operations, utilizing std::atomic for managing node pointers rather than just a global mutex. Optimize for lookup speed and provide a brief justification of your chosen MAX_LEVEL based on anticipated data volume (N=10^6).",
|
| 397 |
-
"Create a Node.js-based state machine that manages a mock asynchronous workflow (e.g., handling payment, user creation, and email verification) where each state (e.g., PAYMENT_PENDING, PAYMENT_SUCCESS) is an async function. The machine must handle network timeouts, retries with exponential backoff, and allow for state rollback if a subsequent state fails. Implement it using a strict class-based architecture with TypeScript",
|
| 398 |
-
"Using FastAPI, create an endpoint that accepts a large (5,000+ line) JSON structure of nested employee data. Write a function using Pandas to reframe this data into a relational structure, handling missing values, transforming date formats, and computing a new seniority_score based on years_employed and department_rank. Ensure the code includes unit tests using pytest for edge cases in the data transformation",
|
| 399 |
-
];
|
| 400 |
-
|
| 401 |
-
function setStatus(message, { error = false } = {}) {
|
| 402 |
-
statusTextEl.textContent = message;
|
| 403 |
-
statusTextEl.classList.toggle("error", error);
|
| 404 |
-
}
|
| 405 |
-
|
| 406 |
-
function setMetrics(text) {
|
| 407 |
-
metricsEl.textContent = text;
|
| 408 |
-
}
|
| 409 |
-
|
| 410 |
-
function scrollChatToBottom() {
|
| 411 |
-
chatEl.scrollTop = chatEl.scrollHeight;
|
| 412 |
-
}
|
| 413 |
-
|
| 414 |
-
function removePlaceholder() {
|
| 415 |
-
const placeholder = chatEl.querySelector(".placeholder");
|
| 416 |
-
if (placeholder) {
|
| 417 |
-
placeholder.remove();
|
| 418 |
-
}
|
| 419 |
-
}
|
| 420 |
-
|
| 421 |
-
function clearCenterState() {
|
| 422 |
-
if (loadingStateEl) {
|
| 423 |
-
loadingStateEl.remove();
|
| 424 |
-
}
|
| 425 |
-
}
|
| 426 |
-
|
| 427 |
-
function showExamples() {
|
| 428 |
-
clearCenterState();
|
| 429 |
-
removePlaceholder();
|
| 430 |
-
|
| 431 |
-
if (chatEl.querySelector(".examples")) {
|
| 432 |
-
return;
|
| 433 |
-
}
|
| 434 |
-
|
| 435 |
-
const card = document.createElement("div");
|
| 436 |
-
card.className = "message assistant placeholder";
|
| 437 |
-
card.innerHTML = "<strong>Try one of these or enter your own below</strong>";
|
| 438 |
-
|
| 439 |
-
const list = document.createElement("div");
|
| 440 |
-
list.className = "examples";
|
| 441 |
-
|
| 442 |
-
for (const prompt of examplePrompts) {
|
| 443 |
-
const button = document.createElement("button");
|
| 444 |
-
button.type = "button";
|
| 445 |
-
button.className = "example";
|
| 446 |
-
button.textContent = prompt;
|
| 447 |
-
button.addEventListener("click", () => {
|
| 448 |
-
promptEl.value = prompt;
|
| 449 |
-
sendMessage(prompt);
|
| 450 |
-
});
|
| 451 |
-
list.appendChild(button);
|
| 452 |
-
}
|
| 453 |
-
|
| 454 |
-
card.appendChild(list);
|
| 455 |
-
chatEl.appendChild(card);
|
| 456 |
-
}
|
| 457 |
-
|
| 458 |
-
function addMessage(role, text = "") {
|
| 459 |
-
removePlaceholder();
|
| 460 |
-
const node = document.createElement("div");
|
| 461 |
-
node.className = `message ${role}`;
|
| 462 |
-
node.textContent = text;
|
| 463 |
-
chatEl.appendChild(node);
|
| 464 |
-
scrollChatToBottom();
|
| 465 |
-
return node;
|
| 466 |
-
}
|
| 467 |
-
|
| 468 |
-
function setStreamingMessage(node, text) {
|
| 469 |
-
node.textContent = text;
|
| 470 |
-
const cursor = document.createElement("span");
|
| 471 |
-
cursor.className = "cursor";
|
| 472 |
-
cursor.textContent = "▋";
|
| 473 |
-
node.appendChild(cursor);
|
| 474 |
-
scrollChatToBottom();
|
| 475 |
-
}
|
| 476 |
-
|
| 477 |
-
function updateComposer() {
|
| 478 |
-
const ready = Boolean(generator) && !loadFailed;
|
| 479 |
-
promptEl.disabled = !ready || isGenerating;
|
| 480 |
-
sendButton.disabled = !ready;
|
| 481 |
-
resetButton.disabled = !ready || isGenerating;
|
| 482 |
-
sendButton.classList.toggle("stop", isGenerating);
|
| 483 |
-
sendButton.innerHTML = isGenerating ? STOP_ICON : SEND_ICON;
|
| 484 |
-
sendButton.setAttribute(
|
| 485 |
-
"aria-label",
|
| 486 |
-
isGenerating ? "Stop generation" : "Send message",
|
| 487 |
-
);
|
| 488 |
-
sendButton.setAttribute(
|
| 489 |
-
"title",
|
| 490 |
-
isGenerating ? "Stop generation" : "Send message",
|
| 491 |
-
);
|
| 492 |
-
}
|
| 493 |
-
|
| 494 |
-
function resetChat() {
|
| 495 |
-
if (isGenerating) {
|
| 496 |
-
return;
|
| 497 |
-
}
|
| 498 |
-
|
| 499 |
-
conversation.length = 1;
|
| 500 |
-
chatEl.innerHTML = "";
|
| 501 |
-
showExamples();
|
| 502 |
-
setStatus("Ready");
|
| 503 |
-
setMetrics("tokens/sec: -");
|
| 504 |
-
promptEl.value = "";
|
| 505 |
-
promptEl.focus();
|
| 506 |
-
}
|
| 507 |
-
|
| 508 |
-
async function loadModel() {
|
| 509 |
-
if (!window.isSecureContext) {
|
| 510 |
-
throw new Error(
|
| 511 |
-
"WebGPU requires a secure context like https:// or localhost.",
|
| 512 |
-
);
|
| 513 |
-
}
|
| 514 |
-
|
| 515 |
-
if (!("gpu" in navigator)) {
|
| 516 |
-
throw new Error("This browser does not support WebGPU.");
|
| 517 |
-
}
|
| 518 |
-
|
| 519 |
-
setStatus("Loading model...");
|
| 520 |
-
setMetrics("tokens/sec: -");
|
| 521 |
-
|
| 522 |
-
const startedAt = performance.now();
|
| 523 |
-
generator = await pipeline("text-generation", MODEL_ID, {
|
| 524 |
-
dtype: DTYPE,
|
| 525 |
-
revision: REVISION,
|
| 526 |
-
device: "webgpu",
|
| 527 |
-
progress_callback: (p) => {
|
| 528 |
-
if (p.status !== "progress_total") return;
|
| 529 |
-
const percent = Math.round(p.progress);
|
| 530 |
-
setStatus(`Downloading model... ${percent}%`);
|
| 531 |
-
},
|
| 532 |
-
});
|
| 533 |
-
|
| 534 |
-
const seconds = ((performance.now() - startedAt) / 1000).toFixed(1);
|
| 535 |
-
setStatus(`Ready in ${seconds}s`);
|
| 536 |
-
setMetrics("tokens/sec: -");
|
| 537 |
-
showExamples();
|
| 538 |
-
updateComposer();
|
| 539 |
-
}
|
| 540 |
-
|
| 541 |
-
async function sendMessage(overrideText) {
|
| 542 |
-
if (isGenerating) {
|
| 543 |
-
stoppingCriteria.interrupt();
|
| 544 |
-
setStatus("Stopping...");
|
| 545 |
-
return;
|
| 546 |
-
}
|
| 547 |
-
|
| 548 |
-
const text = (overrideText ?? promptEl.value).trim();
|
| 549 |
-
if (!text || !generator) {
|
| 550 |
-
return;
|
| 551 |
-
}
|
| 552 |
-
|
| 553 |
-
isGenerating = true;
|
| 554 |
-
updateComposer();
|
| 555 |
-
stoppingCriteria.reset();
|
| 556 |
-
|
| 557 |
-
addMessage("user", text);
|
| 558 |
-
const assistantNode = addMessage("assistant", "");
|
| 559 |
-
promptEl.value = "";
|
| 560 |
-
|
| 561 |
-
conversation.push({ role: "user", content: text });
|
| 562 |
-
|
| 563 |
-
let streamedText = "";
|
| 564 |
-
let tokenCount = 0;
|
| 565 |
-
let decodeStartedAt = null;
|
| 566 |
-
|
| 567 |
-
clearCenterState();
|
| 568 |
-
setStatus("Generating...");
|
| 569 |
-
setMetrics("tokens/sec: -");
|
| 570 |
-
|
| 571 |
-
try {
|
| 572 |
-
const output = await generator(conversation, {
|
| 573 |
-
max_new_tokens: MAX_NEW_TOKENS,
|
| 574 |
-
do_sample: false,
|
| 575 |
-
stopping_criteria: stoppingCriteria,
|
| 576 |
-
streamer: new TextStreamer(generator.tokenizer, {
|
| 577 |
-
skip_prompt: true,
|
| 578 |
-
skip_special_tokens: true,
|
| 579 |
-
callback_function: (chunk) => {
|
| 580 |
-
streamedText += chunk;
|
| 581 |
-
setStreamingMessage(assistantNode, streamedText);
|
| 582 |
-
},
|
| 583 |
-
token_callback_function: () => {
|
| 584 |
-
const now = performance.now();
|
| 585 |
-
if (decodeStartedAt === null) {
|
| 586 |
-
decodeStartedAt = now;
|
| 587 |
-
}
|
| 588 |
-
tokenCount += 1;
|
| 589 |
-
if (tokenCount < 2) {
|
| 590 |
-
setMetrics("tokens/sec: -");
|
| 591 |
-
return;
|
| 592 |
-
}
|
| 593 |
-
const elapsedSeconds = Math.max(
|
| 594 |
-
(now - decodeStartedAt) / 1000,
|
| 595 |
-
0.001,
|
| 596 |
-
);
|
| 597 |
-
const tokensPerSecond = (
|
| 598 |
-
(tokenCount - 1) /
|
| 599 |
-
elapsedSeconds
|
| 600 |
-
).toFixed(1);
|
| 601 |
-
setMetrics(`tokens/sec: ${tokensPerSecond}`);
|
| 602 |
-
},
|
| 603 |
-
}),
|
| 604 |
-
});
|
| 605 |
-
|
| 606 |
-
const assistantText =
|
| 607 |
-
output?.[0]?.generated_text?.at(-1)?.content?.trim() ||
|
| 608 |
-
streamedText.trim() ||
|
| 609 |
-
"No response generated.";
|
| 610 |
-
assistantNode.textContent = assistantText;
|
| 611 |
-
conversation.push({ role: "assistant", content: assistantText });
|
| 612 |
-
|
| 613 |
-
if (tokenCount >= 2 && decodeStartedAt !== null) {
|
| 614 |
-
const elapsedSeconds = Math.max(
|
| 615 |
-
(performance.now() - decodeStartedAt) / 1000,
|
| 616 |
-
0.001,
|
| 617 |
-
);
|
| 618 |
-
const tokensPerSecond = ((tokenCount - 1) / elapsedSeconds).toFixed(
|
| 619 |
-
1,
|
| 620 |
-
);
|
| 621 |
-
setMetrics(`tokens/sec: ${tokensPerSecond}`);
|
| 622 |
-
} else {
|
| 623 |
-
setMetrics("tokens/sec: -");
|
| 624 |
-
}
|
| 625 |
-
setStatus("Ready");
|
| 626 |
-
} catch (error) {
|
| 627 |
-
console.error(error);
|
| 628 |
-
assistantNode.textContent =
|
| 629 |
-
"Something went wrong while generating a response.";
|
| 630 |
-
assistantNode.classList.add("error");
|
| 631 |
-
setStatus(error.message, { error: true });
|
| 632 |
-
setMetrics("tokens/sec: -");
|
| 633 |
-
} finally {
|
| 634 |
-
isGenerating = false;
|
| 635 |
-
updateComposer();
|
| 636 |
-
if (generator && !loadFailed) {
|
| 637 |
-
promptEl.focus();
|
| 638 |
-
}
|
| 639 |
-
}
|
| 640 |
-
}
|
| 641 |
-
|
| 642 |
-
sendButton.addEventListener("click", sendMessage);
|
| 643 |
-
resetButton.addEventListener("click", resetChat);
|
| 644 |
-
|
| 645 |
-
promptEl.addEventListener("keydown", (event) => {
|
| 646 |
-
if (event.key === "Enter" && !event.shiftKey) {
|
| 647 |
-
event.preventDefault();
|
| 648 |
-
sendMessage();
|
| 649 |
-
}
|
| 650 |
-
});
|
| 651 |
-
|
| 652 |
-
updateComposer();
|
| 653 |
-
|
| 654 |
-
loadModel().catch((error) => {
|
| 655 |
-
console.error(error);
|
| 656 |
-
loadFailed = true;
|
| 657 |
-
setStatus(error.message, { error: true });
|
| 658 |
-
setMetrics("tokens/sec: -");
|
| 659 |
-
updateComposer();
|
| 660 |
-
});
|
| 661 |
-
</script>
|
| 662 |
-
</body>
|
| 663 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|