Upload index.html
Browse files- index.html +91 -36
index.html
CHANGED
|
@@ -265,6 +265,18 @@
|
|
| 265 |
|
| 266 |
#model-progress-bar { transition: width 0.4s ease; }
|
| 267 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
.chip {
|
| 269 |
display: inline-flex; align-items: center; gap: 6px;
|
| 270 |
background: #1c2128; border: 1px solid #30363d; border-radius: 20px;
|
|
@@ -297,6 +309,16 @@
|
|
| 297 |
<div class="text-xs" style="color:#8b949e">IT Support Assistant</div>
|
| 298 |
</div>
|
| 299 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
<div id="model-status-wrap" class="ml-auto flex items-center gap-3">
|
| 301 |
<div id="model-progress-wrap" class="w-36 h-1 rounded-full hidden" style="background:#21262d">
|
| 302 |
<div id="model-progress-bar" class="h-1 rounded-full" style="width:0%;background:#388bfd"></div>
|
|
@@ -1469,12 +1491,32 @@ document.querySelectorAll('.chip').forEach(chip => {
|
|
| 1469 |
sendMessage();
|
| 1470 |
});
|
| 1471 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1472 |
</script>
|
| 1473 |
|
| 1474 |
<!-- ββ WebGPU / LLM module βββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
| 1475 |
<script type="module">
|
| 1476 |
import { pipeline, TextStreamer, env }
|
| 1477 |
-
from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3
|
| 1478 |
|
| 1479 |
env.allowLocalModels = false;
|
| 1480 |
if (env.backends?.onnx?.wasm) {
|
|
@@ -1482,7 +1524,6 @@ if (env.backends?.onnx?.wasm) {
|
|
| 1482 |
env.backends.onnx.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/';
|
| 1483 |
}
|
| 1484 |
|
| 1485 |
-
const MODEL_ID = 'HuggingFaceTB/SmolLM2-360M-Instruct';
|
| 1486 |
const statusEl = document.getElementById('model-status');
|
| 1487 |
const progressWrap= document.getElementById('model-progress-wrap');
|
| 1488 |
const progressBar = document.getElementById('model-progress-bar');
|
|
@@ -1499,36 +1540,7 @@ function setStatusUI(text, color, pct) {
|
|
| 1499 |
}
|
| 1500 |
}
|
| 1501 |
|
| 1502 |
-
|
| 1503 |
-
window._llm.status = 'unavailable';
|
| 1504 |
-
setStatusUI('β οΈ Templates only (no WebGPU)', '#d29922', null);
|
| 1505 |
-
setTimeout(() => statusWrap.classList.add('hidden'), 4000);
|
| 1506 |
-
} else {
|
| 1507 |
-
setStatusUI('β³ Loading AIβ¦', '#d29922', 0);
|
| 1508 |
-
progressWrap.classList.remove('hidden');
|
| 1509 |
-
|
| 1510 |
-
(async () => {
|
| 1511 |
-
window._llm.status = 'loading';
|
| 1512 |
-
try {
|
| 1513 |
-
window._llm.generator = await pipeline('text-generation', MODEL_ID, {
|
| 1514 |
-
dtype: 'q4',
|
| 1515 |
-
device: 'webgpu',
|
| 1516 |
-
progress_callback: (p) => {
|
| 1517 |
-
if (p.status !== 'progress') return;
|
| 1518 |
-
setStatusUI(`ⳠLoading AI⦠${Math.round(p.progress ?? 0)}%`, '#d29922', p.progress ?? 0);
|
| 1519 |
-
},
|
| 1520 |
-
});
|
| 1521 |
-
window._llm.status = 'ready';
|
| 1522 |
-
setStatusUI('β
AI model ready', '#3fb950', null);
|
| 1523 |
-
setTimeout(() => statusWrap.classList.add('hidden'), 3000);
|
| 1524 |
-
} catch (err) {
|
| 1525 |
-
console.error('[DeskFlow] Model load failed:', err);
|
| 1526 |
-
window._llm.status = 'error';
|
| 1527 |
-
setStatusUI('β οΈ Templates only (AI unavailable)', '#d29922', null);
|
| 1528 |
-
setTimeout(() => statusWrap.classList.add('hidden'), 5000);
|
| 1529 |
-
}
|
| 1530 |
-
})();
|
| 1531 |
-
|
| 1532 |
window._llmGenerate = async function(systemPrompt, userPrompt, templateFallback, onToken, onDone, onFallback) {
|
| 1533 |
if (!window._llm.generator) { onFallback(templateFallback); return; }
|
| 1534 |
try {
|
|
@@ -1539,10 +1551,7 @@ if (!navigator.gpu) {
|
|
| 1539 |
let accumulated = '';
|
| 1540 |
const streamer = new TextStreamer(window._llm.generator.tokenizer, {
|
| 1541 |
skip_prompt: true,
|
| 1542 |
-
callback_function: (token) => {
|
| 1543 |
-
accumulated += token;
|
| 1544 |
-
onToken(accumulated);
|
| 1545 |
-
},
|
| 1546 |
});
|
| 1547 |
const result = await window._llm.generator(messages, {
|
| 1548 |
max_new_tokens: 512,
|
|
@@ -1558,6 +1567,52 @@ if (!navigator.gpu) {
|
|
| 1558 |
}
|
| 1559 |
};
|
| 1560 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1561 |
</script>
|
| 1562 |
|
| 1563 |
</body>
|
|
|
|
| 265 |
|
| 266 |
#model-progress-bar { transition: width 0.4s ease; }
|
| 267 |
|
| 268 |
+
/* Model switcher pills */
|
| 269 |
+
.model-pill {
|
| 270 |
+
background: #1c2128; border: 1px solid #30363d; border-radius: 20px;
|
| 271 |
+
padding: 3px 11px; font-size: 0.7rem; color: #8b949e; font-family: inherit;
|
| 272 |
+
cursor: pointer; transition: background .15s, border-color .15s, color .15s; white-space: nowrap;
|
| 273 |
+
}
|
| 274 |
+
.model-pill:hover:not(:disabled) { border-color: #58a6ff; color: #58a6ff; }
|
| 275 |
+
.model-pill.active {
|
| 276 |
+
background: rgba(56,139,253,0.12); border-color: #388bfd; color: #58a6ff; font-weight: 500;
|
| 277 |
+
}
|
| 278 |
+
.model-pill:disabled { opacity: 0.45; cursor: not-allowed; }
|
| 279 |
+
|
| 280 |
.chip {
|
| 281 |
display: inline-flex; align-items: center; gap: 6px;
|
| 282 |
background: #1c2128; border: 1px solid #30363d; border-radius: 20px;
|
|
|
|
| 309 |
<div class="text-xs" style="color:#8b949e">IT Support Assistant</div>
|
| 310 |
</div>
|
| 311 |
</div>
|
| 312 |
+
<!-- Model switcher -->
|
| 313 |
+
<div id="model-switcher" class="flex items-center gap-1.5 ml-4">
|
| 314 |
+
<button class="model-pill active"
|
| 315 |
+
data-model-id="HuggingFaceTB/SmolLM2-360M-Instruct"
|
| 316 |
+
data-model-label="SmolLM2-360M">SmolLM2</button>
|
| 317 |
+
<button class="model-pill"
|
| 318 |
+
data-model-id="LiquidAI/LFM2.5-350M-ONNX"
|
| 319 |
+
data-model-label="LFM2.5-350M">LFM2.5</button>
|
| 320 |
+
</div>
|
| 321 |
+
|
| 322 |
<div id="model-status-wrap" class="ml-auto flex items-center gap-3">
|
| 323 |
<div id="model-progress-wrap" class="w-36 h-1 rounded-full hidden" style="background:#21262d">
|
| 324 |
<div id="model-progress-bar" class="h-1 rounded-full" style="width:0%;background:#388bfd"></div>
|
|
|
|
| 1491 |
sendMessage();
|
| 1492 |
});
|
| 1493 |
});
|
| 1494 |
+
|
| 1495 |
+
// ββ Model switcher ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ //
|
| 1496 |
+
document.querySelectorAll('.model-pill').forEach(pill => {
|
| 1497 |
+
pill.addEventListener('click', () => {
|
| 1498 |
+
if (pill.classList.contains('active')) return;
|
| 1499 |
+
if (typeof window._llmSwitchModel !== 'function') return;
|
| 1500 |
+
|
| 1501 |
+
document.querySelectorAll('.model-pill').forEach(p => {
|
| 1502 |
+
p.classList.remove('active');
|
| 1503 |
+
p.disabled = true;
|
| 1504 |
+
});
|
| 1505 |
+
pill.classList.add('active');
|
| 1506 |
+
|
| 1507 |
+
window._llmSwitchModel(
|
| 1508 |
+
pill.dataset.modelId,
|
| 1509 |
+
pill.dataset.modelLabel,
|
| 1510 |
+
() => document.querySelectorAll('.model-pill').forEach(p => { p.disabled = false; }),
|
| 1511 |
+
);
|
| 1512 |
+
});
|
| 1513 |
+
});
|
| 1514 |
</script>
|
| 1515 |
|
| 1516 |
<!-- ββ WebGPU / LLM module βββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
| 1517 |
<script type="module">
|
| 1518 |
import { pipeline, TextStreamer, env }
|
| 1519 |
+
from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3/dist/transformers.min.js';
|
| 1520 |
|
| 1521 |
env.allowLocalModels = false;
|
| 1522 |
if (env.backends?.onnx?.wasm) {
|
|
|
|
| 1524 |
env.backends.onnx.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/';
|
| 1525 |
}
|
| 1526 |
|
|
|
|
| 1527 |
const statusEl = document.getElementById('model-status');
|
| 1528 |
const progressWrap= document.getElementById('model-progress-wrap');
|
| 1529 |
const progressBar = document.getElementById('model-progress-bar');
|
|
|
|
| 1540 |
}
|
| 1541 |
}
|
| 1542 |
|
| 1543 |
+
function bindGenerate() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1544 |
window._llmGenerate = async function(systemPrompt, userPrompt, templateFallback, onToken, onDone, onFallback) {
|
| 1545 |
if (!window._llm.generator) { onFallback(templateFallback); return; }
|
| 1546 |
try {
|
|
|
|
| 1551 |
let accumulated = '';
|
| 1552 |
const streamer = new TextStreamer(window._llm.generator.tokenizer, {
|
| 1553 |
skip_prompt: true,
|
| 1554 |
+
callback_function: (token) => { accumulated += token; onToken(accumulated); },
|
|
|
|
|
|
|
|
|
|
| 1555 |
});
|
| 1556 |
const result = await window._llm.generator(messages, {
|
| 1557 |
max_new_tokens: 512,
|
|
|
|
| 1567 |
}
|
| 1568 |
};
|
| 1569 |
}
|
| 1570 |
+
|
| 1571 |
+
async function loadModel(modelId, label, onComplete) {
|
| 1572 |
+
window._llmGenerate = null;
|
| 1573 |
+
window._llm.status = 'loading';
|
| 1574 |
+
|
| 1575 |
+
if (window._llm.generator) {
|
| 1576 |
+
try { await window._llm.generator.dispose(); } catch {}
|
| 1577 |
+
window._llm.generator = null;
|
| 1578 |
+
}
|
| 1579 |
+
|
| 1580 |
+
statusWrap.classList.remove('hidden');
|
| 1581 |
+
setStatusUI(`β³ Loading ${label}β¦`, '#d29922', 0);
|
| 1582 |
+
progressWrap.classList.remove('hidden');
|
| 1583 |
+
|
| 1584 |
+
try {
|
| 1585 |
+
window._llm.generator = await pipeline('text-generation', modelId, {
|
| 1586 |
+
dtype: 'q4',
|
| 1587 |
+
device: 'webgpu',
|
| 1588 |
+
progress_callback: (p) => {
|
| 1589 |
+
if (p.status !== 'progress') return;
|
| 1590 |
+
setStatusUI(`β³ ${label} ${Math.round(p.progress ?? 0)}%`, '#d29922', p.progress ?? 0);
|
| 1591 |
+
},
|
| 1592 |
+
});
|
| 1593 |
+
window._llm.status = 'ready';
|
| 1594 |
+
bindGenerate();
|
| 1595 |
+
setStatusUI(`β
${label} ready`, '#3fb950', null);
|
| 1596 |
+
setTimeout(() => statusWrap.classList.add('hidden'), 3000);
|
| 1597 |
+
} catch (err) {
|
| 1598 |
+
console.error('[DeskFlow] Model load failed:', err);
|
| 1599 |
+
window._llm.status = 'error';
|
| 1600 |
+
setStatusUI(`β οΈ ${label} unavailable β using templates`, '#d29922', null);
|
| 1601 |
+
setTimeout(() => statusWrap.classList.add('hidden'), 5000);
|
| 1602 |
+
}
|
| 1603 |
+
|
| 1604 |
+
onComplete?.();
|
| 1605 |
+
}
|
| 1606 |
+
|
| 1607 |
+
window._llmSwitchModel = loadModel;
|
| 1608 |
+
|
| 1609 |
+
if (!navigator.gpu) {
|
| 1610 |
+
window._llm.status = 'unavailable';
|
| 1611 |
+
setStatusUI('β οΈ Templates only (no WebGPU)', '#d29922', null);
|
| 1612 |
+
setTimeout(() => statusWrap.classList.add('hidden'), 4000);
|
| 1613 |
+
} else {
|
| 1614 |
+
loadModel('HuggingFaceTB/SmolLM2-360M-Instruct', 'SmolLM2-360M', null);
|
| 1615 |
+
}
|
| 1616 |
</script>
|
| 1617 |
|
| 1618 |
</body>
|