akhaliq HF Staff commited on
Commit
a79d5cf
·
1 Parent(s): 5894cc6

feat: implement support for aborting active chat generation jobs

Browse files
Files changed (1) hide show
  1. index.html +65 -15
index.html CHANGED
@@ -205,10 +205,10 @@ async function getClient() {
205
  return gradioClient;
206
  }
207
 
208
- // ── State ──
209
  let conversations = [];
210
  let currentConv = null;
211
  let isGenerating = false;
 
212
 
213
  // ── Init ──
214
  function init() {
@@ -231,6 +231,14 @@ function autoResize() {
231
 
232
  // ── Chat Management ──
233
  function newChat() {
 
 
 
 
 
 
 
 
234
  const conv = { id: Date.now(), title: 'New Chat', messages: [] };
235
  conversations.unshift(conv);
236
  currentConv = conv;
@@ -240,6 +248,14 @@ function newChat() {
240
  }
241
 
242
  function switchChat(id) {
 
 
 
 
 
 
 
 
243
  currentConv = conversations.find(c => c.id === id);
244
  renderChatList();
245
  renderMessages();
@@ -307,11 +323,23 @@ function escapeHTML(s) {
307
  const d = document.createElement('div'); d.textContent = s; return d.innerHTML;
308
  }
309
 
 
310
  // ── Send Message (uses Gradio JS Client) ──
311
  async function sendMessage() {
 
 
 
 
 
 
 
 
 
 
 
312
  const input = document.getElementById('userInput');
313
  const text = input.value.trim();
314
- if (!text || isGenerating) return;
315
 
316
  // Hide welcome
317
  const welcome = document.getElementById('welcome');
@@ -331,7 +359,7 @@ async function sendMessage() {
331
  input.value = '';
332
  input.style.height = 'auto';
333
  isGenerating = true;
334
- document.getElementById('sendBtn').disabled = true;
335
 
336
  // Add assistant placeholder with typing indicator
337
  const assistantDiv = document.createElement('div');
@@ -347,7 +375,7 @@ async function sendMessage() {
347
  const client = await getClient();
348
  const history = currentConv.messages.slice(0, -1);
349
 
350
- const job = client.submit("/generate", {
351
  message: text,
352
  history: JSON.stringify(history),
353
  system_prompt: document.getElementById('systemPrompt').value,
@@ -356,24 +384,46 @@ async function sendMessage() {
356
  max_new_tokens: parseInt(document.getElementById('maxTokens').value),
357
  });
358
 
359
- for await (const event of job) {
360
- if (event.type === "data") {
361
- fullText = event.data[0];
362
- bubble.innerHTML = formatResponse(fullText);
363
- messagesEl.scrollTop = messagesEl.scrollHeight;
364
- }
365
- }
 
 
 
 
 
 
366
  } catch (err) {
 
367
  if (!fullText) {
368
  fullText = 'Sorry, an error occurred. Please try again.';
369
  bubble.innerHTML = `<span style="color:var(--red)">${escapeHTML(err.message || fullText)}</span>`;
370
  }
 
 
 
 
 
 
 
 
 
371
  }
 
372
 
373
- currentConv.messages.push({ role: 'assistant', content: fullText });
374
- isGenerating = false;
375
- document.getElementById('sendBtn').disabled = false;
376
- document.getElementById('userInput').focus();
 
 
 
 
 
377
  }
378
 
379
  // ── Globals for onclick handlers ──
 
205
  return gradioClient;
206
  }
207
 
 
208
  let conversations = [];
209
  let currentConv = null;
210
  let isGenerating = false;
211
+ let currentJob = null;
212
 
213
  // ── Init ──
214
  function init() {
 
231
 
232
  // ── Chat Management ──
233
  function newChat() {
234
+ if (isGenerating && currentJob) {
235
+ try { currentJob.destroy(); } catch(e) {}
236
+ }
237
+ isGenerating = false;
238
+ currentJob = null;
239
+ document.getElementById('sendBtn').disabled = false;
240
+ updateSendBtnIcon(false);
241
+
242
  const conv = { id: Date.now(), title: 'New Chat', messages: [] };
243
  conversations.unshift(conv);
244
  currentConv = conv;
 
248
  }
249
 
250
  function switchChat(id) {
251
+ if (isGenerating && currentJob) {
252
+ try { currentJob.destroy(); } catch(e) {}
253
+ }
254
+ isGenerating = false;
255
+ currentJob = null;
256
+ document.getElementById('sendBtn').disabled = false;
257
+ updateSendBtnIcon(false);
258
+
259
  currentConv = conversations.find(c => c.id === id);
260
  renderChatList();
261
  renderMessages();
 
323
  const d = document.createElement('div'); d.textContent = s; return d.innerHTML;
324
  }
325
 
326
+ // ── Send Message (uses Gradio JS Client) ──
327
  // ── Send Message (uses Gradio JS Client) ──
328
  async function sendMessage() {
329
+ if (isGenerating) {
330
+ if (currentJob) {
331
+ try { currentJob.destroy(); } catch(e) {}
332
+ }
333
+ isGenerating = false;
334
+ currentJob = null;
335
+ document.getElementById('sendBtn').disabled = false;
336
+ updateSendBtnIcon(false);
337
+ return;
338
+ }
339
+
340
  const input = document.getElementById('userInput');
341
  const text = input.value.trim();
342
+ if (!text) return;
343
 
344
  // Hide welcome
345
  const welcome = document.getElementById('welcome');
 
359
  input.value = '';
360
  input.style.height = 'auto';
361
  isGenerating = true;
362
+ updateSendBtnIcon(true);
363
 
364
  // Add assistant placeholder with typing indicator
365
  const assistantDiv = document.createElement('div');
 
375
  const client = await getClient();
376
  const history = currentConv.messages.slice(0, -1);
377
 
378
+ currentJob = client.submit("/generate", {
379
  message: text,
380
  history: JSON.stringify(history),
381
  system_prompt: document.getElementById('systemPrompt').value,
 
384
  max_new_tokens: parseInt(document.getElementById('maxTokens').value),
385
  });
386
 
387
+ await new Promise((resolve, reject) => {
388
+ currentJob.on("data", (event) => {
389
+ if (event.data && event.data[0]) {
390
+ fullText = event.data[0];
391
+ bubble.innerHTML = formatResponse(fullText);
392
+ messagesEl.scrollTop = messagesEl.scrollHeight;
393
+ }
394
+ });
395
+ currentJob.on("status", (status) => {
396
+ if (status.stage === "complete") resolve();
397
+ if (status.stage === "error") reject(new Error(status.message || "Generation failed"));
398
+ });
399
+ });
400
  } catch (err) {
401
+ console.error("Generation error:", err);
402
  if (!fullText) {
403
  fullText = 'Sorry, an error occurred. Please try again.';
404
  bubble.innerHTML = `<span style="color:var(--red)">${escapeHTML(err.message || fullText)}</span>`;
405
  }
406
+ } finally {
407
+ isGenerating = false;
408
+ currentJob = null;
409
+ document.getElementById('sendBtn').disabled = false;
410
+ updateSendBtnIcon(false);
411
+ if (fullText) {
412
+ currentConv.messages.push({ role: 'assistant', content: fullText });
413
+ }
414
+ document.getElementById('userInput').focus();
415
  }
416
+ }
417
 
418
+ function updateSendBtnIcon(loading) {
419
+ const btn = document.getElementById('sendBtn');
420
+ if (loading) {
421
+ btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="6" y="6" width="12" height="12"/></svg>`;
422
+ btn.title = "Stop generating";
423
+ } else {
424
+ btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13"/><path d="M22 2L15 22L11 13L2 9L22 2Z"/></svg>`;
425
+ btn.title = "Send message";
426
+ }
427
  }
428
 
429
  // ── Globals for onclick handlers ──