Eric Xu commited on
Commit
d00cf52
·
unverified ·
1 Parent(s): b6efc9e

Add elapsed timer, pulsing progress bar, timeouts with fallbacks for long LLM calls

Browse files
Files changed (1) hide show
  1. web/static/index.html +53 -13
web/static/index.html CHANGED
@@ -167,6 +167,13 @@
167
  transition: width 0.3s;
168
  width: 0%;
169
  }
 
 
 
 
 
 
 
170
  .progress-text {
171
  font-size: 0.85rem;
172
  color: var(--text2);
@@ -824,21 +831,48 @@ async function runFullPipeline() {
824
  sessionId = sessData.session_id;
825
  // session created silently
826
 
 
 
 
 
 
 
 
 
 
 
827
  // Phase 2: Suggest segments
828
  document.getElementById('pipelineProgressText').textContent = 'Choosing panel segments...';
829
  document.getElementById('pipelineProgressBar').style.width = '10%';
830
- logStep('Choosing the right panel segments...');
831
-
832
- const segResp = await fetch(apiUrl('/api/suggest-segments'), {
833
- method: 'POST',
834
- headers: llmHeaders(),
835
- body: JSON.stringify({
836
- entity_text: text,
837
- audience_context: audienceCtx || `People who would evaluate: ${text.substring(0, 200)}`,
838
- }),
839
- });
840
- const segData = await segResp.json();
841
- const segments = segData.segments || [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
842
 
843
  // Scale segment counts to match requested panel size
844
  const totalSuggested = segments.reduce((a, s) => a + (s.count || 8), 0);
@@ -850,7 +884,7 @@ async function runFullPipeline() {
850
  document.getElementById('pipelineProgressBar').style.width = '20%';
851
 
852
  // Phase 3: Generate cohort
853
- document.getElementById('pipelineProgressText').textContent = 'Building your panel...';
854
  logStep('Building panel members for each segment...');
855
 
856
  const desc = audienceCtx || `People evaluating: ${text.substring(0, 200)}`;
@@ -859,6 +893,10 @@ async function runFullPipeline() {
859
  headers: llmHeaders(),
860
  body: JSON.stringify({description: desc, audience_context: audienceCtx, segments, parallel: 3}),
861
  });
 
 
 
 
862
  const cohortData = await cohortResp.json();
863
 
864
  // Upload cohort to our session
@@ -939,6 +977,8 @@ async function runFullPipeline() {
939
  logStep(`Error: ${e.message}`, 'err');
940
  } finally {
941
  btn.disabled = false;
 
 
942
  }
943
  }
944
 
 
167
  transition: width 0.3s;
168
  width: 0%;
169
  }
170
+ .progress-fill.pulsing {
171
+ animation: pulse-bar 1.5s ease-in-out infinite;
172
+ }
173
+ @keyframes pulse-bar {
174
+ 0%, 100% { opacity: 1; }
175
+ 50% { opacity: 0.5; }
176
+ }
177
  .progress-text {
178
  font-size: 0.85rem;
179
  color: var(--text2);
 
831
  sessionId = sessData.session_id;
832
  // session created silently
833
 
834
+ // Start elapsed timer
835
+ const startTime = Date.now();
836
+ const timerInterval = setInterval(() => {
837
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
838
+ const current = document.getElementById('pipelineProgressText').textContent;
839
+ const base = current.replace(/ \(\d+s\)$/, '');
840
+ document.getElementById('pipelineProgressText').textContent = `${base} (${elapsed}s)`;
841
+ }, 1000);
842
+ document.getElementById('pipelineProgressBar').classList.add('pulsing');
843
+
844
  // Phase 2: Suggest segments
845
  document.getElementById('pipelineProgressText').textContent = 'Choosing panel segments...';
846
  document.getElementById('pipelineProgressBar').style.width = '10%';
847
+ logStep('Asking LLM to choose panel segments...');
848
+
849
+ let segments;
850
+ try {
851
+ const segResp = await Promise.race([
852
+ fetch(apiUrl('/api/suggest-segments'), {
853
+ method: 'POST',
854
+ headers: llmHeaders(),
855
+ body: JSON.stringify({
856
+ entity_text: text,
857
+ audience_context: audienceCtx || `People who would evaluate: ${text.substring(0, 200)}`,
858
+ }),
859
+ }),
860
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Segment suggestion timed out after 60s')), 60000)),
861
+ ]);
862
+ if (!segResp.ok) {
863
+ const err = await segResp.json().catch(() => ({}));
864
+ throw new Error(err.detail || `Server error ${segResp.status}`);
865
+ }
866
+ const segData = await segResp.json();
867
+ segments = segData.segments || [];
868
+ } catch (e) {
869
+ logStep(`Segment suggestion failed: ${e.message} — using defaults`, 'err');
870
+ segments = [
871
+ {label: 'Primary audience', count: Math.ceil(panelSize / 3)},
872
+ {label: 'Adjacent audience', count: Math.ceil(panelSize / 3)},
873
+ {label: 'Skeptical perspective', count: Math.ceil(panelSize / 3)},
874
+ ];
875
+ }
876
 
877
  // Scale segment counts to match requested panel size
878
  const totalSuggested = segments.reduce((a, s) => a + (s.count || 8), 0);
 
884
  document.getElementById('pipelineProgressBar').style.width = '20%';
885
 
886
  // Phase 3: Generate cohort
887
+ document.getElementById('pipelineProgressText').textContent = 'Building your panel (this takes 30-60s)...';
888
  logStep('Building panel members for each segment...');
889
 
890
  const desc = audienceCtx || `People evaluating: ${text.substring(0, 200)}`;
 
893
  headers: llmHeaders(),
894
  body: JSON.stringify({description: desc, audience_context: audienceCtx, segments, parallel: 3}),
895
  });
896
+ if (!cohortResp.ok) {
897
+ const err = await cohortResp.json().catch(() => ({}));
898
+ throw new Error(`Failed to build panel: ${err.detail || cohortResp.status}`);
899
+ }
900
  const cohortData = await cohortResp.json();
901
 
902
  // Upload cohort to our session
 
977
  logStep(`Error: ${e.message}`, 'err');
978
  } finally {
979
  btn.disabled = false;
980
+ clearInterval(timerInterval);
981
+ document.getElementById('pipelineProgressBar').classList.remove('pulsing');
982
  }
983
  }
984