mihailik commited on
Commit
7256ada
·
1 Parent(s): 8d3c64b

Error handling - more.

Browse files
Files changed (2) hide show
  1. chat-copil-web.html +70 -225
  2. chat-full.js +11 -0
chat-copil-web.html CHANGED
@@ -1,250 +1,95 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
-
4
  <head>
5
- <meta charset="utf-8" />
6
- <title>Minimal On-Device Chat</title>
7
  <style>
8
  body {
9
- margin: 0;
10
  font-family: sans-serif;
11
- background: #111;
12
- color: #eee;
13
- display: flex;
14
- flex-direction: column;
15
- height: 100vh;
16
- }
17
-
18
- header,
19
- form {
20
- padding: 10px;
21
- background: #222;
22
- display: flex;
23
- gap: 10px;
24
- align-items: center;
25
  }
26
-
27
  #chat {
28
- flex: 1;
29
- overflow-y: auto;
30
- padding: 10px;
31
- display: flex;
32
- flex-direction: column;
33
- gap: 8px;
34
- }
35
-
36
- .msg {
37
- display: flex;
38
- gap: 8px;
39
- align-items: flex-start;
40
- }
41
-
42
- .avatar {
43
- width: 24px;
44
- height: 24px;
45
- border-radius: 50%;
46
- background: #444;
47
- color: #ccc;
48
- font-size: 12px;
49
- font-weight: bold;
50
- display: flex;
51
- align-items: center;
52
- justify-content: center;
53
- }
54
-
55
- .bubble {
56
- background: #333;
57
- padding: 8px 10px;
58
  border-radius: 8px;
59
- max-width: 80%;
60
- white-space: pre-wrap;
61
  }
62
-
63
- .user .bubble {
64
- background: #2a2a2a;
 
65
  }
66
-
67
- .error .bubble {
68
- background: #4a1a1a;
69
- color: #f88;
70
  }
71
-
72
- textarea {
73
- flex: 1;
74
- padding: 8px;
75
- border-radius: 6px;
76
- border: none;
77
- background: #1a1a1a;
78
- color: #eee;
79
  }
80
-
81
- button,
82
- select {
83
- padding: 6px 10px;
84
- background: #444;
85
- color: #eee;
86
- border: none;
87
  border-radius: 6px;
88
  }
89
  </style>
90
  </head>
91
-
92
  <body>
93
- <header>
94
- <strong>Model:</strong>
95
- <select id="model">
96
- <option value="Xenova/distilgpt2">distilgpt2</option>
97
- <option value="Xenova/phi-3-mini-4k-instruct">phi-3-mini</option>
98
- <option value="Xenova/t5-small">t5-small</option>
99
- </select>
100
- <span id="status">Idle</span>
101
- </header>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
- <div id="chat" aria-live="polite" aria-busy="false"></div>
104
-
105
- <form id="composer">
106
- <textarea id="input" placeholder="Say something…" rows="2"></textarea>
107
- <button id="send" type="submit">Send</button>
108
- </form>
109
-
110
- <script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@3.0.0/dist/transformers.min.js"></script>
111
- <script>
112
- (function () {
113
- const elChat = document.getElementById('chat');
114
- const elInput = document.getElementById('input');
115
- const elSend = document.getElementById('send');
116
- const elForm = document.getElementById('composer');
117
- const elModel = document.getElementById('model');
118
- const elStatus = document.getElementById('status');
119
-
120
- const { pipeline, AutoTokenizer } = window.transformers || {};
121
-
122
- const state = {
123
- busy: false,
124
- modelId: elModel.value,
125
- systemPrompt: 'You are a helpful assistant.',
126
- history: [],
127
- resources: new Map(),
128
- };
129
-
130
- const setBusy = (busy, label = '') => {
131
- state.busy = busy;
132
- [elInput, elSend, elModel].forEach(el => el.disabled = busy);
133
- elChat.setAttribute('aria-busy', String(busy));
134
- elStatus.textContent = busy ? label : 'Idle';
135
- };
136
-
137
- const addMessage = (role, text, variant = '') => {
138
- const msg = document.createElement('div');
139
- msg.className = `msg ${role} ${variant}`;
140
- const avatar = document.createElement('div');
141
- avatar.className = 'avatar';
142
- avatar.textContent = role === 'user' ? 'U' : (variant === 'error' ? '!' : 'AI');
143
- const bubble = document.createElement('div');
144
- bubble.className = 'bubble';
145
- bubble.textContent = text;
146
- msg.append(avatar, bubble);
147
- elChat.appendChild(msg);
148
- elChat.scrollTop = elChat.scrollHeight;
149
- return bubble;
150
- };
151
-
152
- const pushError = (err, context = 'Error') => {
153
- const msg = `${context}: ${err?.message || err}`;
154
- console.error(context, err);
155
- addMessage('assistant', msg, 'error');
156
- state.history.push({ role: 'assistant', content: msg });
157
- elStatus.textContent = 'Error';
158
- };
159
-
160
- const ensureModelLoaded = async (modelId) => {
161
- if (state.resources.has(modelId)) return state.resources.get(modelId);
162
- setBusy(true, 'Loading…');
163
- const task = modelId.includes('t5') ? 'text2text-generation' : 'text-generation';
164
- const pipe = await pipeline(task, modelId);
165
- const tokenizer = await AutoTokenizer.from_pretrained(modelId).catch(() => null);
166
- const res = { task, pipe, tokenizer };
167
- state.resources.set(modelId, res);
168
- setBusy(false);
169
- return res;
170
- };
171
-
172
- const buildPrompt = (tokenizer, task, history, systemPrompt) => {
173
- const messages = systemPrompt ? [{ role: 'system', content: systemPrompt }] : [];
174
- messages.push(...history);
175
- if (tokenizer?.apply_chat_template) {
176
  try {
177
- return tokenizer.apply_chat_template(messages, { add_generation_prompt: true, tokenize: false });
178
- } catch {}
179
- }
180
- return messages.map(m => `${m.role}: ${m.content}`).join('\n') + '\nassistant:';
181
- };
182
-
183
- const generateReply = async (text) => {
184
- addMessage('user', text);
185
- state.history.push({ role: 'user', content: text });
186
- const botBubble = addMessage('assistant', '…');
187
-
188
- try {
189
- const { task, pipe, tokenizer } = await ensureModelLoaded(state.modelId);
190
- setBusy(true, 'Generating…');
191
- const prompt = buildPrompt(tokenizer, task, state.history, state.systemPrompt);
192
- const output = await pipe(prompt, {
193
- max_new_tokens: 200,
194
- temperature: 0.7,
195
- top_p: 0.95,
196
- do_sample: true,
197
- repetition_penalty: 1.1,
198
- return_full_text: false,
199
- });
200
- const textOut = output?.[0]?.generated_text || output?.[0]?.summary_text || output?.[0]?.translation_text || '(no output)';
201
- botBubble.textContent = textOut.trim();
202
- state.history.push({ role: 'assistant', content: textOut.trim() });
203
- } catch (err) {
204
- pushError(err, 'Generation failed');
205
- botBubble.textContent = '(error)';
206
- } finally {
207
- setBusy(false);
208
- }
209
- };
210
-
211
- elForm.addEventListener('submit', async (e) => {
212
- e.preventDefault();
213
- const text = elInput.value.trim();
214
- if (!text || state.busy) return;
215
- elInput.value = '';
216
- try {
217
- await generateReply(text);
218
- } catch (err) {
219
- pushError(err, 'Submit error');
220
- }
221
- });
222
-
223
- elInput.addEventListener('keydown', (e) => {
224
- if (e.key === 'Enter' && !e.shiftKey) {
225
- e.preventDefault();
226
- elForm.requestSubmit();
227
  }
228
  });
229
-
230
- elModel.addEventListener('change', async () => {
231
- if (state.busy) return;
232
- state.modelId = elModel.value;
233
- addMessage('assistant', `Switched to model: ${state.modelId}`);
234
- try {
235
- await ensureModelLoaded(state.modelId);
236
- } catch (err) {
237
- pushError(err, 'Model load failed');
238
- }
239
- });
240
-
241
- window.addEventListener('error', e => pushError(e.error || e.message, 'Uncaught error'));
242
- window.addEventListener('unhandledrejection', e => pushError(e.reason, 'Unhandled rejection'));
243
-
244
- addMessage('assistant', 'Hello! Choose a model and say something.');
245
- setTimeout(() => ensureModelLoaded(state.modelId).catch(err => pushError(err, 'Initial load')), 100);
246
  })();
247
  </script>
248
  </body>
249
-
250
- </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
+ <meta charset="UTF-8" />
5
+ <title>AI Chat with Transformers.js</title>
6
  <style>
7
  body {
 
8
  font-family: sans-serif;
9
+ background: #f4f4f4;
10
+ margin: 0;
11
+ padding: 0;
 
 
 
 
 
 
 
 
 
 
 
12
  }
 
13
  #chat {
14
+ max-width: 600px;
15
+ margin: 40px auto;
16
+ background: #fff;
17
+ padding: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  border-radius: 8px;
19
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
 
20
  }
21
+ .message {
22
+ margin: 10px 0;
23
+ padding: 10px;
24
+ border-radius: 6px;
25
  }
26
+ .user {
27
+ background: #d0eaff;
28
+ text-align: right;
 
29
  }
30
+ .ai {
31
+ background: #e8ffe8;
32
+ text-align: left;
 
 
 
 
 
33
  }
34
+ input[type="text"] {
35
+ width: calc(100% - 22px);
36
+ padding: 10px;
37
+ margin-top: 10px;
38
+ border: 1px solid #ccc;
 
 
39
  border-radius: 6px;
40
  }
41
  </style>
42
  </head>
 
43
  <body>
44
+ <div id="chat">
45
+ <div class="message ai">🤖 Hello! Type something and I'll respond.</div>
46
+ <input type="text" id="input" placeholder="Say something..." />
47
+ </div>
48
+
49
+ <script type="module">
50
+ (async function () {
51
+ const chat = document.getElementById('chat');
52
+ const input = document.getElementById('input');
53
+
54
+ let pipeline;
55
+ try {
56
+ const { pipeline: loadPipeline } = await import('https://cdn.jsdelivr.net/npm/@xenova/transformers@3.0.0/dist/transformers.min.js');
57
+ pipeline = await loadPipeline('text-generation', 'Xenova/distilgpt2');
58
+ } catch (err) {
59
+ const div = document.createElement('div');
60
+ div.className = 'message ai';
61
+ div.style.cssText = 'color:#f88;background:#400;padding:10px;border-radius:6px;margin:10px';
62
+ div.textContent = '❌ Failed to load Transformers.js: ' + (err.message || err);
63
+ chat.appendChild(div);
64
+ console.error('Transformers.js load error:', err);
65
+ return;
66
+ }
67
+
68
+ input.addEventListener('keydown', async (e) => {
69
+ if (e.key === 'Enter' && input.value.trim()) {
70
+ const userText = input.value.trim();
71
+ input.value = '';
72
+
73
+ const userDiv = document.createElement('div');
74
+ userDiv.className = 'message user';
75
+ userDiv.textContent = userText;
76
+ chat.appendChild(userDiv);
77
+
78
+ const aiDiv = document.createElement('div');
79
+ aiDiv.className = 'message ai';
80
+ aiDiv.textContent = 'Thinking...';
81
+ chat.appendChild(aiDiv);
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  try {
84
+ const result = await pipeline(userText, { max_new_tokens: 50 });
85
+ aiDiv.textContent = result[0]?.generated_text || '🤖 (No response)';
86
+ } catch (err) {
87
+ aiDiv.textContent = '⚠️ Error generating response: ' + (err.message || err);
88
+ console.error('Generation error:', err);
89
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  }
91
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  })();
93
  </script>
94
  </body>
95
+ </html>
 
chat-full.js CHANGED
@@ -184,3 +184,14 @@ window.onerror = function (msg, url, line, col, error) {
184
  pushMsg('sys', `global unhandled: ${msg}`, details);
185
  return false;
186
  };
 
 
 
 
 
 
 
 
 
 
 
 
184
  pushMsg('sys', `global unhandled: ${msg}`, details);
185
  return false;
186
  };
187
+
188
+ // DEBUG: перевірка виконання модуля
189
+ try {
190
+ const el = document.querySelector('#messages');
191
+ if (el) {
192
+ const div = document.createElement('div');
193
+ div.className = 'msg sys';
194
+ div.textContent = '[debug] JS loaded';
195
+ el.appendChild(div);
196
+ }
197
+ } catch (e) {}