← back to Exo
feat: show ETA on prefill progress bar (#1557)
bddad7e79cb505f895f2fa0d8e21892f65b6ac7e · 2026-02-20 04:37:56 -0800 · Alex Cheema
## Summary
- Show estimated time remaining during prefill (prompt processing phase)
- Track prefill start time via performance.now() and extrapolate from
observed token throughput
- Display ~Xs remaining or ~Xm Ys remaining next to the percentage on
the progress bar
- Wait 200ms before showing ETA to ensure a stable sample window
## Changes
**PrefillProgressBar.svelte**: Add etaText derived computation that
calculates remaining time from (remainingTokens / tokensPerMs). Renders
in a new flex row below the progress bar alongside the percentage.
**app.svelte.ts**: Add startedAt: number field to PrefillProgress
interface. Set on first prefill_progress SSE event, preserved across
subsequent updates.
## Test plan
- [ ] Start inference with a long prompt (10k+ tokens) on a multi-node
cluster
- [ ] Verify the progress bar shows ~Xs remaining after ~200ms of
prefill
- [ ] Verify the ETA decreases as prefill progresses
- [ ] Verify short prefills (<200ms) dont flash a briefly-visible ETA
- [ ] Verify ETA disappears when prefill completes and token generation
begins
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: rltakashige <rl.takashige@gmail.com>
Files touched
M dashboard/src/lib/components/PrefillProgressBar.svelteM dashboard/src/lib/stores/app.svelte.ts
Diff
commit bddad7e79cb505f895f2fa0d8e21892f65b6ac7e
Author: Alex Cheema <41707476+AlexCheema@users.noreply.github.com>
Date: Fri Feb 20 04:37:56 2026 -0800
feat: show ETA on prefill progress bar (#1557)
## Summary
- Show estimated time remaining during prefill (prompt processing phase)
- Track prefill start time via performance.now() and extrapolate from
observed token throughput
- Display ~Xs remaining or ~Xm Ys remaining next to the percentage on
the progress bar
- Wait 200ms before showing ETA to ensure a stable sample window
## Changes
**PrefillProgressBar.svelte**: Add etaText derived computation that
calculates remaining time from (remainingTokens / tokensPerMs). Renders
in a new flex row below the progress bar alongside the percentage.
**app.svelte.ts**: Add startedAt: number field to PrefillProgress
interface. Set on first prefill_progress SSE event, preserved across
subsequent updates.
## Test plan
- [ ] Start inference with a long prompt (10k+ tokens) on a multi-node
cluster
- [ ] Verify the progress bar shows ~Xs remaining after ~200ms of
prefill
- [ ] Verify the ETA decreases as prefill progresses
- [ ] Verify short prefills (<200ms) dont flash a briefly-visible ETA
- [ ] Verify ETA disappears when prefill completes and token generation
begins
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: rltakashige <rl.takashige@gmail.com>
---
.../src/lib/components/PrefillProgressBar.svelte | 22 ++++++++++++++++++++--
dashboard/src/lib/stores/app.svelte.ts | 3 +++
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/dashboard/src/lib/components/PrefillProgressBar.svelte b/dashboard/src/lib/components/PrefillProgressBar.svelte
index ea08d11d..c07be12c 100644
--- a/dashboard/src/lib/components/PrefillProgressBar.svelte
+++ b/dashboard/src/lib/components/PrefillProgressBar.svelte
@@ -14,6 +14,21 @@
: 0,
);
+ const etaText = $derived.by(() => {
+ if (progress.processed <= 0 || progress.total <= 0) return null;
+ const elapsedMs = performance.now() - progress.startedAt;
+ if (elapsedMs < 200) return null; // need a minimum sample window
+ const tokensPerMs = progress.processed / elapsedMs;
+ const remainingTokens = progress.total - progress.processed;
+ const remainingMs = remainingTokens / tokensPerMs;
+ const remainingSec = Math.ceil(remainingMs / 1000);
+ if (remainingSec <= 0) return null;
+ if (remainingSec < 60) return `~${remainingSec}s remaining`;
+ const mins = Math.floor(remainingSec / 60);
+ const secs = remainingSec % 60;
+ return `~${mins}m ${secs}s remaining`;
+ });
+
function formatTokenCount(count: number | undefined): string {
if (count == null) return "0";
if (count >= 1000) {
@@ -40,8 +55,11 @@
style="width: {percentage}%"
></div>
</div>
- <div class="text-right text-xs text-exo-light-gray/70 mt-0.5 font-mono">
- {percentage}%
+ <div
+ class="flex items-center justify-between text-xs text-exo-light-gray/70 mt-0.5 font-mono"
+ >
+ <span>{etaText ?? ""}</span>
+ <span>{percentage}%</span>
</div>
</div>
diff --git a/dashboard/src/lib/stores/app.svelte.ts b/dashboard/src/lib/stores/app.svelte.ts
index 03379cee..c10c8e0a 100644
--- a/dashboard/src/lib/stores/app.svelte.ts
+++ b/dashboard/src/lib/stores/app.svelte.ts
@@ -281,6 +281,8 @@ export interface TokenData {
export interface PrefillProgress {
processed: number;
total: number;
+ /** Timestamp (performance.now()) when prefill started. */
+ startedAt: number;
}
export interface Message {
@@ -2464,6 +2466,7 @@ class AppStore {
this.prefillProgress = {
processed: inner.processed_tokens,
total: inner.total_tokens,
+ startedAt: this.prefillProgress?.startedAt ?? performance.now(),
};
},
},
← addf73a1 Add support for Ollama API (#1560)
·
back to Exo
·
fix: enable psutil fallback for memory monitoring when macmo e32b649d →