Skip to content

feat: replace hnswlib-node with hnswlib-wasm for cross-platform compatibility#47

Merged
tickernelz merged 1 commit intotickernelz:mainfrom
kui123456789:feat/replace-hnswlib-node-with-wasm
Feb 28, 2026
Merged

feat: replace hnswlib-node with hnswlib-wasm for cross-platform compatibility#47
tickernelz merged 1 commit intotickernelz:mainfrom
kui123456789:feat/replace-hnswlib-node-with-wasm

Conversation

@kui123456789
Copy link
Contributor

Summary

Replace hnswlib-node (native C++ addon) with hnswlib-wasm (WebAssembly) to eliminate the need for Visual Studio Build Tools / C++ compiler on Windows.

Problem

hnswlib-node requires native compilation via node-gyp, which demands:

  • Visual Studio Build Tools (~2-3 GB download)
  • Python 3
  • MSVC C++ compiler

This is a significant barrier for Windows users who just want to install and use the plugin.

Solution

Replace hnswlib-node ^3.0.0 with hnswlib-wasm ^0.8.2. This is based on the author's own wasm implementation from commit d5581bb, adapted for the current v2.11.3 dual-index architecture.

Changes

package.json:

  • hnswlib-node ^3.0.0hnswlib-wasm ^0.8.2

src/services/sqlite/hnsw-index.ts:

  • Lazy async import for hnswlib-wasm module
  • 3-arg constructor ("cosine", dims, maxElements) instead of separate initIndex()
  • readIndex() / writeIndex() are sync (no await)
  • addPoint() and searchKnn() accept Float32Array directly (removed vectorToArray() helper)

Verification

  • TypeScript: npx tsc --noEmit — zero errors
  • npm install — clean, no native compilation needed
  • Pre-commit hooks (prettier) — passed

…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)
Copilot AI review requested due to automatic review settings February 28, 2026 03:08
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-node with hnswlib-wasm in 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.

Comment on lines 36 to 40
"@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"
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +20

async function loadHNSWLib(): Promise<any> {
if (!HNSWLib) {
const module = await import("hnswlib-wasm");
HNSWLib = module;
}
return HNSWLib;
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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;

Copilot uses AI. Check for mistakes.
@tickernelz tickernelz merged commit f155fb4 into tickernelz:main Feb 28, 2026
4 of 5 checks passed
kui123456789 added a commit to kui123456789/opencode-mem that referenced this pull request Mar 1, 2026
- 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants