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 <noreply@anthropic.com>
This commit is contained in:
1 parent
eb72a2e646
commit
9f39a82982
38 files changed
+3857
-246
No files matched your search
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* "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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user