Spaces:
Running
Running
File size: 5,158 Bytes
c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 dd1b723 c480f99 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
import { pipeline, TextStreamer } from 'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';
// DOM Elements
const chatMessages = document.getElementById('chatMessages');
const userInput = document.getElementById('userInput');
const sendButton = document.getElementById('sendButton');
const modelStatus = document.getElementById('modelStatus');
const statusText = document.getElementById('statusText');
const progressFill = document.getElementById('progressFill');
// State
let generator = null;
let conversationHistory = [
{ role: "system", content: "You are a helpful, friendly AI assistant. Provide clear, concise, and accurate responses." }
];
let isGenerating = false;
// Initialize the application
async function init() {
try {
updateStatus('Loading AI model...', 0);
// Create text generation pipeline with progress tracking
generator = await pipeline(
"text-generation",
"onnx-community/Llama-3.2-1B-Instruct-q4f16",
{
dtype: "q4f16",
device: "webgpu",
progress_callback: (progress) => {
if (progress.status === 'progress') {
const percentage = Math.round((progress.loaded / progress.total) * 100);
updateStatus(`Loading model: ${progress.file}`, percentage);
}
}
}
);
updateStatus('Model ready!', 100);
// Enable input after model is loaded
setTimeout(() => {
modelStatus.classList.add('ready');
userInput.disabled = false;
sendButton.disabled = false;
userInput.focus();
}, 500);
} catch (error) {
console.error('Error initializing model:', error);
updateStatus('Error loading model. Please refresh the page.', 0);
statusText.style.color = '#FF3B30';
}
}
// Update status display
function updateStatus(message, progress) {
statusText.textContent = message;
progressFill.style.width = `${progress}%`;
}
// Add message to chat
function addMessage(role, content, isStreaming = false) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${role}`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.textContent = role === 'user' ? 'π€' : 'π€';
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
if (isStreaming) {
contentDiv.innerHTML = '<div class="typing-indicator">
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
</div>';
} else {
contentDiv.innerHTML = formatMessage(content);
}
messageDiv.appendChild(avatar);
messageDiv.appendChild(contentDiv);
// Remove welcome message if it exists
const welcomeMessage = chatMessages.querySelector('.welcome-message');
if (welcomeMessage) {
welcomeMessage.remove();
}
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
return contentDiv;
}
// Format message content
function formatMessage(text) {
// Simple formatting for line breaks
return text
.split('\n')
.map(line => line.trim() ? `<p>${escapeHtml(line)}</p>` : '')
.join('');
}
// Escape HTML to prevent XSS
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Handle message sending
async function sendMessage() {
const message = userInput.value.trim();
if (!message || isGenerating || !generator) return;
// Add user message
addMessage('user', message);
conversationHistory.push({ role: "user", content: message });
// Clear input
userInput.value = '';
userInput.style.height = 'auto';
// Disable input while generating
isGenerating = true;
userInput.disabled = true;
sendButton.disabled = true;
try {
// Add assistant message placeholder with typing indicator
const assistantContentDiv = addMessage('assistant', '', true);
let fullResponse = '';
// Create streamer for real-time output
const streamer = new TextStreamer(generator.tokenizer, {
skip_prompt: true,
skip_special_tokens: true,
callback_function: (text) => {
fullResponse += text;
assistantContentDiv.innerHTML = formatMessage(fullResponse);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
});
// Generate response
const output = await generator(conversationHistory, {
max_new_tokens: 512,
do_sample: true,
temperature: 0.7,
top_p: 0.9,
streamer: streamer
});
// Get the final generated text
const finalResponse = output[0].generated_text.at(-1).content;
// Update conversation history
conversationHistory.push({ role: "assistant", content: finalResponse });
// Ensure final response is displayed
assistantContentDiv.innerHTML = formatMessage(finalResponse);
} catch (error) {
console.error('Error generating response:', error);
addMessage('assistant', 'Sorry, I encountered an error. Please try again.');
} finally {
// Re-enable input
isGenerating = false;
userInput.disabled = false;
sendButton.disabled = false;
userInput.focus();
}
}
// Auto-resize textarea
userInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 120) + 'px';
});
// Handle Enter key
userInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
// Send button click
sendButton.addEventListener('click', sendMessage);
// Initialize app when page loads
init(); |