feat: replace hnswlib-node with hnswlib-wasm for cross-platform compatibility#47
Conversation
…tibility Remove native C++ dependency (hnswlib-node) that requires Visual Studio Build Tools on Windows. Replace with hnswlib-wasm which runs everywhere via WebAssembly. Based on the author's own wasm implementation from commit d5581bb, adapted for the current v2.11.3 dual-index architecture. Changes: - Replace hnswlib-node ^3.0.0 with hnswlib-wasm ^0.8.2 - Use lazy async import for hnswlib-wasm module - Adapt to wasm API: 3-arg constructor, sync read/writeIndex, Float32Array params - Remove vectorToArray() helper (wasm accepts Float32Array directly)
There was a problem hiding this comment.
Pull request overview
This PR swaps the HNSW vector index backend from the native hnswlib-node addon to hnswlib-wasm to avoid node-gyp/C++ toolchain requirements and improve cross-platform installability (notably on Windows).
Changes:
- Replaces
hnswlib-nodewithhnswlib-wasmin dependencies. - Updates the SQLite HNSW index wrapper to lazy-load the WASM module and adjust API usage (constructor, sync read/write, Float32Array inputs).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/services/sqlite/hnsw-index.ts |
Switches to a lazy async import of hnswlib-wasm and updates index construction/IO and vector method calls accordingly. |
package.json |
Replaces the native dependency with the WASM dependency. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "@opencode-ai/plugin": "^1.0.162", | ||
| "@xenova/transformers": "^2.17.2", | ||
| "franc-min": "^6.2.0", | ||
| "hnswlib-node": "^3.0.0", | ||
| "hnswlib-wasm": "^0.8.2", | ||
| "iso-639-3": "^3.0.1" |
There was a problem hiding this comment.
bun.lock still references hnswlib-node@3.0.0, so Bun installs may continue pulling the native addon even though package.json switched to hnswlib-wasm. Regenerate and commit the lockfile so it removes hnswlib-node and pins hnswlib-wasm@^0.8.2 instead (this is important for the Windows install story).
|
|
||
| async function loadHNSWLib(): Promise<any> { | ||
| if (!HNSWLib) { | ||
| const module = await import("hnswlib-wasm"); | ||
| HNSWLib = module; | ||
| } | ||
| return HNSWLib; |
There was a problem hiding this comment.
loadHNSWLib() can trigger multiple parallel import("hnswlib-wasm") calls if several requests hit ensureInitialized() concurrently before the first import resolves. Consider caching the in-flight import (e.g., store a Promise and await it) so the module is only loaded/initialized once per process.
| async function loadHNSWLib(): Promise<any> { | |
| if (!HNSWLib) { | |
| const module = await import("hnswlib-wasm"); | |
| HNSWLib = module; | |
| } | |
| return HNSWLib; | |
| let hnswLibPromise: Promise<any> | null = null; | |
| async function loadHNSWLib(): Promise<any> { | |
| if (HNSWLib) { | |
| return HNSWLib; | |
| } | |
| if (!hnswLibPromise) { | |
| hnswLibPromise = import("hnswlib-wasm") | |
| .then((module) => { | |
| HNSWLib = module; | |
| return module; | |
| }) | |
| .catch((error) => { | |
| hnswLibPromise = null; | |
| throw error; | |
| }); | |
| } | |
| return hnswLibPromise; |
- Use loadHnswlib() to properly initialize the WASM library instead of assigning the ES module namespace directly - Fix HierarchicalNSW constructor calls (remove invalid maxElements as 3rd arg, add separate initIndex() call) - Bridge Emscripten virtual filesystem with Node.js real filesystem: read .hnsw files from disk into virtual FS before readIndex(), write virtual FS data back to disk after writeIndex() - Fix readIndex() to pass maxElements as required 2nd argument - Fixes vector search returning 0 results (regression from PR tickernelz#47)
Summary
Replace
hnswlib-node(native C++ addon) withhnswlib-wasm(WebAssembly) to eliminate the need for Visual Studio Build Tools / C++ compiler on Windows.Problem
hnswlib-noderequires native compilation vianode-gyp, which demands:This is a significant barrier for Windows users who just want to install and use the plugin.
Solution
Replace
hnswlib-node ^3.0.0withhnswlib-wasm ^0.8.2. This is based on the author's own wasm implementation from commitd5581bb, adapted for the current v2.11.3 dual-index architecture.Changes
package.json:hnswlib-node ^3.0.0→hnswlib-wasm ^0.8.2src/services/sqlite/hnsw-index.ts:hnswlib-wasmmodule"cosine", dims, maxElements) instead of separateinitIndex()readIndex()/writeIndex()are sync (noawait)addPoint()andsearchKnn()acceptFloat32Arraydirectly (removedvectorToArray()helper)Verification
npx tsc --noEmit— zero errorsnpm install— clean, no native compilation needed