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>
39 lines
1.6 KiB
TypeScript
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();
|