Supra-Tex / hf_space /generation_progress.js
UniLat3D's picture
Show actual GPU queue position and tasks ahead
9f32d99 verified
Raw History Blame Contribute Delete
5.16 kB
// The server starts the generation clock after weights and GPU admission.
const root = document.getElementById('st-app');
if (!root || root.dataset.generationProgressInstalled) return;
root.dataset.generationProgressInstalled = 'true';
let token = '';
let started = null;
let elapsedFloor = 0;
let lastState = '';
let clock = 'waiting';
let phase = 'queued';
let queueState = 'checking';
let queuePosition = null;
let queueSize = null;
let queueAhead = null;
let queueRunning = null;
const text = (element, value) => {
if (element && element.textContent !== value) element.textContent = value;
};
const paint = () => {
const card = root.querySelector('.st-generation');
if (!card) {
token = '';
lastState = '';
started = null;
return;
}
if (card.dataset.token !== token) {
token = card.dataset.token;
started = null;
elapsedFloor = 0;
lastState = '';
clock = 'waiting';
phase = card.dataset.phase || 'queued';
queueState = 'checking';
queuePosition = queueSize = queueAhead = queueRunning = null;
}
const state = root.querySelector('#st-generation-state [data-token]');
const signature = state ? [state.dataset.token, state.dataset.status, state.dataset.label, state.dataset.elapsedSeconds, state.dataset.clock,
state.dataset.queueState, state.dataset.queuePosition, state.dataset.queueSize, state.dataset.queueAhead, state.dataset.queueRunning].join('|') : '';
if (state && signature !== lastState && state.dataset.token === token) {
lastState = signature;
phase = state.dataset.status;
queueState = state.dataset.queueState || 'checking';
const count = value => value !== undefined && /^\d+$/.test(value) ? Number(value) : null;
queuePosition = count(state.dataset.queuePosition);
queueSize = count(state.dataset.queueSize);
queueAhead = count(state.dataset.queueAhead);
queueRunning = count(state.dataset.queueRunning);
const nextClock = state.dataset.clock || 'waiting';
const serverElapsed = Number(state.dataset.elapsedSeconds) || 0;
if (nextClock === 'running') {
// A delayed poll must not rewind a clock that is already running.
const localElapsed = started === null ? 0 : (performance.now() - started) / 1000;
elapsedFloor = Math.max(serverElapsed, localElapsed);
started = performance.now() - elapsedFloor * 1000;
} else {
elapsedFloor = serverElapsed;
started = null;
}
clock = nextClock;
text(card.querySelector('.st-generation-stage'), 'Stage: ' + state.dataset.label);
}
const preparing = phase === 'preparing';
const timed = clock !== 'waiting';
text(card.querySelector('h3'), preparing ? 'Loading weights' : timed ? 'Generating 3D' : 'Waiting for GPU');
card.querySelector('.st-generation-preparing').hidden = !preparing;
card.querySelector('.st-generation-timing').hidden = !timed;
card.querySelector('.st-generation-stage').hidden = !timed;
card.querySelector('.st-generation-queue').hidden = timed || preparing;
let positionText = queueState === 'checking' ? 'Checking queue position…' : 'Queue position is currently unavailable.';
let detailText = '';
if (queueState === 'waiting' && queuePosition > 0 && queueSize >= queuePosition && queueAhead !== null && queueRunning !== null) {
positionText = `Your queue position: #${queuePosition} of ${queueSize} waiting`;
detailText = `${queueAhead} ${queueAhead === 1 ? 'task' : 'tasks'} waiting ahead · ${queueRunning} running`;
} else if (queueState === 'admitted') {
positionText = 'Your task has reached the GPU worker.';
detailText = 'Starting…';
}
text(card.querySelector('.st-generation-queue-position'), positionText);
text(card.querySelector('.st-generation-queue-detail'), detailText);
card.classList.toggle('waiting', false);
if (!timed) return;
const elapsed = started === null ? elapsedFloor : Math.max(elapsedFloor, (performance.now() - started) / 1000);
const estimate = Number(card.dataset.estimate) || 360;
const percent = Math.min(95, elapsed / estimate * 100);
text(card.querySelector('.st-generation-elapsed'), `Elapsed: ${Math.floor(elapsed)} s`);
const track = card.querySelector('.st-estimate-track');
track.setAttribute('aria-valuenow', String(Math.floor(percent)));
track.setAttribute('aria-valuetext', `Elapsed ${Math.floor(elapsed)} seconds; estimated total ${estimate} seconds`);
card.querySelector('.st-estimate-fill').style.width = `${percent}%`;
const overdue = elapsed >= estimate;
card.querySelector('.st-generation-overdue').hidden = !overdue;
card.classList.toggle('waiting', overdue);
};
const observer = new MutationObserver(paint);
observer.observe(root, {childList: true, subtree: true});
const interval = window.setInterval(paint, 250);
paint();
window.addEventListener('pagehide', () => {
observer.disconnect();
window.clearInterval(interval);
}, {once: true});