Add cancel + progress UI, EngineToggle, and Docker deployment
This commit is contained in:
@@ -77,6 +77,44 @@ describe("BrowserEngine", () => {
|
||||
expect("modelId" in message).toBe(false);
|
||||
});
|
||||
|
||||
test("asks the worker to stream only when a consumer listens", () => {
|
||||
const worker = new FakeWorker();
|
||||
const engine = new BrowserEngine(worker);
|
||||
void engine.transcribe(new Float32Array(4));
|
||||
void engine.transcribe(new Float32Array(4), { onPartial: () => {} });
|
||||
void engine.transcribe(new Float32Array(4), { onProgress: () => {} });
|
||||
expect("stream" in worker.posted[0]!.message).toBe(false);
|
||||
expect(worker.posted[1]!.message.stream).toBe(true);
|
||||
expect(worker.posted[2]!.message.stream).toBe(true);
|
||||
});
|
||||
|
||||
test("routes transcribe-progress to onProgress, ignoring other ids", async () => {
|
||||
const worker = new FakeWorker();
|
||||
const engine = new BrowserEngine(worker);
|
||||
const updates: { processedSeconds: number; totalSeconds: number }[] = [];
|
||||
const promise = engine.transcribe(new Float32Array(4), {
|
||||
onProgress: (progress) => updates.push(progress),
|
||||
});
|
||||
const requestId = worker.lastRequestId();
|
||||
|
||||
worker.emit({ type: "transcribe-progress", processedSeconds: 5, totalSeconds: 60 });
|
||||
worker.emit({
|
||||
type: "transcribe-progress",
|
||||
processedSeconds: 7,
|
||||
totalSeconds: 60,
|
||||
requestId: "other",
|
||||
});
|
||||
worker.emit({ type: "transcribe-progress", processedSeconds: 0, totalSeconds: 90, requestId });
|
||||
worker.emit({ type: "transcribe-progress", processedSeconds: 30, totalSeconds: 90, requestId });
|
||||
worker.emit({ type: "result", text: "done", chunks: [], requestId });
|
||||
|
||||
expect((await promise).text).toBe("done");
|
||||
expect(updates).toEqual([
|
||||
{ processedSeconds: 0, totalSeconds: 90 },
|
||||
{ processedSeconds: 30, totalSeconds: 90 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("resolves on the matching result and routes partials to onPartial", async () => {
|
||||
const worker = new FakeWorker();
|
||||
const engine = new BrowserEngine(worker);
|
||||
@@ -265,6 +303,45 @@ describe("ServerEngine", () => {
|
||||
engine.transcribe(new Float32Array(4), { modelId: "onnx-community/whisper-tiny" }),
|
||||
).rejects.toThrow(/could not reach the server/i);
|
||||
});
|
||||
|
||||
test("passes the abort signal to fetch and rethrows AbortError as-is", async () => {
|
||||
// Cancel (D-018 amendment): the UI aborts the controller; the rejection
|
||||
// must stay an AbortError, not be rewrapped as a network failure.
|
||||
let seenSignal: AbortSignal | undefined;
|
||||
const engine = new ServerEngine(async (_url, init) => {
|
||||
seenSignal = init?.signal ?? undefined;
|
||||
throw new DOMException("The user aborted a request.", "AbortError");
|
||||
});
|
||||
const controller = new AbortController();
|
||||
const promise = engine.transcribe(new Float32Array(4), {
|
||||
modelId: "onnx-community/whisper-tiny",
|
||||
signal: controller.signal,
|
||||
});
|
||||
expect(promise).rejects.toMatchObject({ name: "AbortError" });
|
||||
await promise.catch(() => {});
|
||||
expect(seenSignal).toBe(controller.signal);
|
||||
});
|
||||
|
||||
test("an abort mid-stream surfaces as the stream's rejection", () => {
|
||||
// Once the 200 arrived, an abort tears down the body stream; whatever
|
||||
// error the reader throws must propagate out of transcribe().
|
||||
const body = new ReadableStream<Uint8Array>({
|
||||
start(streamController) {
|
||||
streamController.enqueue(
|
||||
new TextEncoder().encode(
|
||||
`${JSON.stringify({ type: "segment", text: " a", start: 0, end: 1 })}\n`,
|
||||
),
|
||||
);
|
||||
streamController.error(
|
||||
new DOMException("The user aborted a request.", "AbortError"),
|
||||
);
|
||||
},
|
||||
});
|
||||
const engine = new ServerEngine(async () => new Response(body, { status: 200 }));
|
||||
expect(
|
||||
engine.transcribe(new Float32Array(4), { modelId: "onnx-community/whisper-tiny" }),
|
||||
).rejects.toMatchObject({ name: "AbortError" });
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user