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 <[email protected]> Co-authored-by: rltakashige <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
rltakashige
parent
addf73a144
commit
bddad7e79c
@@ -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>
|
||||
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user