anycoder-0de491e3 / index.js
akhaliq's picture
akhaliq HF Staff
Upload index.js with huggingface_hub
7754c6d verified
// Main application logic for the chatbot
class ChatbotApp {
constructor() {
this.generator = null;
this.messages = [
{ role: "system", content: "You are a helpful assistant." }
];
this.isGenerating = false;
this.init();
}
async init() {
// Initialize UI elements
this.loadingScreen = document.getElementById('loadingScreen');
this.chatContainer = document.getElementById('chatContainer');
this.messagesArea = document.getElementById('messagesArea');
this.messageInput = document.getElementById('messageInput');
this.sendButton = document.getElementById('sendButton');
this.progressBar = document.getElementById('progressBar');
this.progressText = document.getElementById('progressText');
// Set up event listeners
this.sendButton.addEventListener('click', () => this.sendMessage());
this.messageInput.addEventListener('keydown', (e) => this.handleKeyPress(e));
this.messageInput.addEventListener('input', () => this.adjustTextareaHeight());
// Load the model
await this.loadModel();
}
async loadModel() {
try {
// Import transformers.js
const { pipeline, TextStreamer, env } = await import('https://cdn.jsdelivr.net/npm/@huggingface/transformers');
// Configure environment
env.allowLocalModels = false;
env.allowRemoteModels = true;
// Create a text generation pipeline
this.generator = await pipeline(
"text-generation",
"onnx-community/gemma-3-270m-it-ONNX",
{
dtype: "fp32",
progress_callback: (info) => {
if (info.status === 'downloading') {
const progress = Math.round((info.loaded / info.total) * 100);
this.updateProgress(progress);
}
}
}
);
// Model loaded successfully
this.loadingScreen.classList.add('hidden');
this.chatContainer.classList.remove('hidden');
this.messageInput.focus();
} catch (error) {
console.error('Error loading model:', error);
this.showError('Failed to load model. Please refresh the page and try again.');
}
}
updateProgress(progress) {
this.progressBar.style.width = `${progress}%`;
this.progressText.textContent = `${progress}%`;
}
showError(message) {
this.loadingScreen.innerHTML = `
<div class="text-center">
<div class="inline-flex items-center justify-center w-16 h-16 mb-4 bg-red-100 rounded-full">
<svg class="w-8 h-8 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + "d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<h2 class="text-xl font-semibold text-gray-900 mb-2">Error</h2>
<p class="text-gray-500">${message}</p>
<button onclick="location.reload()" class="mt-4 px-4 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600 transition-colors">
Retry
</button>
</div>
`;
}
handleKeyPress(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.sendMessage();
}
}
adjustTextareaHeight() {
this.messageInput.style.height = 'auto';
this.messageInput.style.height = Math.min(this.messageInput.scrollHeight, 120) + 'px';
}
async sendMessage() {
const message = this.messageInput.value.trim();
if (!message || this.isGenerating) return;
// Add user message to UI
this.addMessageToUI('user', message);
this.messages.push({ role: "user", content: message });
// Clear input
this.messageInput.value = '';
this.adjustTextareaHeight();
// Disable send button
this.isGenerating = true;
this.sendButton.disabled = true;
this.sendButton.innerHTML = `
<svg class="w-5 h-5 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + "d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15">
</path>
</svg>
`;
// Add assistant message placeholder
const assistantMessageId = this.addMessageToUI('assistant', '', true);
try {
// Generate response with streaming
let fullResponse = '';
const streamer = new this.generator.tokenizer.TextStreamer(this.generator.tokenizer, {
skip_prompt: true,
skip_special_tokens: true,
callback_function: (text) => {
fullResponse += text;
this.updateMessage(assistantMessageId, fullResponse);
}
});
const output = await this.generator(this.messages, {
max_new_tokens: 512,
do_sample: false,
streamer: streamer,
});
const response = output[0].generated_text.at(-1).content;
this.messages.push({ role: "assistant", content: response });
} catch (error) {
console.error('Error generating response:', error);
this.updateMessage(assistantMessageId, 'Sorry, I encountered an error. Please try again.');
} finally {
// Re-enable send button
this.isGenerating = false;
this.sendButton.disabled = false;
this.sendButton.innerHTML = `
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path>
</svg>
`;
this.messageInput.focus();
}
}
addMessageToUI(role, content, isStreaming = false) {
const messageId = Date.now();
const messageDiv = document.createElement('div');
messageDiv.className = 'flex items-start space-x-3';
messageDiv.id = `message-${messageId}`;
if (role === 'user') {
messageDiv.innerHTML = `
<div class="w-8 h-8 bg-gray-300 rounded-full flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + "d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
</div>
<div class="bg-gray-100 rounded-lg shadow-sm p-4 max-w-md">
<p class="text-gray-800 whitespace-pre-wrap">${content}</p>
</div>
`;
} else {
messageDiv.innerHTML = `
<div
class="w-8 h-8 bg-gradient-to-br from-purple-500 to-pink-500 rounded-full flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + "d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z">
</path>
</svg>
</div>
<div class="bg-white rounded-lg shadow-sm p-4 max-w-md">
<p class="text-gray-800 whitespace-pre-wrap">${isStreaming ? '<span class="animate-pulse">β–‹</span>' : content}</p>
</div>
`;
}
this.messagesArea.appendChild(messageDiv);
this.messagesArea.scrollTop = this.messagesArea.scrollHeight;
return messageId;
}
updateMessage(messageId, content) {
const messageElement = document.getElementById(`message-${messageId}`);
if (messageElement) {
const contentElement = messageElement.querySelector('.bg-white p, .bg-gray-100 p');
contentElement.innerHTML = content + '<span class="animate-pulse">β–‹</span>';
this.messagesArea.scrollTop = this.messagesArea.scrollHeight;
}
}
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new ChatbotApp();
});