Resumable generation¶
Resumability is opt-in, per request. It journals a text-only request’s full prompt, generated tokens, text patches, and RNG state in same-origin OPFS. Optional KV checkpoints accelerate recovery; token replay does not require checkpoint-capable model libraries.
const sessionId = crypto.randomUUID();
const chunks = await engine.chat.completions.create({
messages,
stream: true,
extra_body: {
resumable: { enabled: true, sessionId, strictPersistence: true },
},
});
for await (const chunk of chunks) { /* display chunk */ }
// After a crash, load the SAME model/configuration before continuing.
const saved = await engine.resumeChatCompletion(sessionId);
// Replace any stale UI response with saved.recoveredText first.
const resumed = await engine.resumeChatCompletion(sessionId, {
continueGeneration: true,
});
Request and recovery behavior¶
Path |
Behavior |
|---|---|
Resumability disabled |
Normal WebLLM behavior, including in-memory conversation prefix reuse. No session journal or checkpoint is created. |
New resumable request; any number of prior turns |
Supply the full message history. The request resets and prefills that history, even if its prefix is in memory. The persisted prompt is self-contained, not a delta referring to another session. |
Another session on the same model |
Uses a new ID and waits for the current request’s model lock. Its prefill
replaces the in-memory cache; the previous session’s persisted data remains.
As in ordinary WebLLM, |
Reusing a session ID |
A new request rejects an existing ID, even after completion. Use
|
Read-only recovery |
|
Continued recovery with usable KV |
Import the newest readable committed checkpoint, rebuild output and penalties, and forward only journaled tokens beyond that checkpoint. Restore the RNG before sampling new tokens. |
KV absent, corrupt, unsupported, or import rejected |
Re-prefill the persisted prompt and forward the saved generated tokens. Saved tokens are not resampled. Restore the RNG and continue. |
No generated token committed yet |
Sample the first token from checkpoint logits, or from replayed prompt logits. A supplied request seed is restored first. |
Checkpoint has no saved logits |
KV recovery works if a journaled token follows the checkpoint. Otherwise fall back to prompt/token replay to obtain next-token logits. |
Missing model, RNG state, or replay context; already finished |
No further generation; recover saved text where possible. A missing session or malformed resumable configuration raises an error. |
Streaming continuation |
Available when both the original request and resume options use
|
Unconsumed or cancelled stream |
An unstarted direct-engine stream holds no generation/session locks.
|
Interrupt, crash, or another crash during recovery |
Continue the same unfinished session from its last valid journal prefix. An interrupted generation is resumable; a normally finished one is not. |
Concurrent tab/worker access |
One writer per session, using Web Locks or a sync-access-handle lock. Continuing/deleting an active session rejects. Without cross-context locking, persistence is disabled or fails according to strictness. |
Persistence and storage¶
durabilityMode: "exact" (default) waits for each token’s OPFS append before
exposing it. "relaxed" queues writes and waits every eight tokens or when
250 ms have elapsed at a token boundary; a crash can lose an unflushed suffix.
Neither mode protects against origin eviction, clearing browser data, or all
OS/power failures.
strictPersistence: false (default) lets generation continue after journal
failure, without further persistence. If only a KV checkpoint write fails, that
checkpoint is skipped and healthy token journaling can continue.
true makes persistence failures reject
generation, including an abort write/flush failure during explicit stream
cancellation. Unsupported KV export and a low reported storage estimate skip KV
capture but still permit token journaling; strictness does not require KV recovery.
Estimates are advisory: a browser can report headroom while enforcing a lower
quota. An actual checkpoint write failure follows the same strictness policy.
Cleanup failure after a durable finished record is logged and retried on later
session inspection, not treated as a failed generation.
Prompt checkpointing defaults to enabled. Decode checkpoints default to every
512 tokens, aligned to the cache page size. KV capture is skipped below the
storage headroom threshold (at least 512 MiB, or twice the checkpoint size).
Retention keeps the newest two payload-valid committed checkpoints; incomplete,
uncommitted, and invalid checkpoints are removed under the session lock.
Normal completion removes residual KV but retains the journal/text. Use
listResumableSessions() and deleteResumableSession(id) to manage sessions.
There is no TTL or automatic journal eviction.
Persisted data includes the full conversation and is not encrypted by WebLLM. Deletion does not clear the active model’s in-memory prefix cache. After normal or resumed completion, an ordinary non-resumable follow-up can reuse a matching in-memory conversation prefix; another resumable request always prefills anew.
Supported scope and validation¶
Supported: text-only chat completions with one choice. The legacy
completions endpoint rejects resumability. Resumable chat requests reject
images, grammar/JSON/structural-tag constraints, and custom LogitProcessors.
KV export additionally requires a compatible model library containing TVM’s
checkpoint primitives and a supported pure KV-cache layout.
Unsupported cache layouts use token replay.
Model/tokenizer/weights/configuration fingerprints are not implemented. Keep those artifacts unchanged when resuming; a matching model ID or KV layout hash alone does not verify model identity. This is not a persistent cross-request prefix cache.
Run npm ci, npm test -- --runInBand, and npm run test:browser for the
unit and model-free browser regressions. The latter exercises real OPFS, Web
Locks, and XGrammar WASM; its tensor-boundary fixtures are not GPU inference.
The opt-in real-model suite requires WebGPU with shader-f16 support, enough
storage for model weights/checkpoints, and a checkpoint-capable
Qwen3-0.6B-q4f16_1-MLC model library:
npm run build:browser-tests
WEBLLM_TEST_MODEL_LIB=https://your-host/Qwen3-0.6B.wasm \
npx playwright test --config tests/browser/playwright.config.mjs \
--headed --grep 'real WebGPU'
It compares uninterrupted seeded output with repeated page-crash recovery, with four prior messages and a prompt spanning multiple prefill chunks, separately forcing token replay and KV recovery. It covers all combinations of exact/relaxed durability and strict/best-effort persistence, completed-session inspection, ordinary prefix reuse after session deletion, and full-history prefill for a new resumable session. Opting in fails, rather than skips, if GPU inference or KV import is unavailable.
Additional real-GPU cases cover zero-token recovery at checkpoint write/commit boundaries, saved logits present or absent, decode-checkpoint retention and corruption, torn journal repair, token-write failures, cancellation, competing sessions/tabs, low browser quota, and deliberate browser-process crashes followed by relaunching the same profile. Fault hooks pause writes or inject storage errors; they do not replace inference. Unsupported requests are checked for rejection without creating sessions.
For a locally compiled model library and downloaded model directory:
WEBLLM_TEST_MODEL_LIB_PATH=/absolute/path/to/Qwen3-0.6B.wasm \
WEBLLM_TEST_MODEL_PATH=/absolute/path/to/Qwen3-0.6B-q4f16_1-MLC \
WEBLLM_TEST_BROWSER_EXECUTABLE=/absolute/path/to/chromium \
npx playwright test --config tests/browser/playwright.config.mjs
The model directory must contain the matching configuration, tokenizer files, tensor-cache manifest, and weight shards. Local files are served over loopback HTTP. Do not set both the model-library URL and local-path options.
The browser bundle uses the package-locked runtime by default. To test a TVM
source build, run WEBLLM_TEST_RUNTIME_PATH=/absolute/path/to/tvm/web npm run
build:browser-tests after building that checkout’s WASM runtime and JavaScript
package. Run without this variable to rebuild against the published runtime.
Record the TVM/MLC-LLM revisions and model-library hash alongside each result.
Each test uses a fresh persistent browser profile, retaining its OPFS contents
across page reloads. Set WEBLLM_TEST_PROFILE_ROOT to an existing directory
on a volume with ample free space if the default temporary volume is nearly
full; Chromium’s blob-storage reserve can reject large cache writes even when
the model itself would fit. On macOS, also set MAC_CHROMIUM_TMPDIR to that
volume for Chromium’s temporary files; TMPDIR alone does not redirect them.
Use Playwright’s --output option to move test output if needed.
Profiles are removed after each test. These tests
exercise browser-process and page recovery, not OS/power-loss durability.