vikramlingam's picture
Upload folder using huggingface_hub
2229feb verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gemma 3 270M WebGPU Demo</title>
<style>
:root {
--bg-color: #0d1117; /* Github Dim */
--chat-bg: #161b22;
--text-color: #c9d1d9;
--accent: #238636; /* Green */
--user-msg: #1f6feb; /* Blue */
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
margin: 0;
display: flex;
flex-direction: column;
height: 100vh;
}
header {
padding: 1rem;
border-bottom: 1px solid #30363d;
text-align: center;
}
#chat-container {
flex: 1;
padding: 1rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
max-width: 800px;
margin: 0 auto;
width: 100%;
}
.message {
padding: 0.8rem 1.2rem;
border-radius: 8px;
max-width: 80%;
line-height: 1.5;
}
.user {
align-self: flex-end;
background-color: var(--user-msg);
color: white;
}
.assistant {
align-self: flex-start;
background-color: var(--chat-bg);
border: 1px solid #30363d;
}
#input-area {
padding: 1rem;
background-color: var(--chat-bg);
border-top: 1px solid #30363d;
display: flex;
gap: 10px;
justify-content: center;
}
input[type="text"] {
padding: 10px;
border-radius: 6px;
border: 1px solid #30363d;
background-color: var(--bg-color);
color: white;
flex: 1;
max-width: 600px;
}
button {
padding: 10px 20px;
border-radius: 6px;
border: none;
background-color: var(--accent);
color: white;
font-weight: bold;
cursor: pointer;
}
button:disabled {
background-color: #30363d;
cursor: not-allowed;
}
#status {
font-size: 0.8rem;
color: #8b949e;
text-align: center;
margin-top: 5px;
}
</style>
</head>
<body>
<header>
<h2>Gemma 3 270M-it (WebGPU)</h2>
<div id="status">Converting model to engine...</div>
</header>
<div id="chat-container">
<div class="message assistant">
Hello! I am Gemma 3 (270M), running entirely in your browser using WebGPU. How can I help you today?
</div>
</div>
<div id="input-area">
<input type="text" id="user-input" placeholder="Type a message..." disabled>
<button id="send-btn" disabled>Send</button>
</div>
<script type="module">
import { CreateMLCEngine } from "https://esm.run/@mlc-ai/web-llm";
let engine;
const chatContainer = document.getElementById('chat-container');
const userInput = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const statusLabel = document.getElementById('status');
// Configuration for local serving
// NOTE: You must serve the 'dist' folder at http://localhost:8000
// e.g. python3 -m http.server 8000 --cors
const appConfig = {
model_list: [
{
"model": "http://localhost:8000/gemma-3-270m-it-mlc",
"model_id": "gemma-3-270m-it",
"model_lib": "http://localhost:8000/libs/gemma-3-270m-it-webgpu.wasm",
"overrides": {
"context_window_size": 2048 // Lower context for faster load on generic demos
}
}
]
};
async function init() {
try {
statusLabel.textContent = "Loading WebGPU Engine (this may take a moment)...";
engine = await CreateMLCEngine("gemma-3-270m-it", {
appConfig,
initProgressCallback: (report) => {
statusLabel.textContent = report.text;
}
});
statusLabel.textContent = "Ready";
userInput.disabled = false;
sendBtn.disabled = false;
userInput.focus();
} catch (err) {
statusLabel.textContent = "Error: " + err.message;
console.error(err);
appendMessage("assistant", "Error loading model. Make sure you are serving the files at http://localhost:8000 and your browser supports WebGPU.");
}
}
function appendMessage(role, text) {
const msgDiv = document.createElement('div');
msgDiv.className = `message ${role}`;
msgDiv.textContent = text;
chatContainer.appendChild(msgDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
return msgDiv;
}
async function handleSend() {
const text = userInput.value.trim();
if (!text) return;
appendMessage("user", text);
userInput.value = "";
userInput.disabled = true;
sendBtn.disabled = true;
const assistantMsgDiv = appendMessage("assistant", "Thinking...");
try {
const chunks = await engine.chat.completions.create({
messages: [
{ role: "user", content: text }
],
stream: true,
});
let fullResponse = "";
for await (const chunk of chunks) {
const content = chunk.choices[0]?.delta?.content || "";
fullResponse += content;
assistantMsgDiv.textContent = fullResponse;
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// Check usage stats if available
const stats = await engine.runtimeStatsText();
statusLabel.textContent = stats;
} catch (err) {
assistantMsgDiv.textContent += "\n[Error generating response]";
console.error(err);
} finally {
userInput.disabled = false;
sendBtn.disabled = false;
userInput.focus();
}
}
sendBtn.addEventListener('click', handleSend);
userInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') handleSend();
});
init();
</script>
</body>
</html>