Upload page: resumable/parallel uploads, per-session cancel, disk-backed jobs#71
Merged
Merged
Conversation
…ked jobs Frontend (UploadPage): - Resumable uploads: the picked file is stashed in IndexedDB with a chunk cursor, so a tab close mid-upload resumes on reopen instead of dying. - Parallel runs with independent per-session polling; an "Active" section shows each in-flight run with a phase label (Uploading / Queued for GPU / Running) and a per-card Cancel button. - Testing model: a client-side 20s dry run for local dev without a GPU. - Delete button and a terminal "Cancelled" status on Recent Uploads. Backend: - Per-session subprocess tracking (_tracked_run / bind_session / cancel_session) replaces the global single-process tracking, so /api/cancel-inference/<sid> kills exactly one session's process group and leaves other users' jobs running. The global /cancel-inference kill-all is kept and re-implemented on the same registry. - inference_jobs is mirrored to tmp/<sid>/job.json (atomic write) and read back through _get_inference_job, so status/viewer endpoints survive a gunicorn restart; a job left "running"/"queued" on disk by a dead process is coerced to "failed" instead of polling forever. - Job lifecycle is queued -> running -> completed/failed/cancelled; an on_start callback flips queued->running at GPU acquisition and aborts a run cancelled while still queued. Renamed the per-session cancel view to cancel_inference_session to avoid a duplicate Flask endpoint name with the existing global cancel view.
It was dev scaffolding for exercising the flow without a GPU; not meant to ship. Removes the Testing option, its localStorage timer state, and the resume/cancel branches that special-cased it.
…e exposure - Route job.json paths through secure_filename (the CodeQL-recognised path-injection barrier per path_safety.py); session_id reaches the filesystem in _job_meta_path/_persist_inference_job. Real ids are UUIDs and pass through unchanged. - Per-session cancel endpoint no longer returns str(e) to the client; logs the detail server-side and returns a generic message.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Upgrades the existing inference-upload flow (which recently gained server-side queueing + a global kill-all cancel) with resumable/parallel uploads and per-session cancellation. Nothing is a parallel system — this improves the code already on
main.Frontend (
UploadPage)pendingUploads.ts), so closing the tab mid-upload resumes on reopen instead of dying. Best-effort: falls back to abeforeunloadwarning if IDB is unavailable.Backend
_tracked_run/bind_session/cancel_sessionreplace the global single-process tracking.POST /api/cancel-inference/<sid>SIGTERMs (then SIGKILLs) exactly one session's process group; other users' jobs keep running. The globalPOST /api/cancel-inferencekill-all is kept and re-implemented on the same registry.inference_jobsis mirrored totmp/<sid>/job.json(atomic write) and read via_get_inference_job, so status/viewer endpoints survive a gunicorn restart. A job leftrunning/queuedon disk by a dead process is coerced tofailed(no phantom infinite poll).queued → running → completed/failed/cancelled— anon_startcallback flipsqueued → runningat GPU acquisition and aborts a run cancelled while still queued.cancel_inference_sessionto avoid a duplicate Flask endpoint name with the existing global cancel view (would have prevented app startup).Notes
InferenceUpload.tsx(an orphaned component, imported nowhere) is left untouched.Testing
tsc -bclean · 101/101 Vitest tests pass · production build succeeds./api/inference-status/<sid>→ 404 not_found, per-session/api/cancel-inference/<sid>→ 404 for unknown, global/api/cancel-inference→ 200.