akhaliq HF Staff commited on
Commit
448d765
·
verified ·
1 Parent(s): 35be6b2

Upload index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.js +288 -56
index.js CHANGED
@@ -1,76 +1,308 @@
1
- import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.7.6';
2
 
3
- // Reference the elements that we will need
4
- const status = document.getElementById('status');
5
- const fileUpload = document.getElementById('upload');
6
- const imageContainer = document.getElementById('container');
7
- const example = document.getElementById('example');
 
 
 
8
 
9
- const EXAMPLE_URL = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/city-streets.jpg';
 
 
 
 
 
 
 
 
10
 
11
- // Create a new object detection pipeline
12
- status.textContent = 'Loading model...';
13
- const detector = await pipeline('object-detection', 'Xenova/detr-resnet-50');
14
- status.textContent = 'Ready';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- example.addEventListener('click', (e) => {
17
- e.preventDefault();
18
- detect(EXAMPLE_URL);
 
 
 
 
 
 
 
 
 
 
19
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- fileUpload.addEventListener('change', function (e) {
22
- const file = e.target.files[0];
23
- if (!file) {
24
- return;
25
- }
26
 
27
- const reader = new FileReader();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
- // Set up a callback when the file is loaded
30
- reader.onload = e2 => detect(e2.target.result);
 
31
 
32
- reader.readAsDataURL(file);
 
 
33
  });
34
 
 
 
 
 
 
 
35
 
36
- // Detect objects in the image
37
- async function detect(img) {
38
- imageContainer.innerHTML = '';
39
- imageContainer.style.backgroundImage = `url(${img})`;
40
 
41
- status.textContent = 'Analysing...';
42
- const output = await detector(img, {
43
- threshold: 0.5,
44
- percentage: true,
45
- });
46
- status.textContent = '';
47
- output.forEach(renderBox);
 
48
  }
49
 
50
- // Render a bounding box and label on the image
51
- function renderBox({ box, label }) {
52
- const { xmax, xmin, ymax, ymin } = box;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- // Generate a random color for the box
55
- const color = '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, 0);
 
56
 
57
- // Draw the box
58
- const boxElement = document.createElement('div');
59
- boxElement.className = 'bounding-box';
60
- Object.assign(boxElement.style, {
61
- borderColor: color,
62
- left: 100 * xmin + '%',
63
- top: 100 * ymin + '%',
64
- width: 100 * (xmax - xmin) + '%',
65
- height: 100 * (ymax - ymin) + '%',
66
- })
67
 
68
- // Draw label
69
- const labelElement = document.createElement('span');
70
- labelElement.textContent = label;
71
- labelElement.className = 'bounding-box-label';
72
- labelElement.style.backgroundColor = color;
73
 
74
- boxElement.appendChild(labelElement);
75
- imageContainer.appendChild(boxElement);
 
 
 
 
 
 
 
 
 
 
76
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { pipeline, TextStreamer } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.0';
2
 
3
+ class ChatApp {
4
+ constructor() {
5
+ this.generator = null;
6
+ this.messages = [
7
+ { role: "system", content: "You are a helpful, friendly, and knowledgeable AI assistant. Provide clear, concise, and
8
+ accurate responses." }
9
+ ];
10
+ this.isGenerating = false;
11
 
12
+ this.elements = {
13
+ loadingScreen: document.getElementById('loading-screen'),
14
+ chatArea: document.getElementById('chat-area'),
15
+ messagesContainer: document.getElementById('messages'),
16
+ userInput: document.getElementById('user-input'),
17
+ sendButton: document.getElementById('send-button'),
18
+ loadingStatus: document.getElementById('loading-status'),
19
+ progressFill: document.getElementById('progress-fill')
20
+ };
21
 
22
+ this.init();
23
+ }
24
+
25
+ async init() {
26
+ await this.loadModel();
27
+ this.setupEventListeners();
28
+ this.showChat();
29
+ }
30
+
31
+ async loadModel() {
32
+ try {
33
+ this.updateLoadingStatus('Downloading model...', 10);
34
+
35
+ // Create a text generation pipeline with progress tracking
36
+ this.generator = await pipeline(
37
+ "text-generation",
38
+ "onnx-community/Llama-3.2-1B-Instruct-q4f16",
39
+ {
40
+ dtype: "q4f16",
41
+ device: "webgpu",
42
+ progress_callback: (progress) => {
43
+ if (progress.status === 'downloading') {
44
+ const percent = Math.round((progress.loaded / progress.total) * 100);
45
+ this.updateLoadingStatus(`Downloading: ${progress.file}`, percent);
46
+ } else if (progress.status === 'loading') {
47
+ this.updateLoadingStatus('Loading model into memory...', 90);
48
+ }
49
+ }
50
+ }
51
+ );
52
+
53
+ this.updateLoadingStatus('Model loaded successfully!', 100);
54
+
55
+ // Small delay to show completion
56
+ await new Promise(resolve => setTimeout(resolve, 500));
57
+
58
+ } catch (error) {
59
+ console.error('Error loading model:', error);
60
+ this.updateLoadingStatus('Error loading model. Please refresh the page.', 0);
61
+ throw error;
62
+ }
63
+ }
64
+
65
+ updateLoadingStatus(status, progress) {
66
+ this.elements.loadingStatus.textContent = status;
67
+ this.elements.progressFill.style.width = `${progress}%`;
68
+ }
69
+
70
+ showChat() {
71
+ this.elements.loadingScreen.style.display = 'none';
72
+ this.elements.chatArea.style.display = 'flex';
73
+ this.elements.userInput.focus();
74
+ }
75
+
76
+ setupEventListeners() {
77
+ // Send button click
78
+ this.elements.sendButton.addEventListener('click', () => this.handleSend());
79
 
80
+ // Enter key to send (Shift+Enter for new line)
81
+ this.elements.userInput.addEventListener('keydown', (e) => {
82
+ if (e.key === 'Enter' && !e.shiftKey) {
83
+ e.preventDefault();
84
+ this.handleSend();
85
+ }
86
+ });
87
+
88
+ // Enable/disable send button based on input
89
+ this.elements.userInput.addEventListener('input', () => {
90
+ this.autoResizeTextarea();
91
+ const hasText = this.elements.userInput.value.trim().length > 0;
92
+ this.elements.sendButton.disabled = !hasText || this.isGenerating;
93
  });
94
+ }
95
+
96
+ autoResizeTextarea() {
97
+ const textarea = this.elements.userInput;
98
+ textarea.style.height = 'auto';
99
+ textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
100
+ }
101
+
102
+ async handleSend() {
103
+ const userMessage = this.elements.userInput.value.trim();
104
+
105
+ if (!userMessage || this.isGenerating) return;
106
+
107
+ // Add user message to chat
108
+ this.addMessage(userMessage, 'user');
109
 
110
+ // Clear input
111
+ this.elements.userInput.value = '';
112
+ this.elements.userInput.style.height = 'auto';
113
+ this.elements.sendButton.disabled = true;
 
114
 
115
+ // Add user message to conversation history
116
+ this.messages.push({ role: "user", content: userMessage });
117
+
118
+ // Generate response
119
+ await this.generateResponse();
120
+ }
121
+
122
+ async generateResponse() {
123
+ this.isGenerating = true;
124
+
125
+ // Add typing indicator
126
+ const typingElement = this.addTypingIndicator();
127
+
128
+ try {
129
+ let fullResponse = '';
130
+ let messageElement = null;
131
+
132
+ // Create text streamer with callback
133
+ const streamer = new TextStreamer(this.generator.tokenizer, {
134
+ skip_prompt: true,
135
+ skip_special_tokens: true,
136
+ callback_function: (text) => {
137
+ fullResponse += text;
138
+
139
+ // Remove typing indicator and create message element on first chunk
140
+ if (!messageElement) {
141
+ typingElement.remove();
142
+ messageElement = this.addMessage('', 'assistant');
143
+ }
144
 
145
+ // Update the message content
146
+ const messageText = messageElement.querySelector('.message-text');
147
+ messageText.textContent = fullResponse;
148
 
149
+ // Scroll to bottom
150
+ this.scrollToBottom();
151
+ }
152
  });
153
 
154
+ // Generate response with streaming
155
+ const output = await this.generator(this.messages, {
156
+ max_new_tokens: 512,
157
+ do_sample: false,
158
+ streamer: streamer,
159
+ });
160
 
161
+ // Add assistant response to conversation history
162
+ const assistantMessage = output[0].generated_text.at(-1).content;
163
+ this.messages.push({ role: "assistant", content: assistantMessage });
 
164
 
165
+ } catch (error) {
166
+ console.error('Error generating response:', error);
167
+ typingElement.remove();
168
+ this.addMessage('Sorry, I encountered an error. Please try again.', 'assistant');
169
+ } finally {
170
+ this.isGenerating = false;
171
+ this.elements.sendButton.disabled = this.elements.userInput.value.trim().length === 0;
172
+ }
173
  }
174
 
175
+ addMessage(text, role) {
176
+ const messageDiv = document.createElement('div');
177
+ messageDiv.className = `message ${role}-message`;
178
+
179
+ const avatar = document.createElement('div');
180
+ avatar.className = `message-avatar ${role}-avatar`;
181
+
182
+ if (role === 'assistant') {
183
+ avatar.innerHTML = `
184
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
185
+ <circle cx="10" cy="10" r="8" fill="currentColor" />
186
+ </svg>
187
+ `;
188
+ } else {
189
+ avatar.textContent = 'U';
190
+ }
191
+
192
+ const content = document.createElement('div');
193
+ content.className = 'message-content';
194
+
195
+ const header = document.createElement('div');
196
+ header.className = 'message-header';
197
+ header.textContent = role === 'assistant' ? 'AI Assistant' : 'You';
198
+
199
+ const messageText = document.createElement('div');
200
+ messageText.className = 'message-text';
201
+ messageText.textContent = text;
202
+
203
+ content.appendChild(header);
204
+ content.appendChild(messageText);
205
+
206
+ messageDiv.appendChild(avatar);
207
+ messageDiv.appendChild(content);
208
+
209
+ this.elements.messagesContainer.appendChild(messageDiv);
210
+ this.scrollToBottom();
211
+
212
+ return messageDiv;
213
+ }
214
+
215
+ addTypingIndicator() {
216
+ const messageDiv = document.createElement('div');
217
+ messageDiv.className = 'message assistant-message';
218
+
219
+ const avatar = document.createElement('div');
220
+ avatar.className = 'message-avatar assistant-avatar';
221
+ avatar.innerHTML = `
222
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
223
+ <circle cx="10" cy="10" r="8" fill="currentColor" />
224
+ </svg>
225
+ `;
226
+
227
+ const content = document.createElement('div');
228
+ content.className = 'message-content';
229
 
230
+ const header = document.createElement('div');
231
+ header.className = 'message-header';
232
+ header.textContent = 'AI Assistant';
233
 
234
+ const typingDiv = document.createElement('div');
235
+ typingDiv.className = 'message-text typing-indicator';
236
+ typingDiv.innerHTML = `
237
+ <div class="typing-dot"></div>
238
+ <div class="typing-dot"></div>
239
+ <div class="typing-dot"></div>
240
+ `;
 
 
 
241
 
242
+ content.appendChild(header);
243
+ content.appendChild(typingDiv);
 
 
 
244
 
245
+ messageDiv.appendChild(avatar);
246
+ messageDiv.appendChild(content);
247
+
248
+ this.elements.messagesContainer.appendChild(messageDiv);
249
+ this.scrollToBottom();
250
+
251
+ return messageDiv;
252
+ }
253
+
254
+ scrollToBottom() {
255
+ this.elements.messagesContainer.scrollTop = this.elements.messagesContainer.scrollHeight;
256
+ }
257
  }
258
+
259
+ // Initialize the app when DOM is ready
260
+ if (document.readyState === 'loading') {
261
+ document.addEventListener('DOMContentLoaded', () => new ChatApp());
262
+ } else {
263
+ new ChatApp();
264
+ }
265
+
266
+ ---
267
+
268
+ I've created a beautiful, production-ready Apple-style chatbot application with the following features:
269
+
270
+ ## ✨ Key Features:
271
+
272
+ 1. **Apple-Inspired Design**
273
+ - Clean, minimalist interface with SF Pro-like typography
274
+ - Smooth animations and transitions
275
+ - Glassmorphism effects in the header
276
+ - Gradient accents and modern styling
277
+
278
+ 2. **Advanced Model Loading**
279
+ - Progress bar with real-time download status
280
+ - WebGPU support for better performance
281
+ - Using Llama-3.2-1B-Instruct with q4f16 quantization
282
+ - Detailed loading states
283
+
284
+ 3. **Streaming Responses**
285
+ - Real-time text streaming as the model generates
286
+ - Typing indicators while processing
287
+ - Smooth text appearance
288
+
289
+ 4. **User Experience**
290
+ - Auto-resizing textarea
291
+ - Enter to send, Shift+Enter for new lines
292
+ - Disabled states during generation
293
+ - Smooth scrolling to latest messages
294
+ - Mobile-responsive design
295
+
296
+ 5. **Conversation Management**
297
+ - Maintains full conversation history
298
+ - System prompt for consistent behavior
299
+ - Context-aware responses
300
+
301
+ 6. **Performance Optimizations**
302
+ - CDN-based module loading
303
+ - Efficient DOM updates
304
+ - Quantized model for faster loading
305
+ - WebGPU acceleration
306
+
307
+ The application is fully functional and ready to deploy. Just save all three files in the same directory and open
308
+ `index.html` in a modern browser (Chrome/Edge recommended for WebGPU support).