anycoder-00da736e / index.js
akhaliq's picture
akhaliq HF Staff
Upload index.js with huggingface_hub
d25692c verified
raw
history blame
12.5 kB
// Main application logic for Apple Style Chatbot
import { pipeline, TextStreamer } from 'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';
class AppleChatbot {
constructor() {
this.generator = null;
this.isGenerating = false;
this.messages = [];
this.settings = {
maxTokens: 512,
temperature: 0.7,
streaming: true
};
this.init();
}
async init() {
// Initialize UI elements
this.loadingScreen = document.getElementById('loadingScreen');
this.chatContainer = document.getElementById('chatContainer');
this.inputArea = document.getElementById('inputArea');
this.messagesList = document.getElementById('messagesList');
this.messageInput = document.getElementById('messageInput');
this.sendBtn = document.getElementById('sendBtn');
this.clearBtn = document.getElementById('clearBtn');
this.settingsBtn = document.getElementById('settingsBtn');
this.progressFill = document.getElementById('progressFill');
this.loadingStatus = document.getElementById('loadingStatus');
this.charCount = document.getElementById('charCount');
// Setup event listeners
this.setupEventListeners();
// Load model
await this.loadModel();
}
setupEventListeners() {
// Send button
this.sendBtn.addEventListener('click', () => this.sendMessage());
// Enter key to send (Shift+Enter for new line)
this.messageInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.sendMessage();
}
});
// Character count
this.messageInput.addEventListener('input', (e) => {
const length = e.target.value.length;
this.charCount.textContent = `${length} / 1000`;
this.sendBtn.disabled = length === 0 || this.isGenerating;
// Auto-resize textarea
e.target.style.height = 'auto';
e.target.style.height = Math.min(e.target.scrollHeight, 120) + 'px';
});
// Clear button
this.clearBtn.addEventListener('click', () => this.clearChat());
// Settings
this.settingsBtn.addEventListener('click', () => this.openSettings());
document.getElementById('closeSettings').addEventListener('click', () => this.closeSettings());
document.getElementById('saveSettings').addEventListener('click', () => this.saveSettings());
// Settings inputs
document.getElementById('maxTokens').addEventListener('input', (e) => {
document.getElementById('maxTokensValue').textContent = e.target.value;
});
document.getElementById('temperature').addEventListener('input', (e) => {
document.getElementById('temperatureValue').textContent = e.target.value;
});
// Close modal on backdrop click
document.getElementById('settingsModal').addEventListener('click', (e) => {
if (e.target.id === 'settingsModal') {
this.closeSettings();
}
});
}
async loadModel() {
try {
this.updateProgress(10, 'Downloading model...');
// Initialize the text generation pipeline with progress callback
this.generator = await pipeline(
'text-generation',
'onnx-community/gemma-3-270m-it-ONNX',
{
dtype: 'fp32',
progress_callback: (progress) => {
const percentage = Math.round(progress.progress * 100);
const status = progress.status || 'Loading...';
this.updateProgress(percentage, status);
}
}
);
this.updateProgress(100, 'Model ready!');
// Hide loading screen and show chat
setTimeout(() => {
this.loadingScreen.style.display = 'none';
this.chatContainer.style.display = 'block';
this.inputArea.style.display = 'block';
this.messageInput.focus();
}, 500);
} catch (error) {
console.error('Error loading model:', error);
this.showError('Failed to load AI model. Please refresh the page and try again.');
}
}
updateProgress(progress, status) {
this.progressFill.style.width = `${progress}%`;
this.loadingStatus.textContent = status;
}
async sendMessage() {
const text = this.messageInput.value.trim();
if (!text || this.isGenerating || !this.generator) return;
// Add user message
this.addMessage('user', text);
this.messages.push({ role: 'user', content: text });
// Clear input
this.messageInput.value = '';
this.charCount.textContent = '0 / 1000';
this.sendBtn.disabled = true;
this.messageInput.style.height = 'auto';
// Show typing indicator
const typingMessage = this.showTypingIndicator();
try {
this.isGenerating = true;
// Generate response
const response = await this.generateResponse();
// Remove typing indicator
typingMessage.remove();
// Add assistant response
this.addMessage('assistant', response);
this.messages.push({ role: 'assistant', content: response });
} catch (error) {
console.error('Error generating response:', error);
typingMessage.remove();
this.addMessage('assistant', 'Sorry, I encountered an error while generating a response. Please try again.');
} finally {
this.isGenerating = false;
this.sendBtn.disabled = false;
this.messageInput.focus();
}
}
async generateResponse() {
if (!this.generator) throw new Error('Generator not initialized');
const generationParams = {
max_new_tokens: this.settings.maxTokens,
do_sample: this.settings.temperature > 0,
temperature: this.settings.temperature,
};
// Create prompt from messages
const prompt = this.createPrompt();
// Generate response
const output = await this.generator(prompt, generationParams);
// Extract the assistant's response
const generatedText = output[0].generated_text;
const response = generatedText.replace(prompt, '').trim();
return response || 'I apologize, but I couldn\'t generate a response.';
}
createPrompt() {
// Create a simple prompt from the conversation history
let prompt = '';
for (const msg of this.messages) {
if (msg.role === 'user') {
prompt += `User: ${msg.content}\n`;
} else {
prompt += `Assistant: ${msg.content}\n`;
}
}
prompt += 'Assistant: ';
return prompt;
}
addMessage(role, content) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${role}`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.innerHTML = role === 'user'
? '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>'
: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7V17L12 22L22 17V7L12 2Z" /></svg>';
const messageContent = document.createElement('div');
messageContent.className = 'message-content';
const messageText = document.createElement('div');
messageText.className = 'message-text';
messageText.textContent = content;
const messageTime = document.createElement('div');
messageTime.className = 'message-time';
messageTime.textContent = this.getCurrentTime();
messageContent.appendChild(messageText);
messageContent.appendChild(messageTime);
messageDiv.appendChild(avatar);
messageDiv.appendChild(messageContent);
this.messagesList.appendChild(messageDiv);
// Scroll to bottom
this.chatContainer.scrollTop = this.chatContainer.scrollHeight;
}
showTypingIndicator() {
const messageDiv = document.createElement('div');
messageDiv.className = 'message assistant';
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7V17L12 22L22 17V7L12 2Z" /></svg>';
const messageContent = document.createElement('div');
messageContent.className = 'message-content';
const typingIndicator = document.createElement('div');
typingIndicator.className = 'message-text typing-indicator';
typingIndicator.innerHTML = `
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
`;
const messageTime = document.createElement('div');
messageTime.className = 'message-time';
messageTime.textContent = 'Typing...';
messageContent.appendChild(typingIndicator);
messageContent.appendChild(messageTime);
messageDiv.appendChild(avatar);
messageDiv.appendChild(messageContent);
this.messagesList.appendChild(messageDiv);
this.chatContainer.scrollTop = this.chatContainer.scrollHeight;
return messageDiv;
}
getCurrentTime() {
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}
clearChat() {
if (confirm('Are you sure you want to clear all messages?')) {
// Keep only the welcome message
this.messagesList.innerHTML = `
<div class="message assistant">
<div class="message-avatar">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7V17L12 22L22 17V7L12 2Z" />
</svg>
</div>
<div class="message-content">
<div class="message-text">
Hello! I'm your AI assistant powered by Gemma-3. How can I help you today?
</div>
<div class="message-time">${this.getCurrentTime()}</div>
</div>
</div>
`;
this.messages = [];
this.messageInput.focus();
}
}
openSettings() {
document.getElementById('settingsModal').classList.add('active');
document.getElementById('maxTokens').value = this.settings.maxTokens;
document.getElementById('maxTokensValue').textContent = this.settings.maxTokens;
document.getElementById('temperature').value = this.settings.temperature;
document.getElementById('temperatureValue').textContent = this.settings.temperature;
document.getElementById('streaming').checked = this.settings.streaming;
}
closeSettings() {
document.getElementById('settingsModal').classList.remove('active');
}
saveSettings() {
this.settings.maxTokens = parseInt(document.getElementById('maxTokens').value);
this.settings.temperature = parseFloat(document.getElementById('temperature').value);
this.settings.streaming = document.getElementById('streaming').checked;
this.closeSettings();
}
showError(message) {
this.loadingStatus.textContent = message;
this.loadingStatus.style.color = 'var(--error)';
}
}
// Initialize the chatbot when the page loads
document.addEventListener('DOMContentLoaded', () => {
new AppleChatbot();
});