akhaliq HF Staff commited on
Commit
878e46a
·
verified ·
1 Parent(s): 5c801cc

Upload index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.js +241 -56
index.js CHANGED
@@ -1,76 +1,261 @@
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";
2
 
3
+ class AppleChatBot {
4
+ constructor() {
5
+ this.generator = null;
6
+ this.isGenerating = false;
7
+ this.messages = [
8
+ { role: "system", content: "You are a helpful assistant. Be concise and friendly in your responses." }
9
+ ];
10
+ this.init();
11
+ }
12
 
13
+ async init() {
14
+ this.setupEventListeners();
15
+ await this.loadModel();
16
+ }
17
 
18
+ setupEventListeners() {
19
+ const messageInput = document.getElementById('messageInput');
20
+ const sendButton = document.getElementById('sendButton');
 
21
 
22
+ // Handle input changes
23
+ messageInput.addEventListener('input', () => {
24
+ this.updateCharCount();
25
+ this.autoResizeTextarea();
26
+ sendButton.disabled = !messageInput.value.trim() || this.isGenerating;
27
  });
28
 
29
+ // Handle Enter key
30
+ messageInput.addEventListener('keydown', (e) => {
31
+ if (e.key === 'Enter' && !e.shiftKey) {
32
+ e.preventDefault();
33
+ this.sendMessage();
34
+ }
35
+ });
36
+
37
+ // Handle send button
38
+ sendButton.addEventListener('click', () => this.sendMessage());
39
+ }
40
+
41
+ updateCharCount() {
42
+ const messageInput = document.getElementById('messageInput');
43
+ const charCount = document.getElementById('charCount');
44
+ charCount.textContent = `${messageInput.value.length} / 2000`;
45
+ }
46
+
47
+ autoResizeTextarea() {
48
+ const textarea = document.getElementById('messageInput');
49
+ textarea.style.height = 'auto';
50
+ textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
51
+ }
52
+
53
+ async loadModel() {
54
+ try {
55
+ // Show loading overlay
56
+ const loadingOverlay = document.getElementById('loadingOverlay');
57
+ const progressFill = document.getElementById('progressFill');
58
+ const progressText = document.getElementById('progressText');
59
+ const modelStatus = document.getElementById('modelStatus');
60
+ const statusDot = modelStatus.querySelector('.status-dot');
61
+ const statusText = modelStatus.querySelector('.status-text');
62
+
63
+ // Update status
64
+ statusText.textContent = 'Loading model...';
65
+ statusDot.className = 'status-dot loading';
66
+
67
+ // Create pipeline with progress callback
68
+ this.generator = await pipeline(
69
+ "text-generation",
70
+ "onnx-community/gemma-3-270m-it-ONNX",
71
+ {
72
+ dtype: "fp32",
73
+ progress_callback: (progress) => {
74
+ const percentage = Math.round(progress.progress * 100);
75
+ progressFill.style.width = `${percentage}%`;
76
+ progressText.textContent = `${percentage}%`;
77
+
78
+ if (progress.status === 'downloading') {
79
+ statusText.textContent = `Downloading... ${percentage}%`;
80
+ } else if (progress.status === 'ready') {
81
+ statusText.textContent = 'Processing model...';
82
+ }
83
+ }
84
+ }
85
+ );
86
+
87
+ // Hide loading overlay
88
+ loadingOverlay.style.opacity = '0';
89
+ setTimeout(() => {
90
+ loadingOverlay.style.display = 'none';
91
+ }, 300);
92
+
93
+ // Update status to ready
94
+ statusText.textContent = 'Ready';
95
+ statusDot.className = 'status-dot ready';
96
+ document.getElementById('sendButton').disabled = false;
97
+
98
+ } catch (error) {
99
+ console.error('Error loading model:', error);
100
+ const loadingOverlay = document.getElementById('loadingOverlay');
101
+ const loadingContent = loadingOverlay.querySelector('.loading-content');
102
+
103
+ loadingContent.innerHTML = `
104
+ <div class="error-content">
105
+ <i class="fas fa-exclamation-triangle"></i>
106
+ <h2>Failed to Load Model</h2>
107
+ <p>There was an error loading the AI model. Please refresh the page and try again.</p>
108
+ <button onclick="location.reload()" class="retry-button">
109
+ <i class="fas fa-redo"></i> Retry
110
+ </button>
111
+ </div>
112
+ `;
113
+ }
114
+ }
115
+
116
+ async sendMessage() {
117
+ if (this.isGenerating || !this.generator) return;
118
+
119
+ const messageInput = document.getElementById('messageInput');
120
+ const message = messageInput.value.trim();
121
 
122
+ if (!message) return;
123
 
124
+ // Add user message to history
125
+ this.messages.push({ role: "user", content: message });
126
+ this.addMessageToUI(message, 'user');
127
 
128
+ // Clear input
129
+ messageInput.value = '';
130
+ this.updateCharCount();
131
+ this.autoResizeTextarea();
132
+
133
+ // Disable send button
134
+ this.isGenerating = true;
135
+ document.getElementById('sendButton').disabled = true;
136
+
137
+ // Show typing indicator
138
+ this.showTypingIndicator();
139
+
140
+ try {
141
+ // Create text streamer for real-time output
142
+ const streamer = new TextStreamer(this.generator.tokenizer, {
143
+ skip_prompt: true,
144
+ skip_special_tokens: true,
145
+ callback_function: (text) => {
146
+ this.updateStreamingMessage(text);
147
+ }
148
  });
149
 
150
+ // Generate response
151
+ const output = await this.generator(this.messages, {
152
+ max_new_tokens: 512,
153
+ do_sample: false,
154
+ temperature: 0.7,
155
+ streamer: streamer
156
+ });
157
 
158
+ // Get the assistant's response
159
+ const assistantMessage = output[0].generated_text.at(-1).content;
160
+ this.messages.push({ role: "assistant", content: assistantMessage });
 
161
 
162
+ } catch (error) {
163
+ console.error('Error generating response:', error);
164
+ this.addMessageToUI('Sorry, I encountered an error. Please try again.', 'assistant error');
165
+ } finally {
166
+ // Hide typing indicator and re-enable send button
167
+ this.hideTypingIndicator();
168
+ this.isGenerating = false;
169
+ document.getElementById('sendButton').disabled = false;
170
+ }
171
  }
172
 
173
+ showTypingIndicator() {
174
+ const typingIndicator = document.getElementById('typingIndicator');
175
+ typingIndicator.style.display = 'block';
176
 
177
+ // Create a placeholder message for streaming
178
+ this.streamingMessageElement = this.addMessageToUI('', 'assistant', true);
179
+ }
180
+
181
+ hideTypingIndicator() {
182
+ const typingIndicator = document.getElementById('typingIndicator');
183
+ typingIndicator.style.display = 'none';
184
+ }
185
+
186
+ updateStreamingMessage(text) {
187
+ if (this.streamingMessageElement) {
188
+ const messageContent = this.streamingMessageElement.querySelector('.message-text');
189
+ if (messageContent) {
190
+ messageContent.textContent = text;
191
+ this.scrollToBottom();
192
+ }
193
+ }
194
+ }
195
 
196
+ addMessageToUI(content, role, isStreaming = false) {
197
+ const chatMessages = document.getElementById('chatMessages');
198
+ const messageDiv = document.createElement('div');
199
+ messageDiv.className = `message ${role}`;
 
 
 
 
 
 
200
 
201
+ const messageContent = document.createElement('div');
202
+ messageContent.className = 'message-content';
 
 
 
203
 
204
+ if (role === 'assistant') {
205
+ messageContent.innerHTML = `
206
+ <div class="avatar">
207
+ <i class="fas fa-robot"></i>
208
+ </div>
209
+ <div class="message-content-inner">
210
+ <div class="message-role">AI Assistant</div>
211
+ <div class="message-text">${isStreaming ? '' : this.formatMessage(content)}</div>
212
+ </div>
213
+ `;
214
+ } else if (role === 'user') {
215
+ messageContent.innerHTML = `
216
+ <div class="avatar user">
217
+ <i class="fas fa-user"></i>
218
+ </div>
219
+ <div class="message-content-inner">
220
+ <div class="message-role">You</div>
221
+ <div class="message-text">${this.formatMessage(content)}</div>
222
+ </div>
223
+ `;
224
+ } else {
225
+ messageContent.innerHTML = `<p>${content}</p>`;
226
  }
227
+
228
+ messageDiv.appendChild(messageContent);
229
+ chatMessages.appendChild(messageDiv);
230
+
231
+ // Smooth scroll to bottom
232
+ this.scrollToBottom();
233
+
234
+ // Add entrance animation
235
+ requestAnimationFrame(() => {
236
+ messageDiv.style.opacity = '1';
237
+ messageDiv.style.transform = 'translateY(0)';
238
+ });
239
+
240
+ return messageDiv;
241
+ }
242
+
243
+ formatMessage(content) {
244
+ // Basic markdown-like formatting
245
+ return content
246
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
247
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
248
+ .replace(/`(.*?)`/g, '<code>$1</code>')
249
+ .replace(/\n/g, '<br>');
250
+ }
251
+
252
+ scrollToBottom() {
253
+ const chatMessages = document.getElementById('chatMessages');
254
+ chatMessages.scrollTop = chatMessages.scrollHeight;
255
+ }
256
+ }
257
+
258
+ // Initialize the chatbot when the page loads
259
+ document.addEventListener('DOMContentLoaded', () => {
260
+ new AppleChatBot();
261
+ });