Download src/worker.js from hsienberg/browser-symptom-pattern-explorer-template: direct link, hf CLI and curl.
- Browser
- Download file 2.64 kB
-
https://huggingface.co/spaces/hsienberg/browser-symptom-pattern-explorer-template/resolve/main/src/worker.js
- Command line
-
hf download hf://spaces/hsienberg/browser-symptom-pattern-explorer-template/src/worker.js
-
curl -L -o worker.js https://huggingface.co/spaces/hsienberg/browser-symptom-pattern-explorer-template/resolve/main/src/worker.js
2.64 kB
| import { | |
| env, | |
| ModelRegistry, | |
| pipeline, | |
| } from "https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.2.0"; | |
| import { CONDITION_CARDS, MODEL_ID } from "./core.js"; | |
| env.useBrowserCache = true; | |
| env.useWasmCache = true; | |
| let extractor; | |
| let cardVectors; | |
| let runtime; | |
| function dot(first, second) { | |
| return first.reduce( | |
| (sum, value, index) => sum + value * second[index], | |
| 0, | |
| ); | |
| } | |
| async function loadExtractor() { | |
| if (extractor) return extractor; | |
| runtime = { | |
| device: "wasm", | |
| dtype: "q8", | |
| }; | |
| let totalBytes = null; | |
| try { | |
| const files = await ModelRegistry.get_pipeline_files( | |
| "feature-extraction", | |
| MODEL_ID, | |
| runtime, | |
| ); | |
| totalBytes = files.reduce((sum, file) => sum + (file.size ?? 0), 0); | |
| } catch { | |
| // File introspection is helpful but must not prevent model loading. | |
| } | |
| self.postMessage({ type: "loading", runtime, totalBytes }); | |
| const started = performance.now(); | |
| extractor = await pipeline( | |
| "feature-extraction", | |
| MODEL_ID, | |
| { | |
| ...runtime, | |
| progress_callback: (event) => { | |
| if (event.status === "progress_total") { | |
| self.postMessage({ | |
| type: "progress", | |
| progress: event.progress, | |
| }); | |
| } | |
| }, | |
| }, | |
| ); | |
| const cardOutput = await extractor( | |
| CONDITION_CARDS.map( | |
| (card) => `${card.label}: ${card.description}`, | |
| ), | |
| { pooling: "mean", normalize: true }, | |
| ); | |
| cardVectors = cardOutput.tolist(); | |
| self.postMessage({ | |
| type: "ready", | |
| runtime, | |
| loadMs: Math.round(performance.now() - started), | |
| totalBytes, | |
| }); | |
| return extractor; | |
| } | |
| self.addEventListener("message", async (event) => { | |
| if (event.data?.type !== "analyze") return; | |
| try { | |
| const pipe = await loadExtractor(); | |
| const started = performance.now(); | |
| const queryOutput = await pipe(event.data.text, { | |
| pooling: "mean", | |
| normalize: true, | |
| }); | |
| const query = queryOutput.tolist()[0]; | |
| const ranked = CONDITION_CARDS.map((card, index) => ({ | |
| label: card.label, | |
| score: dot(query, cardVectors[index]), | |
| })).sort((first, second) => second.score - first.score); | |
| const result = { | |
| labels: ranked.map((row) => row.label), | |
| scores: ranked.map((row) => row.score), | |
| }; | |
| self.postMessage({ | |
| type: "result", | |
| requestId: event.data.requestId, | |
| result, | |
| runtime, | |
| inferenceMs: Math.round(performance.now() - started), | |
| }); | |
| } catch (error) { | |
| self.postMessage({ | |
| type: "error", | |
| requestId: event.data.requestId, | |
| message: error instanceof Error ? error.message : String(error), | |
| }); | |
| } | |
| }); | |