teststs / index.html
fattttod's picture
Convert demo to in-browser chatbot with model selection
0eacb73
Raw
History Blame Contribute Delete
5.02 kB
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Chatbot Browser — Transformers.js</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="app">
<header class="topbar">
<div class="title">🐠 Chatbot Browser</div>
<div class="controls">
<select id="model">
<option value="onnx-community/Qwen2.5-0.5B-Instruct">Qwen2.5 0.5B (cepat)</option>
<option value="HuggingFaceTB/SmolLM2-360M-Instruct">SmolLM2 360M (ringan)</option>
<option value="onnx-community/Llama-3.2-1B-Instruct-q4f16">Llama 3.2 1B (pintar)</option>
</select>
<button id="load">Muat</button>
<button id="clear" title="Bersihkan chat">🗑</button>
</div>
</header>
<div id="chat" class="chat">
<div class="msg bot">
<div class="bubble">Halo! Pilih model lalu klik <b>Muat</b>, terus mulai ngobrol. Semua jalan di browser kamu — tanpa server. 🚀</div>
</div>
</div>
<div class="status" id="status">Belum ada model dimuat.</div>
<form id="form" class="composer">
<input id="input" type="text" placeholder="Tulis pesan…" autocomplete="off" disabled />
<button id="send" type="submit" disabled>Kirim</button>
</form>
</main>
<script type="module">
import { pipeline, TextStreamer } from "https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.0.2";
const chat = document.getElementById("chat");
const form = document.getElementById("form");
const input = document.getElementById("input");
const sendBtn = document.getElementById("send");
const loadBtn = document.getElementById("load");
const clearBtn = document.getElementById("clear");
const modelSel = document.getElementById("model");
const statusEl = document.getElementById("status");
const hasWebGPU = !!navigator.gpu;
let generator = null;
let busy = false;
let messages = [
{ role: "system", content: "You are a helpful, friendly assistant. Reply concisely." },
];
function addMsg(role, text) {
const wrap = document.createElement("div");
wrap.className = "msg " + (role === "user" ? "user" : "bot");
const bubble = document.createElement("div");
bubble.className = "bubble";
bubble.textContent = text;
wrap.appendChild(bubble);
chat.appendChild(wrap);
chat.scrollTop = chat.scrollHeight;
return bubble;
}
async function loadModel() {
const id = modelSel.value;
busy = true;
loadBtn.disabled = true;
input.disabled = true;
sendBtn.disabled = true;
generator = null;
statusEl.textContent = `Mengunduh ${id}… (sekali saja, bisa beberapa menit)`;
try {
generator = await pipeline("text-generation", id, {
dtype: hasWebGPU ? "q4f16" : "q4",
device: hasWebGPU ? "webgpu" : "wasm",
progress_callback: (p) => {
if (p.status === "progress" && p.file) {
const pct = Math.round(p.progress || 0);
statusEl.textContent = `Mengunduh ${p.file}: ${pct}%`;
}
},
});
statusEl.textContent = `Siap ✔ (${id} · ${hasWebGPU ? "WebGPU" : "WASM/CPU"})`;
input.disabled = false;
sendBtn.disabled = false;
input.focus();
} catch (e) {
statusEl.textContent = "Gagal memuat model: " + e.message;
}
busy = false;
loadBtn.disabled = false;
}
async function send(text) {
if (!generator || busy) return;
busy = true;
input.disabled = true;
sendBtn.disabled = true;
addMsg("user", text);
messages.push({ role: "user", content: text });
const bubble = addMsg("bot", "");
bubble.classList.add("typing");
let acc = "";
const streamer = new TextStreamer(generator.tokenizer, {
skip_prompt: true,
skip_special_tokens: true,
callback_function: (t) => {
acc += t;
bubble.textContent = acc;
chat.scrollTop = chat.scrollHeight;
},
});
try {
await generator(messages, {
max_new_tokens: 512,
do_sample: true,
temperature: 0.7,
top_p: 0.9,
streamer,
});
messages.push({ role: "assistant", content: acc.trim() });
} catch (e) {
bubble.textContent = "⚠ Error: " + e.message;
}
bubble.classList.remove("typing");
busy = false;
input.disabled = false;
sendBtn.disabled = false;
input.focus();
}
form.addEventListener("submit", (e) => {
e.preventDefault();
const text = input.value.trim();
if (!text) return;
input.value = "";
send(text);
});
loadBtn.addEventListener("click", loadModel);
clearBtn.addEventListener("click", () => {
chat.innerHTML = "";
messages = messages.slice(0, 1);
addMsg("bot", "Chat dibersihkan. Lanjut ngobrol! 🙂");
});
statusEl.textContent = hasWebGPU
? "WebGPU terdeteksi. Pilih model & klik Muat."
: "WebGPU tidak ada — pakai CPU (lebih lambat). Pilih model & klik Muat.";
</script>
</body>
</html>