AI Agent commited on
Commit
87e6e59
·
1 Parent(s): 03cfa1a

Fix UI 404 by applying async job queue client logic to actual index.html template

Browse files
Files changed (1) hide show
  1. templates/index.html +86 -50
templates/index.html CHANGED
@@ -433,72 +433,108 @@
433
  cpu_threads: 16
434
  };
435
 
436
- const res = await fetch('/api/query', {
437
  method: 'POST',
438
  headers: { 'Content-Type': 'application/json', 'X-Session-Token': SESSION_TOKEN },
439
  body: JSON.stringify(payload)
440
  });
441
 
442
- if (!res.ok) {
443
- throw new Error(`Network response was not ok: ${res.status}`);
444
  }
 
 
 
445
 
446
  diag.info('Generation started. Streaming chunks...');
447
- const reader = res.body.getReader();
448
- const decoder = new TextDecoder('utf-8');
449
- let done = false;
450
  let answer = '';
451
 
452
- while (!done) {
453
- const { value, done: readerDone } = await reader.read();
454
- if (value) {
455
- const chunk = decoder.decode(value, { stream: true });
456
- const lines = chunk.split('\n\n');
457
- for (let line of lines) {
458
- if (line.startsWith('data: ')) {
459
- try {
460
- const data = JSON.parse(line.substring(6));
461
- if (data.chunk) {
462
- genGraph.style.display = 'none';
463
- answer += data.chunk;
464
- diag.info(`Received chunk: ${data.chunk}`);
465
- if (window.marked) {
466
- answerContent.innerHTML = marked.parse(answer);
467
- } else {
468
- answerContent.textContent = answer;
469
- }
470
- // Auto-scroll to bottom
471
- window.scrollTo(0, document.body.scrollHeight);
472
- } else if (data.status) {
473
- diag.info(`Status update:`, data.status);
474
- if (milestoneText) {
475
- const msg = typeof data.status === 'string' ? data.status : (data.status.message || 'Processing...');
476
- milestoneText.setAttribute('data-status', msg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  }
478
- } else if (data.metrics) {
479
- diag.info(`Generation metrics:`, data.metrics);
480
- const metricsHtml = `
481
- <div style="font-size: 0.85rem; color: #666; margin-top: 15px; border-top: 1px dashed var(--border-color); padding-top: 10px;">
482
- Analysis took ${data.metrics.time_seconds?.toFixed(1) || '?'} seconds.
483
- Tokens: ${data.metrics.tokens_in || '?'} in, ${data.metrics.tokens_out || '?'} out.
484
- </div>
485
- `;
486
- answerContent.innerHTML += metricsHtml;
487
- window.scrollTo(0, document.body.scrollHeight);
488
- } else if (data.done) {
489
- diag.info('Generation complete.');
490
- } else if (data.error) {
491
- genGraph.style.display = 'none';
492
- diag.error(`Backend error: ${data.error}`);
493
- answerContent.innerHTML += `<p style="color:#d32f2f; font-weight:bold;">Error: ${data.error}</p>`;
494
  }
495
- } catch (e) {
496
- // Ignore parse errors on incomplete chunks
497
  }
498
  }
499
  }
 
 
 
 
 
 
500
  }
501
- done = readerDone;
502
  }
503
  } catch (e) {
504
  genGraph.style.display = 'none';
 
433
  cpu_threads: 16
434
  };
435
 
436
+ const startRes = await fetch('/api/query/start', {
437
  method: 'POST',
438
  headers: { 'Content-Type': 'application/json', 'X-Session-Token': SESSION_TOKEN },
439
  body: JSON.stringify(payload)
440
  });
441
 
442
+ if (!startRes.ok) {
443
+ throw new Error(`Network response was not ok: ${startRes.status}`);
444
  }
445
+
446
+ const startData = await startRes.json();
447
+ const jobId = startData.job_id;
448
 
449
  diag.info('Generation started. Streaming chunks...');
450
+
451
+ let jobDone = false;
452
+ let currentOffset = 0;
453
  let answer = '';
454
 
455
+ while (!jobDone) {
456
+ try {
457
+ const res = await fetch(`/api/query/stream/${jobId}?offset=${currentOffset}`, {
458
+ headers: { 'X-Session-Token': SESSION_TOKEN }
459
+ });
460
+
461
+ if (!res.ok) {
462
+ if (res.status === 404) throw new Error("Job not found or expired on server.");
463
+ throw new Error(`Stream network response was not ok: ${res.status}`);
464
+ }
465
+
466
+ const reader = res.body.getReader();
467
+ const decoder = new TextDecoder('utf-8');
468
+ let buffer = '';
469
+
470
+ while (!jobDone) {
471
+ const { value, done: readerDone } = await reader.read();
472
+ if (readerDone) break;
473
+ if (value) {
474
+ buffer += decoder.decode(value, { stream: true });
475
+ const lines = buffer.split('\n\n');
476
+ buffer = lines.pop(); // keep the last incomplete chunk in buffer
477
+
478
+ for (let line of lines) {
479
+ if (line.startsWith('data: ')) {
480
+ try {
481
+ const data = JSON.parse(line.substring(6));
482
+
483
+ // Successfully received an event
484
+ currentOffset++;
485
+
486
+ if (data.chunk) {
487
+ genGraph.style.display = 'none';
488
+ answer += data.chunk;
489
+ diag.info(`Received chunk: ${data.chunk}`);
490
+ if (window.marked) {
491
+ answerContent.innerHTML = marked.parse(answer);
492
+ } else {
493
+ answerContent.textContent = answer;
494
+ }
495
+ // Auto-scroll to bottom
496
+ window.scrollTo(0, document.body.scrollHeight);
497
+ } else if (data.status) {
498
+ diag.info(`Status update:`, data.status);
499
+ if (milestoneText) {
500
+ const msg = typeof data.status === 'string' ? data.status : (data.status.message || 'Processing...');
501
+ milestoneText.setAttribute('data-status', msg);
502
+ }
503
+ } else if (data.metrics) {
504
+ diag.info(`Generation metrics:`, data.metrics);
505
+ const metricsHtml = `
506
+ <div style="font-size: 0.85rem; color: #666; margin-top: 15px; border-top: 1px dashed var(--border-color); padding-top: 10px;">
507
+ Analysis took ${data.metrics.time_seconds?.toFixed(1) || '?'} seconds.
508
+ Tokens: ${data.metrics.tokens_in || '?'} in, ${data.metrics.tokens_out || '?'} out.
509
+ </div>
510
+ `;
511
+ answerContent.innerHTML += metricsHtml;
512
+ window.scrollTo(0, document.body.scrollHeight);
513
+ } else if (data.done) {
514
+ diag.info('Generation complete.');
515
+ jobDone = true;
516
+ break;
517
+ } else if (data.error) {
518
+ genGraph.style.display = 'none';
519
+ diag.error(`Backend error: ${data.error}`);
520
+ answerContent.innerHTML += `<p style="color:#d32f2f; font-weight:bold;">Error: ${data.error}</p>`;
521
+ jobDone = true;
522
+ break;
523
+ }
524
+ } catch (e) {
525
+ // Ignore parse errors on incomplete chunks
526
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
527
  }
 
 
528
  }
529
  }
530
  }
531
+ } catch (err) {
532
+ if (err.message.includes("Job not found") || jobDone) {
533
+ throw err;
534
+ }
535
+ diag.warn('Network dropped, reconnecting to job...', err);
536
+ await new Promise(r => setTimeout(r, 1500));
537
  }
 
538
  }
539
  } catch (e) {
540
  genGraph.style.display = 'none';