Files
au_whisper_web/src/lib/modelCache.ts
T
Lucas WintherandClaude Fable 5 9f39a82982 WIP: M3 PWA/offline implementation, M4 auth (backend + frontend), D-013 faster-whisper
- M3: hand-rolled service worker (precache + runtime /ort caching), precache
  manifest injection, SW registration/update toast, manifest + generated icons,
  production build script, offline indicator in ModelPicker (D-005/D-007)
- M4 backend: bun:sqlite users/sessions (D-009), Passport local strategy behind
  connect-compat adapter (D-008), /api/auth routes wired into dev+prod serving,
  Bun.password Argon2id, 38 auth tests
- M4 frontend: AuthForms + useAuth + framework-free authClient, session-aware
  header, anonymous-first fail-silent offline handling, 18 client tests
- D-013: server engine (M5) will use faster-whisper via Python sidecar;
  browser side stays Transformers.js (CTranslate2 has no WASM build)
- bun test: 83 pass / tsc --noEmit clean
- Note: M3 doc closeout (WorkBreakdown/Architecture/tests/offline.md) still in
  flight from a background agent; follow-up commit will finish docs

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-10 04:02:28 +02:00

61 lines
2.1 KiB
TypeScript

/**
* "Is this model already downloaded?" helper (Decisions.md D-005).
*
* Transformers.js v3 persists Hub downloads in the browser Cache API (Cache
* Storage, cache name "transformers-cache"). We inspect the cached request
* URLs and match them against catalog model ids. Used by `ModelPicker` for the
* downloaded badge (M2) and reused by the offline indicator (M3).
*
* `matchCachedModels` is pure so it can be unit-tested; only
* `getDownloadedModels` touches the `caches` global.
*/
/** Cache name used by Transformers.js v3 (`env.cacheName` default). */
export const TRANSFORMERS_CACHE_NAME = "transformers-cache";
/**
* Pure matcher: which of `modelIds` have at least one cached ONNX weight file?
*
* Cached URLs look like
* `https://huggingface.co/<modelId>/resolve/main/onnx/<file>.onnx`. We require
* an `.onnx` hit (not just tokenizer/config JSON) so a model whose weights
* never finished downloading does not show as available.
*/
export function matchCachedModels(
urls: Iterable<string>,
modelIds: readonly string[],
): Set<string> {
const downloaded = new Set<string>();
for (const url of urls) {
if (!url.split("?")[0]?.endsWith(".onnx")) continue;
for (const modelId of modelIds) {
if (!downloaded.has(modelId) && url.includes(`/${modelId}/`)) {
downloaded.add(modelId);
}
}
}
return downloaded;
}
/**
* Query the browser Cache API for models with cached weights. Returns an
* empty set where Cache Storage is unavailable (non-secure contexts, tests).
*/
export async function getDownloadedModels(
modelIds: readonly string[],
): Promise<Set<string>> {
if (typeof caches === "undefined") return new Set();
try {
const urls: string[] = [];
// Match any transformers-owned cache, in case the name is ever versioned.
for (const name of await caches.keys()) {
if (!name.includes("transformers")) continue;
const cache = await caches.open(name);
for (const request of await cache.keys()) urls.push(request.url);
}
return matchCachedModels(urls, modelIds);
} catch {
return new Set();
}
}