Eric Xu commited on
Add elapsed timer, pulsing progress bar, timeouts with fallbacks for long LLM calls
Browse files- 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('
|
| 831 |
-
|
| 832 |
-
|
| 833 |
-
|
| 834 |
-
|
| 835 |
-
|
| 836 |
-
|
| 837 |
-
|
| 838 |
-
|
| 839 |
-
|
| 840 |
-
|
| 841 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|