Start on live transcription and python sidecar (faster-whisper)

This commit is contained in:
Lucas Winther committed 2026-07-10 09:28:42 +02:00
1 parent 15bf14728a
commit 033ea32968
14 files changed
+1376 -61

No files matched your search

+10 -2
View File
@@ -194,6 +194,7 @@ async function transcribe(message: TranscribeMessage): Promise<void> {
type: "error",
stage: "transcribe",
message: "No model loaded — send a \"load\" message first.",
requestId: message.requestId,
});
return;
}
@@ -204,7 +205,8 @@ async function transcribe(message: TranscribeMessage): Promise<void> {
asr.tokenizer as ConstructorParameters<typeof WhisperTextStreamer>[0],
{
skip_prompt: true,
callback_function: (text: string) => post({ type: "partial", text }),
callback_function: (text: string) =>
post({ type: "partial", text, requestId: message.requestId }),
},
);
@@ -233,6 +235,7 @@ async function transcribe(message: TranscribeMessage): Promise<void> {
type: "result",
text: (single.text ?? "").trim(),
chunks: single.chunks ?? [],
requestId: message.requestId,
};
post(result);
}
@@ -255,7 +258,12 @@ scope.addEventListener("message", (event) => {
break;
case "transcribe":
transcribe(data).catch((error: unknown) => {
post({ type: "error", stage: "transcribe", message: errorMessage(error) });
post({
type: "error",
stage: "transcribe",
message: errorMessage(error),
requestId: data.requestId,
});
});
break;
}
+27 -5
View File
@@ -61,6 +61,12 @@ export interface TranscribeMessage {
language?: string;
/** Only meaningful for multilingual models. Defaults to "transcribe". */
task?: WhisperTask;
/**
* Optional correlation id, echoed on the partial/result/error messages this
* request produces (D-016). The live-transcription loop uses it to match
* responses to segment passes; the file flow omits it and is unchanged.
*/
requestId?: string;
}
export type UiToWorkerMessage = LoadMessage | TranscribeMessage;
@@ -96,6 +102,8 @@ export type LoadProgressMessage =
export interface PartialMessage {
type: "partial";
text: string;
/** Echo of the originating transcribe request's id, if it carried one. */
requestId?: string;
}
/** One timestamped segment of the final transcript. */
@@ -109,12 +117,16 @@ export interface ResultMessage {
type: "result";
text: string;
chunks: TranscriptChunk[];
/** Echo of the originating transcribe request's id, if it carried one. */
requestId?: string;
}
export interface ErrorMessage {
type: "error";
stage: "load" | "transcribe";
message: string;
/** Echo of the failing transcribe request's id, if it carried one. */
requestId?: string;
}
export type WorkerToUiMessage =
@@ -131,6 +143,10 @@ function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function isOptionalString(value: unknown): value is string | undefined {
return value === undefined || typeof value === "string";
}
const DEVICES: readonly string[] = ["webgpu", "wasm"];
const DTYPES: readonly string[] = ["fp32", "fp16", "q8", "q4"];
@@ -165,10 +181,11 @@ export function isUiToWorkerMessage(value: unknown): value is UiToWorkerMessage
case "transcribe":
return (
value.audio instanceof Float32Array &&
(value.language === undefined || typeof value.language === "string") &&
isOptionalString(value.language) &&
(value.task === undefined ||
value.task === "transcribe" ||
value.task === "translate")
value.task === "translate") &&
isOptionalString(value.requestId)
);
default:
return false;
@@ -197,13 +214,18 @@ export function isWorkerToUiMessage(value: unknown): value is WorkerToUiMessage
return false;
}
case "partial":
return typeof value.text === "string";
return typeof value.text === "string" && isOptionalString(value.requestId);
case "result":
return typeof value.text === "string" && Array.isArray(value.chunks);
return (
typeof value.text === "string" &&
Array.isArray(value.chunks) &&
isOptionalString(value.requestId)
);
case "error":
return (
typeof value.message === "string" &&
(value.stage === "load" || value.stage === "transcribe")
(value.stage === "load" || value.stage === "transcribe") &&
isOptionalString(value.requestId)
);
default:
return false;