Fixed for decoder dl speed and mobile warnings on low memory devices.

This commit is contained in:
Lucas Winther committed 2026-07-29 07:47:42 +02:00
1 parent 2643a446d0
commit 78e99fc415
7 files changed
+208 -32

No files matched your search

+48 -18
View File
@@ -213,6 +213,38 @@ export function App() {
return deviceOverrides.current.get(overrideKey(forModelId, preference)) ?? preference;
}
/** Mirrors ModelPicker's size-estimate fallback: until the worker reports an
* active device, assume WASM unless the user asked for an accelerator. */
function guessDevice(preference: DevicePreference): InferenceDevice {
return (
activeDevice ??
(preference === "webgpu" ? "webgpu" : preference === "webnn" ? "webnn" : "wasm")
);
}
/**
* Whether loading `forModelId` on `device` risks crashing a mobile tab
* (D-027) — independent of whether the user has already been asked. Used
* both to decide whether to show the confirm dialog and, regardless of the
* user's answer, to decide whether the worker should load sub-model
* sessions sequentially (memory-safe but slower) or in parallel (fast,
* safe everywhere this isn't true).
*/
function isMobileLoadRisk(forModelId: string, device: InferenceDevice): boolean {
const targetModel = getModel(forModelId);
if (!targetModel) return false;
const peakBytes = estimatePeakLoadBytes(targetModel, resolveDefaultDtype(forModelId, device));
const hint: MobileMemoryHint = {
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
maxTouchPoints: typeof navigator !== "undefined" ? navigator.maxTouchPoints : undefined,
deviceMemoryGB:
typeof navigator !== "undefined"
? (navigator as Navigator & { deviceMemory?: number }).deviceMemory
: undefined,
};
return shouldWarnBeforeLoad(hint, peakBytes);
}
/**
* Confirm before a browser-engine load that risks crashing the tab on
* mobile (D-027). Returns true if the load should proceed — either it
@@ -223,25 +255,12 @@ export function App() {
function confirmRiskyMobileLoad(forModelId: string, preference: DevicePreference): boolean {
const targetModel = getModel(forModelId);
if (!targetModel) return true;
// Mirrors ModelPicker's size-estimate fallback: until the worker reports
// an active device, assume WASM unless the user asked for an accelerator.
const device: InferenceDevice =
activeDevice ??
(preference === "webgpu" ? "webgpu" : preference === "webnn" ? "webnn" : "wasm");
const device = guessDevice(preference);
const key = overrideKey(forModelId, device);
if (mobileWarningAcknowledged.current.has(key)) return true;
if (!isMobileLoadRisk(forModelId, device)) return true;
const peakBytes = estimatePeakLoadBytes(targetModel, resolveDefaultDtype(forModelId, device));
const hint: MobileMemoryHint = {
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : undefined,
maxTouchPoints: typeof navigator !== "undefined" ? navigator.maxTouchPoints : undefined,
deviceMemoryGB:
typeof navigator !== "undefined"
? (navigator as Navigator & { deviceMemory?: number }).deviceMemory
: undefined,
};
if (!shouldWarnBeforeLoad(hint, peakBytes)) return true;
const proceed = window.confirm(
`${targetModel.displayName} needs roughly ${formatModelSize(peakBytes)} of memory at ` +
"once to load — more than many phones and tablets allow a browser tab to use " +
@@ -379,6 +398,7 @@ export function App() {
type: "load",
modelId: message.modelId,
device: message.device,
sequentialSessionLoad: isMobileLoadRisk(message.modelId, message.device),
});
break;
}
@@ -417,10 +437,16 @@ export function App() {
setLoadStarted(true);
setError(null);
newLoadGate();
const device = effectiveDevice(nextModelId, preference);
postToWorker(worker, {
type: "load",
modelId: nextModelId,
device: effectiveDevice(nextModelId, preference),
device,
// D-027/D-029: sub-model sessions load sequentially (memory-safe, but
// slower — halves the CDN concurrency a fast connection could otherwise
// use) only where the peak-memory crash risk is real; everywhere else
// (desktop, low-risk models) the worker loads them in parallel.
sequentialSessionLoad: isMobileLoadRisk(nextModelId, guessDevice(device)),
});
return worker;
}
@@ -540,10 +566,12 @@ export function App() {
if (!loadStarted) {
startLoad(modelId, devicePreference);
} else {
const device = effectiveDevice(modelId, devicePreference);
postToWorker(workerRef.current!, {
type: "load",
modelId,
device: effectiveDevice(modelId, devicePreference),
device,
sequentialSessionLoad: isMobileLoadRisk(modelId, guessDevice(device)),
});
}
}
@@ -788,10 +816,12 @@ export function App() {
if (!loadStarted) {
startLoad(modelId, devicePreference);
} else {
const device = effectiveDevice(modelId, devicePreference);
postToWorker(worker, {
type: "load",
modelId,
device: effectiveDevice(modelId, devicePreference),
device,
sequentialSessionLoad: isMobileLoadRisk(modelId, guessDevice(device)),
});
}