Download assets/script.js from huggingworld/vectorsearch-turbo-webgpu: direct link, hf CLI and curl.
- Browser
- Download file 6.58 kB
-
https://huggingface.co/spaces/huggingworld/vectorsearch-turbo-webgpu/resolve/main/assets/script.js
- Command line
-
hf download hf://spaces/huggingworld/vectorsearch-turbo-webgpu/assets/script.js
-
curl -L -o script.js https://huggingface.co/spaces/huggingworld/vectorsearch-turbo-webgpu/resolve/main/assets/script.js
6.58 kB
| import { VectorSearch } from '/assets/vectorsearch-min.js'; | |
| // DOM references. | |
| const DB_NAME_INPUT = document.getElementById('db-name-input'); | |
| const DB_SELECT = document.getElementById('db-select'); | |
| const STATUS_EL = document.getElementById('status'); | |
| const QUERY_EMBEDDING_TEXT = document.getElementById('query-embedding-text'); | |
| const QUERY_TOKENS_OUTPUT = document.getElementById('query-tokens-output'); | |
| const QUERY_EMBEDDING_VIZ = document.getElementById('query-embedding-viz'); | |
| const BEST_MATCH_EMBEDDING_VIZ = document.getElementById('best-match-embedding-viz'); | |
| const BEST_MATCH_EMBEDDING_TEXT = document.getElementById('best-match-embedding-text'); | |
| const INPUT_TEXT = document.getElementById('input-text'); | |
| const TARGET_TEXT = document.getElementById('target-text'); | |
| const STORE_BTN = document.getElementById('store-btn'); | |
| const PREDICT_BTN = document.getElementById('predict-btn'); | |
| const THRESHOLD_INPUT = document.getElementById('threshold-input'); | |
| const THRESHOLD_VALUE = document.getElementById('threshold-value'); | |
| const RESULTS_TEXT = document.getElementById('results-text'); | |
| const SIMILARITY_CONTAINER = document.getElementById('similarity-container'); | |
| const SIMILARITY_SCORE_EL = document.getElementById('similarity-score'); | |
| const SIMILARITY_LABEL_EL = document.getElementById('similarity-label'); | |
| // Embedding Model Configuration. | |
| const MODEL_RUNTIME = 'transformersjs'; // OPTIONS 'transformersjs' OR 'litertjs' | |
| const MODEL_URL = 'huggingworld/all-MiniLM-L6-v2'; // OPTIONS 'Xenova/all-MiniLM-L6-v2' if transformersjs runtime or model/embeddinggemma-300M_seq1024_mixed-precision.tflite if litertjs runtime | |
| const SEQ_LENGTH = 1024; | |
| const TOKENIZER = 'huggingworld/embeddinggemma-300m-ONNX'; | |
| const EMBEDDING_MODEL_CONFIG = { | |
| runtime: MODEL_RUNTIME, | |
| url: MODEL_URL, | |
| sequenceLength: SEQ_LENGTH, | |
| tokenizer: TOKENIZER | |
| }; | |
| // Instantiate VectorSearch Master Class. | |
| const VECTOR_SEARCH = new VectorSearch(EMBEDDING_MODEL_CONFIG); | |
| async function predictBtnClickHandler() { | |
| const QUERY_TEXT_VALUE = TARGET_TEXT.value; | |
| const THRESHOLD = parseFloat(THRESHOLD_INPUT.value) || 0.5; | |
| const SELECTED_DB = DB_SELECT.value; | |
| if (QUERY_TEXT_VALUE && SELECTED_DB) { | |
| VECTOR_SEARCH.setDb(SELECTED_DB); | |
| PREDICT_BTN.disabled = true; | |
| STATUS_EL.innerText = `Searching VectorDB (${SELECTED_DB})...`; | |
| const t0 = performance.now(); | |
| await predict(QUERY_TEXT_VALUE, THRESHOLD); | |
| const t1 = performance.now(); | |
| console.log(`Total search time (query embedding + vector search) took ${t1 - t0} milliseconds.`); | |
| STATUS_EL.innerText = 'Search complete'; | |
| PREDICT_BTN.disabled = false; | |
| } | |
| } | |
| async function storeBtnClickHandler() { | |
| const text = INPUT_TEXT.value.trim(); | |
| const dbName = DB_NAME_INPUT.value.trim(); | |
| if (!text || !dbName) return; | |
| STORE_BTN.disabled = true; | |
| const paragraphs = text.split(/\n\s*\n/).map(p => p.trim()).filter(p => p.length > 0); | |
| await VECTOR_SEARCH.storeTexts(paragraphs, dbName, STATUS_EL); | |
| STATUS_EL.innerText = `Stored ${paragraphs.length} paragraphs.`; | |
| STORE_BTN.disabled = false; | |
| INPUT_TEXT.value = ''; | |
| await updateDbList(); | |
| } | |
| async function load() { | |
| try { | |
| await updateDbList(); | |
| // Actually load the runtime and model so ready to use. | |
| await VECTOR_SEARCH.load(STATUS_EL); | |
| STATUS_EL.innerText = 'Ready to store and search'; | |
| STORE_BTN.disabled = false; | |
| PREDICT_BTN.disabled = false; | |
| STORE_BTN.addEventListener('click', storeBtnClickHandler); | |
| PREDICT_BTN.addEventListener('click', predictBtnClickHandler); | |
| THRESHOLD_INPUT.addEventListener('input', () => { | |
| THRESHOLD_VALUE.innerText = THRESHOLD_INPUT.value; | |
| }); | |
| } catch (e) { | |
| console.error(e); | |
| STATUS_EL.innerText = 'Error: ' + e.message; | |
| } | |
| } | |
| async function predict(queryText, threshold) { | |
| // Visualize embeddings and tokens for the search query text. | |
| const { embedding: EMBEDDING_DATA, tokens: TOKENS } = await VECTOR_SEARCH.getEmbedding(queryText); | |
| if (TOKENS) { | |
| VECTOR_SEARCH.renderTokens(TOKENS, QUERY_TOKENS_OUTPUT); | |
| } | |
| await VECTOR_SEARCH.renderEmbedding(EMBEDDING_DATA, QUERY_EMBEDDING_VIZ, QUERY_EMBEDDING_TEXT); | |
| // Now actually search the vector database. | |
| const { results: RESULTS, bestScore: BEST_SCORE, bestIndex: BEST_INDEX } = await VECTOR_SEARCH.search(EMBEDDING_DATA, threshold, DB_SELECT.value); | |
| if (RESULTS.length > 0) { | |
| RESULTS_TEXT.value = RESULTS.map(m => `[Score: ${m.score.toFixed(4)}]\n${m.text}`).join('\n\n'); | |
| updateSimilarityUI(BEST_SCORE); | |
| const bestMatchVector = RESULTS[BEST_INDEX].vector; | |
| if (bestMatchVector) { | |
| await VECTOR_SEARCH.renderEmbedding(bestMatchVector, BEST_MATCH_EMBEDDING_VIZ, BEST_MATCH_EMBEDDING_TEXT); | |
| } | |
| } else { | |
| RESULTS_TEXT.value = "No matches found above threshold."; | |
| SIMILARITY_CONTAINER.classList.add('hidden'); | |
| BEST_MATCH_EMBEDDING_VIZ.innerHTML = ''; | |
| BEST_MATCH_EMBEDDING_TEXT.innerText = ''; | |
| } | |
| } | |
| function updateSimilarityUI(score) { | |
| SIMILARITY_CONTAINER.classList.remove('hidden'); | |
| SIMILARITY_SCORE_EL.innerText = score.toFixed(4); | |
| const HUE = Math.max(0, Math.min(120, score * 120)); | |
| const BACKGROUND_COLOUR = `hsla(${HUE}, 70%, 20%, 0.4)`; | |
| const BORDER_COLOUR = `hsla(${HUE}, 70%, 50%, 0.6)`; | |
| SIMILARITY_CONTAINER.style.backgroundColor = BACKGROUND_COLOUR; | |
| SIMILARITY_CONTAINER.style.borderColor = BORDER_COLOUR; | |
| let label = 'Low Similarity'; | |
| if (score > 0.8) { | |
| label = 'Very High Similarity'; | |
| } else if (score > 0.6) { | |
| label = 'High Similarity'; | |
| } else if (score > 0.4) { | |
| label = 'Moderate Similarity'; | |
| } | |
| SIMILARITY_LABEL_EL.innerText = label; | |
| } | |
| async function updateDbList() { | |
| if (!window.indexedDB.databases) { | |
| console.warn('indexedDB.databases() is not supported in this browser.'); | |
| return; | |
| } | |
| try { | |
| const dbs = await window.indexedDB.databases(); | |
| const currentSelection = DB_SELECT.value; | |
| DB_SELECT.innerHTML = ''; | |
| const currentInputName = DB_NAME_INPUT.value.trim(); | |
| let names = dbs.map(db => db.name).filter(name => name !== undefined); | |
| if (currentInputName && !names.includes(currentInputName)) { | |
| names.push(currentInputName); | |
| } | |
| names.sort(); | |
| names.forEach(name => { | |
| const option = document.createElement('option'); | |
| option.value = name; | |
| option.text = name; | |
| if (name === currentSelection || (currentSelection === '' && name === currentInputName)) { | |
| option.selected = true; | |
| } | |
| DB_SELECT.appendChild(option); | |
| }); | |
| } catch (e) { | |
| console.error('Error fetching databases:', e); | |
| } | |
| } | |
| load(); | |