Files
transcribe/sidecar/queue.ts
T
pastilhasandClaude Fable 5 dd3dbb611b conversations come back as conversations
A probe on the torch stage worker (~/dockers/torch-worker) runs beside every
transcription and decides what the recording is: solo dictation returns as
paragraphs exactly as before, several voices return speaker-labeled with
conversation: true. Nobody declares which — the caller cannot know.

Transcription and the worker each get a serial FIFO queue on their own
resource, so the probe costs a solo memo nothing and multi-user traffic
stays fair. The worker is optional and best-effort throughout: unreachable
or failing, everything still transcribes, just never labeled.

Speaker attribution is segment-granularity by maximal turn overlap —
whisper.cpp's own timestamps, no alignment stage. If boundaries prove
sloppy, wav2vec2 alignment returns as a worker endpoint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 04:27:14 +00:00

39 lines
1.6 KiB
TypeScript

// Two serial queues, one per compute resource, shared by every user of the platform.
//
// The whisper.cpp server and the torch worker each run one job at a time — both saturate their
// device, so concurrency inside them buys nothing. What the platform owes its users is fairness
// and calm under simultaneous requests: jobs enter here and run strictly in arrival order, and a
// slow episode ahead of you delays you but never breaks you.
//
// The two queues are deliberately independent: a probe or diarization runs on the torch worker
// WHILE someone else's transcription runs on Whisper. That is what makes the conversation check
// free for solo memos — it happens on the other resource's time.
//
// v2 (planned, not built): feature-aware ordering — short memos ahead of hour-long episodes,
// probes ahead of full diarizations. The queue object is the seam where that goes; callers will
// not change.
export class SerialQueue {
private tail: Promise<unknown> = Promise.resolve();
private depth = 0;
/** Jobs currently running or waiting. */
get pending(): number {
return this.depth;
}
run<T>(job: () => Promise<T>): Promise<T> {
this.depth += 1;
const result = this.tail.then(job).finally(() => {
this.depth -= 1;
});
// The chain itself must survive a failed job — the failure belongs to the caller who
// submitted it, not to everyone queued behind them.
this.tail = result.catch(() => undefined);
return result;
}
}
export const whisperQueue = new SerialQueue();
export const workerQueue = new SerialQueue();