fix: upscale tool times out on CPU-only systems (NAS/low-power hardware)

The upscale function called runPythonWithProgress without a timeout parameter,
defaulting to the bridge's 10-minute hard limit. On CPU-only systems like
Synology NAS devices, Real-ESRGAN 4x upscaling easily exceeds this for modest
images. Additionally, when the timeout fired on the dispatcher path, the Python
process was left running and blocked all subsequent AI operations.

This fix adds an adaptive timeout based on input megapixels, scale factor, and
GPU availability (180s/effective-MP on CPU, 30s/effective-MP on GPU, floor of
10 minutes). It also kills the dispatcher on timeout so subsequent requests can
proceed via a fresh restart.

Closes #119
This commit is contained in:
SnapOtter
2026-05-05 21:14:56 +08:00
parent 7ff100c4f9
commit f856c26fcb
4 changed files with 174 additions and 3 deletions
+19
View File
@@ -1557,6 +1557,25 @@ describe("bridge - dispatcher request timeout", () => {
await expect(promise).rejects.toThrow("Python script timed out");
vi.useRealTimers();
});
it("kills the dispatcher on timeout so subsequent requests can proceed", async () => {
vi.useFakeTimers();
const mock = createMockProcess();
vi.mocked(spawn).mockReturnValue(mock.process);
const initPromise = initDispatcher();
mock.stderr.emit("data", Buffer.from('{"ready": true, "gpu": false}\n'));
vi.advanceTimersByTime(100);
await initPromise;
const promise = runPythonWithProgress("stuck.py", [], { timeout: 2000 });
vi.advanceTimersByTime(3000);
await expect(promise).rejects.toThrow("Python script timed out");
expect(mock.process.kill).toHaveBeenCalledWith("SIGTERM");
vi.useRealTimers();
});
});
// ── Max consecutive crash threshold ─────────────────────────────────