Merge pull request #1754 from debpalash/fix/backend-startup-budget-1749

fix(frontend): align backend startup stall budget
This commit is contained in:
Palash Debnath
2026-09-02 03:28:13 +05:30
committed by GitHub
3 changed files with 26 additions and 2 deletions
+1
View File
@@ -28,6 +28,7 @@ the frozen-backend fallback mirror it for their toolchains.
### Fixed
- The setup splash now waits through the backend's full startup budget instead of reporting slow Windows CUDA initialization as stuck after two minutes (#1749)
- Dubbing jobs can now reuse every source-language code produced by automatic ASR detection without a 400 error on the next upload (#1737)
- Incomplete Sherpa-ONNX model snapshots now self-repair before recognizer startup instead of failing on a missing ONNX file (#1733)
- OmniVoice subprocess startup now allows slow packaged Windows Python runtimes to signal readiness before termination (#1711)
+6 -1
View File
@@ -938,7 +938,12 @@ export function useBootstrapStage(pollMs = 1000) {
// person can leave.
const stallBudgetMs = (stage) => {
if (stage === 'awaiting_setup') return Infinity;
return stage === 'installing_deps' ? 20 * 60 * 1000 : 120 * 1000;
if (stage === 'installing_deps') return 20 * 60 * 1000;
// Rust owns the backend launch and waits up to five minutes so slow
// torch/CUDA imports can finish. Keep the splash alive beyond that
// window; otherwise it reports a false failure at two minutes while
// the supervised backend is still healthy and making progress (#1749).
return stage === 'starting_backend' ? 6 * 60 * 1000 : 120 * 1000;
};
const invoke = async () => {
try {
@@ -102,7 +102,7 @@ describe('useBootstrapStage — awaiting_setup is human-gated (#1376)', () => {
expect(result.current.stage).toBe('installing_deps');
});
it('a machine-owned stage that genuinely wedges still fails', async () => {
it('lets Rust finish its backend startup budget before declaring a wedge', async () => {
// The other half of the contract. Exempting awaiting_setup must not
// disarm the stall detector for stages nothing but the app can advance
// that would trade this bug for the info-less infinite spinner (#879).
@@ -114,10 +114,28 @@ describe('useBootstrapStage — awaiting_setup is human-gated (#1376)', () => {
await act(async () => {});
expect(result.current.stage).toBe('starting_backend');
// The shell waits five minutes for slow torch/CUDA imports. The splash
// must not replace that live launch with its old two-minute false failure.
await act(async () => {
await vi.advanceTimersByTimeAsync(3 * 60 * 1000);
});
expect(result.current.stage).toBe('starting_backend');
expect(result.current.message ?? '').not.toMatch(/stuck/i);
// Pin both sides of the six-minute fallback boundary.
await act(async () => {
await vi.advanceTimersByTimeAsync(3 * 60 * 1000 - 1_000);
});
expect(result.current.stage).toBe('starting_backend');
expect(result.current.message ?? '').not.toMatch(/stuck/i);
// A frontend-only deadlock remains bounded if the shell never advances.
await act(async () => {
await vi.advanceTimersByTimeAsync(2_000);
});
expect(result.current.stage).toBe('failed');
expect(result.current.message).toMatch(/stuck/i);
});