UNPKG

closevector-hnswlib-wasm

Version:

typescript and wasm bindings for Hnswlib

220 lines (190 loc) 8.46 kB
/***************** GENERATED FILE ********************/ import { resolve } from 'path'; import type * as module from './hnswlib-wasm'; import type factory from './hnswlib-wasm'; // import './hnswlib.mjs'; export type HierarchicalNSW = module.HierarchicalNSW; export type BruteforceSearch = module.BruteforceSearch; export type EmscriptenFileSystemManager = module.EmscriptenFileSystemManager; export type L2Space = module.L2Space; export type InnerProductSpace = module.InnerProductSpace; export type HnswModuleFactory = typeof factory; export type normalizePoint = HnswlibModule['normalizePoint']; export const IDBFS_STORE_NAME = 'FILE_DATA'; export * from './constants'; export interface HnswlibModule extends Omit<EmscriptenModule, '_malloc' | '_free'> { normalizePoint(vec: number[]): number[]; L2Space: typeof module.L2Space; InnerProductSpace: typeof module.InnerProductSpace; BruteforceSearch: typeof module.BruteforceSearch; HierarchicalNSW: typeof module.HierarchicalNSW; EmscriptenFileSystemManager: typeof module.EmscriptenFileSystemManager; asm: { malloc(size: number): number; free(ptr: number): void; }; } let library: Awaited<HnswlibModule>; type InputFsType = 'IDBFS' | undefined; export const syncFileSystem = (action: 'read' | 'write'): Promise<void> => { const EmscriptenFileSystemManager: HnswlibModule['EmscriptenFileSystemManager'] = library.EmscriptenFileSystemManager; const syncAction = action === 'read' ? true : action === 'write' ? false : undefined; if (syncAction === undefined) throw new Error('Invalid action type'); return new Promise((resolve, reject) => { try { EmscriptenFileSystemManager.syncFS(syncAction, () => { resolve(); }); } catch (error) { reject(error); } }); }; export const waitForFileSystemInitalized = (): Promise<void> => { const EmscriptenFileSystemManager: HnswlibModule['EmscriptenFileSystemManager'] = library.EmscriptenFileSystemManager; return new Promise((resolve, reject) => { let totalWaitTime = 0; const checkInterval = 100; // Check every 100ms const maxWaitTime = 4000; // Maximum wait time of 4 seconds const checkInitialization = () => { if (EmscriptenFileSystemManager.isInitialized()) { resolve(); } else if (totalWaitTime >= maxWaitTime) { reject(new Error('Failed to initialize filesystem')); } else { totalWaitTime += checkInterval; setTimeout(checkInitialization, checkInterval); } }; setTimeout(checkInitialization, checkInterval); }); }; export const waitForFileSystemSynced = (): Promise<void> => { const EmscriptenFileSystemManager = library.EmscriptenFileSystemManager; return new Promise((resolve, reject) => { let totalWaitTime = 0; const checkInterval = 100; // Check every 100ms const maxWaitTime = 4000; // Maximum wait time of 4 seconds const checkInitialization = () => { if (EmscriptenFileSystemManager.isSynced()) { resolve(); } else if (totalWaitTime >= maxWaitTime) { reject(new Error('Failed to initialize filesystem')); } else { totalWaitTime += checkInterval; setTimeout(checkInitialization, checkInterval); } }; setTimeout(checkInitialization, checkInterval); }); }; /** * Initializes the file system for the HNSW library using the specified file system type. * If no file system type is specified, IDBFS is used by default. * @param inputFsType The type of file system to use. Can be 'IDBFS' or undefined. * @returns A promise that resolves when the file system is initialized, or rejects if initialization fails. */ const initializeFileSystemAsync = async (inputFsType?: InputFsType): Promise<void> => { const fsType = inputFsType == null ? 'IDBFS' : inputFsType; const EmscriptenFileSystemManager = library.EmscriptenFileSystemManager; if (EmscriptenFileSystemManager.isInitialized()) { return; } EmscriptenFileSystemManager.initializeFileSystem(fsType); return await waitForFileSystemInitalized(); }; /** * Load the HNSW library in node or browser */ let promiseCalling: Promise<HnswlibModule> | undefined = undefined; export const loadHnswlib = async (inputFsType?: InputFsType): Promise<HnswlibModule> => { if (promiseCalling) { return promiseCalling; } promiseCalling = new Promise<HnswlibModule>((resolve, reject) => { (async () => { try { // @ts-expect-error - hnswlib can be a global variable in the browser if (typeof hnswlib !== 'undefined' && hnswlib !== null) { // @ts-expect-error - hnswlib can be a global variable in the browser const lib = hnswlib(); if (lib != null) return lib; } if (!library) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const temp = await import('./hnswlib.mjs'); const factoryFunc = temp.default; library = await factoryFunc(); await initializeFileSystemAsync(inputFsType); return resolve(library); // Add this line } return resolve(library); } catch (err) { console.error('----------------------------------------'); console.error('Error initializing the library:', err); reject(err); } })(); promiseCalling = undefined; }); return promiseCalling; }; // disabled due to lack of perfomance improvemant and additional complexity // /** // * Adds items and their corresponding labels to the HierarchicalNSW index using memory pointers. // * This function handles the memory allocation for the Emscripten Module, and properly frees the memory after use. its a wrapper around {@link HierarchicalNSW#addItemsWithPtrs} // * // * ⛔️ This function is only 1.02x faster than vectors for 10k points version which are easier to use. The sole advantage is memory savings // * // * @async // * @param {HnswlibModule} Module - The Emscripten HNSWLIB Module object. // * @param {HierarchicalNSW} index - The HierarchicalNSW index object. // * @param {Float32Array[] | number[][]} items - An array of item vectors to be added to the search index. Each item should be a Float32Array or an array of numbers. // * @param {number[]} labels - An array of numeric labels corresponding to the items. The length of the labels array should match the length of the items array. // * @param {boolean} replaceDeleted - A flag to determine if deleted elements should be replaced (default: false). // * @returns {Promise<void>} A promise that resolves once the items and labels have been added to the index. // */ // export const addItemsWithPtrsHelper = async ( // Module: HnswlibModule, // index: HierarchicalNSW, // items: Float32Array[] | number[][], // labels: number[], // replaceDeleted: boolean // ): Promise<void> => { // const itemCount = items.length; // const dim = items[0].length; // // Flatten the items array into a Float32Array // const flatItems = new Float32Array(itemCount * dim); // items.forEach((vec, i) => { // flatItems.set(vec, i * dim); // }); // // Convert labels to a Uint32Array // const labelsArray = new Uint32Array(labels); // const vecDataPtr = Module.asm.malloc(flatItems.length * Float32Array.BYTES_PER_ELEMENT); // const labelVecDataPtr = Module.asm.malloc(labelsArray.length * Uint32Array.BYTES_PER_ELEMENT); // if (vecDataPtr === 0) { // throw new Error('Failed to allocate memory for vecDataPtr.'); // } // if (labelVecDataPtr === 0) { // throw new Error('Failed to allocate memory for labelVecDataPtr.'); // } // Module.HEAPF32.set(flatItems, vecDataPtr / Float32Array.BYTES_PER_ELEMENT); // Module.HEAPU32.set(labelsArray, labelVecDataPtr / Uint32Array.BYTES_PER_ELEMENT); // await index.addItemsWithPtr( // Module.HEAPF32.subarray( // Math.floor(vecDataPtr / Float32Array.BYTES_PER_ELEMENT), // Math.floor(vecDataPtr / Float32Array.BYTES_PER_ELEMENT) + itemCount * dim // ), // itemCount * dim, // Module.HEAPU32.subarray( // Math.floor(labelVecDataPtr / Uint32Array.BYTES_PER_ELEMENT), // Math.floor(labelVecDataPtr / Uint32Array.BYTES_PER_ELEMENT) + itemCount // ), // itemCount, // replaceDeleted // ); // Module.asm.free(vecDataPtr); // Module.asm.free(labelVecDataPtr); // }; /***************** GENERATED FILE ********************/