feat: whisper-side cancellation + SSE-triggered retry
All checks were successful
Build & Push Docker Image / build-and-push (push) Successful in 48s

- Add cancelJob() to whisper.ts: sends DELETE /jobs/:id to the whisper
  server (best-effort, errors silently ignored)
- DELETE /api/jobs/[id] now calls cancelJob() when cancelling an active
  job that has a whisperJobId, stopping GPU use immediately
- Webhook handler guards against locally-cancelled jobs: returns ok early
  so whisper's late completion cannot overwrite cancelled status or send
  a phantom 'Transcript ready' notification
- Replace blind sleep(Retry-After + 1s) in submitJob() with
  waitForModelReady(): subscribes to /model/events SSE and proceeds as
  soon as state:ready arrives; falls back to the Retry-After timeout if
  SSE is unreachable or closes without model_ready
- Refactor retry tests to use URL-aware makeJobFetch() helper; add 7 new
  tests (3 SSE-triggered retry, 3 cancelJob, 1 webhook cancelled-guard)
  — 144/144 passing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Giancarmine Salucci
2026-05-09 00:40:40 +02:00
parent 01845bec25
commit 04142b17a8
5 changed files with 267 additions and 73 deletions

View File

@@ -1,5 +1,6 @@
import { json, error } from '@sveltejs/kit';
import { getJob, setJobStatus, deleteJob } from '$lib/server/db.js';
import { cancelJob } from '$lib/server/whisper.js';
import { rm } from 'fs/promises';
export async function GET({ params }) {
@@ -17,6 +18,8 @@ export async function DELETE({ params }) {
if (ACTIVE.has(job.status)) {
// Cancel active job (keeps DB record)
setJobStatus(params.id, 'cancelled', 0);
// Best-effort: tell whisper to drop the queued job so it stops using GPU
if (job.whisperJobId) cancelJob(job.whisperJobId).catch(() => {});
} else {
// Hard-delete terminal job + clean up output files
deleteJob(params.id);

View File

@@ -12,6 +12,11 @@ const jobId = params.jobId;
const job = getJob(jobId);
if (!job) throw error(404, 'Job not found');
// Discard the result if the job was cancelled locally while whisper was running
if (job.status === 'cancelled') {
return json({ ok: true });
}
const whisperJob = (await request.json()) as WhisperJob;
if (whisperJob.status === 'failed' || whisperJob.status === 'cancelled') {