diff --git a/.github/workflows/publish-fixed-packages.yml b/.github/workflows/publish-fixed-packages.yml new file mode 100644 index 000000000..16b8f4810 --- /dev/null +++ b/.github/workflows/publish-fixed-packages.yml @@ -0,0 +1,155 @@ +name: Publish (ruvector + rvf-wasm) + +# Publishes the npm packages that the per-feature `build-*.yml` workflows do +# NOT cover: the top-level `ruvector` package, `@ruvector/rvf-wasm`, and +# `@ruvector/rvf`. Runs the regression guard first so a structurally-broken +# tarball (the #354 / #372 / #376 / #415 / #417 class of bug) can never reach +# npm again. +# +# Triggers: +# - manual `workflow_dispatch` (DRY-RUN by default — set dry_run=false to +# actually publish) +# - push of a tag matching `ruvector-v*` (real publish) +# +# Required secrets for a real publish: NPM_TOKEN. + +on: + workflow_dispatch: + inputs: + dry_run: + description: 'Dry run (build + pack + guard only, no npm publish)' + type: boolean + default: true + packages: + description: 'Which packages: all | ruvector | rvf-wasm | rvf' + type: string + default: 'all' + push: + tags: + - 'ruvector-v*' + +permissions: + contents: read + id-token: write # npm provenance + +concurrency: + group: publish-fixed-${{ github.ref }} + cancel-in-progress: false + +env: + IS_DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false }} + WANT: ${{ github.event_name == 'workflow_dispatch' && inputs.packages || 'all' }} + +jobs: + guard: + name: Regression guard + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Build ruvector + working-directory: npm/packages/ruvector + run: | + npm install --no-audit --no-fund --ignore-scripts + npm run build + - name: Tarball integrity check + run: node scripts/ci/check-npm-package-integrity.mjs + + publish: + name: Publish to npm + needs: guard + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Resolve dry-run flag + id: mode + run: | + echo "dry_run=${IS_DRY_RUN}" >> "$GITHUB_OUTPUT" + echo "Mode: dry_run=${IS_DRY_RUN}, packages=${WANT}" + + # --- helper: build a package if it has a build script ---------------- + - name: Build packages + run: | + set -euxo pipefail + for d in npm/packages/ruvector npm/packages/rvf npm/packages/rvf-wasm; do + ( cd "$d" + npm install --no-audit --no-fund --ignore-scripts || true + if node -e "process.exit(require('./package.json').scripts?.build?0:1)"; then npm run build; fi ) + done + + # --- publish one package: skip if version already on npm ------------- + - name: Publish @ruvector/rvf-wasm + if: ${{ env.WANT == 'all' || env.WANT == 'rvf-wasm' }} + working-directory: npm/packages/rvf-wasm + run: | + NAME=$(node -p "require('./package.json').name") + VER=$(node -p "require('./package.json').version") + npm pack >/dev/null + if npm view "$NAME@$VER" version >/dev/null 2>&1; then + echo "::notice::$NAME@$VER already published — skipping"; exit 0 + fi + if [ "${{ steps.mode.outputs.dry_run }}" = "true" ]; then + echo "DRY RUN — would: npm publish --access public --provenance ($NAME@$VER)" + else + npm publish --access public --provenance + fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Publish @ruvector/rvf + if: ${{ env.WANT == 'all' || env.WANT == 'rvf' }} + working-directory: npm/packages/rvf + run: | + NAME=$(node -p "require('./package.json').name") + VER=$(node -p "require('./package.json').version") + npm pack >/dev/null + if npm view "$NAME@$VER" version >/dev/null 2>&1; then + echo "::notice::$NAME@$VER already published — skipping"; exit 0 + fi + if [ "${{ steps.mode.outputs.dry_run }}" = "true" ]; then + echo "DRY RUN — would: npm publish --access public --provenance ($NAME@$VER)" + else + npm publish --access public --provenance + fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Publish ruvector + if: ${{ env.WANT == 'all' || env.WANT == 'ruvector' }} + working-directory: npm/packages/ruvector + run: | + NAME=$(node -p "require('./package.json').name") + VER=$(node -p "require('./package.json').version") + # `prepublishOnly` (build + verify-dist) runs automatically on publish; + # run it now for the dry-run path too so the guard is identical. + npm run prepublishOnly + npm pack >/dev/null + if npm view "$NAME@$VER" version >/dev/null 2>&1; then + echo "::notice::$NAME@$VER already published — skipping"; exit 0 + fi + if [ "${{ steps.mode.outputs.dry_run }}" = "true" ]; then + echo "DRY RUN — would: npm publish --access public ($NAME@$VER)" + else + npm publish --access public + fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Summary + if: always() + run: | + { + echo "## Publish run" + echo "- dry_run: \`${{ steps.mode.outputs.dry_run }}\`" + echo "- packages: \`${WANT}\`" + echo "- ruvector: $(node -p "require('./npm/packages/ruvector/package.json').version")" + echo "- @ruvector/rvf-wasm: $(node -p "require('./npm/packages/rvf-wasm/package.json').version")" + echo "- @ruvector/rvf: $(node -p "require('./npm/packages/rvf/package.json').version")" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/regression-guard.yml b/.github/workflows/regression-guard.yml new file mode 100644 index 000000000..b9046922a --- /dev/null +++ b/.github/workflows/regression-guard.yml @@ -0,0 +1,172 @@ +name: Regression Guard + +# Re-tests the failure modes behind previously-fixed bugs so they cannot +# silently come back. Each job maps to one or more closed issues — see the +# comments. Keep this fast (no full workspace build) so it can gate every PR. + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: regression-guard-${{ github.ref }} + cancel-in-progress: true + +jobs: + # --------------------------------------------------------------------------- + # npm publish hygiene — guards #354 (ONNX wasm not bundled), #323 (.wasm ext), + # #376 (dist/index.js missing from tarball), #415 (rvf-wasm ESM-in-CJS), + # #372 (pi-brain require() of ESM-only package). + # --------------------------------------------------------------------------- + npm-package-integrity: + name: npm package integrity + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + # Build the packages that ship compiled output, so `npm pack` sees the + # real publishable tree. `prepublishOnly` runs build + verify-dist for + # the main package; rvf-wasm / pi-brain ship checked-in `pkg/` & `dist/`. + - name: Build ruvector package + working-directory: npm/packages/ruvector + run: | + npm install --no-audit --no-fund --ignore-scripts --legacy-peer-deps + npm run build + - name: Check published-tarball integrity + run: node scripts/ci/check-npm-package-integrity.mjs + + # --------------------------------------------------------------------------- + # The MCP server must at minimum parse and import cleanly on a stock Node — + # guards #372 (require of ESM-only @ruvector/pi-brain) and #422 (spawnSync of + # `npx ruvector ...` timing out: the handler should not shell out to npx). + # --------------------------------------------------------------------------- + mcp-server-loads: + name: MCP server loads + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - working-directory: npm/packages/ruvector + run: npm install --no-audit --no-fund --ignore-scripts --legacy-peer-deps + - name: Build ruvector dist + working-directory: npm/packages/ruvector + run: npm run build + - name: node --check bin/*.js + working-directory: npm/packages/ruvector + run: | + node --check bin/mcp-server.js + node --check bin/cli.js + - name: IntelligenceEngine invariants (#315, #316) + run: node scripts/ci/check-intelligence-engine-invariants.mjs + - name: hooks_route_enhanced does not shell out to npx (#422) + working-directory: npm/packages/ruvector + run: | + # Extract the body of the `hooks_route_enhanced` case and ensure it + # does not invoke `npx` (cold-start blows the timeout — #422). + BODY=$(awk "/case 'hooks_route_enhanced'/{f=1} f{print} f&&/^ }/{exit}" bin/mcp-server.js) + echo "$BODY" + if echo "$BODY" | grep -qE "\bnpx\b"; then + echo "::error::the hooks_route_enhanced handler in bin/mcp-server.js runs \`npx ...\` — npx cold-start exceeds the timeout (#422). Call the local cli.js directly." + exit 1 + fi + echo "OK: hooks_route_enhanced calls the local CLI, not npx" + - name: pi-brain imports are not require()'d (#372) + working-directory: npm/packages/ruvector + run: | + if grep -nE "require\(\s*['\"]@ruvector/pi-brain['\"]\s*\)" bin/mcp-server.js; then + echo "::error::bin/mcp-server.js require()s @ruvector/pi-brain, which is ESM-only — use await import() (#372)." + exit 1 + fi + echo "OK: pi-brain is loaded via dynamic import" + + # --------------------------------------------------------------------------- + # A default `cargo build` on STABLE must work — guards #438 (avx512f + # target_feature/intrinsics are nightly-only and were forcing every consumer + # of ruvector-core onto nightly). + # --------------------------------------------------------------------------- + stable-toolchain-build: + name: builds on stable (no nightly-only features) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: cargo +stable check ruvector-core (default features) + run: cargo +stable check -p ruvector-core + - name: cargo +stable check ruvector-router-core (default features) + run: cargo +stable check -p ruvector-router-core + - name: avx512 target_feature attrs are cfg-gated behind simd-avx512 (#438) + run: | + # For every `#[target_feature(enable = "avx512…")]` line, scan the + # preceding 3 lines for a `#[cfg(... feature = "simd-avx512" ...)]` + # attribute. If none is present, that nightly-only intrinsic is in + # the default build path and forces nightly on every consumer. + python3 - <<'PY' + import re, sys + from pathlib import Path + BAD = [] + for p in Path('crates/ruvector-core/src').rglob('*.rs'): + lines = p.read_text().splitlines() + for i, line in enumerate(lines): + if re.search(r'#\[target_feature\(enable\s*=\s*"avx512', line): + window = '\n'.join(lines[max(0, i-3):i]) + if 'simd-avx512' not in window: + BAD.append(f"{p}:{i+1}: {line.strip()}") + if BAD: + print('::error::avx512 target_feature attrs are not gated behind `feature = "simd-avx512"` (#438):') + for b in BAD: print(' ' + b) + sys.exit(1) + print('OK: every avx512 target_feature is preceded by a simd-avx512 cfg gate') + PY + + # --------------------------------------------------------------------------- + # Windows-friendly checkout — guards #458 (case-insensitive filesystems can't + # check out two tracked files whose paths differ only by case). Run the check + # on Linux (where the FS *is* case-sensitive, so both files are present and + # we can detect them via `git ls-files`). + # --------------------------------------------------------------------------- + no-case-collisions: + name: no case-insensitive path collisions (#458) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Detect colliding paths + run: | + DUPES=$(git ls-files | awk '{ lo = tolower($0); files[lo] = files[lo] "\n " $0; count[lo]++ } END { for (k in count) if (count[k] > 1) print "COLLISION (lowercase: " k ")" files[k] "\n" }') + if [ -n "$DUPES" ]; then + echo "::error::Tracked paths collide on case-insensitive filesystems — Windows clones lose one of each pair (#458):" + printf '%s\n' "$DUPES" + exit 1 + fi + echo "OK: no case-insensitive path collisions" + + # --------------------------------------------------------------------------- + # postgres extension toolchain mismatch — guards #331 (pgrx pinned to 0.12 but + # `cargo install cargo-pgrx` grabs the latest). We don't build pgrx here + # (heavy); we just assert the pin is documented so users don't hit the wall. + # --------------------------------------------------------------------------- + pgrx-pin-documented: + name: pgrx version pin is documented (#331) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: cargo-pgrx version must be pinned in docs + run: | + PIN=$(grep -oE 'pgrx[^=]*=\s*"[^"]+"' crates/ruvector-postgres/Cargo.toml | head -1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?') + echo "Cargo.toml pins pgrx ~= $PIN" + if ! grep -RIn "cargo-pgrx --version" crates/ruvector-postgres docs 2>/dev/null | grep -q "$PIN"; then + echo "::error::crates/ruvector-postgres pins pgrx $PIN but no doc tells users to 'cargo install cargo-pgrx --version \"$PIN.x\" --locked' (#331)." + exit 1 + fi + echo "OK: pgrx pin is documented" diff --git a/Cargo.lock b/Cargo.lock index 7b9accc37..d775ebf68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10733,6 +10733,13 @@ dependencies = [ "web-sys", ] +[[package]] +name = "ruvllm_retrieval_diffusion" +version = "0.1.0" +dependencies = [ + "ruvllm_sparse_attention", +] + [[package]] name = "ruvllm_sparse_attention" version = "0.1.1" diff --git a/crates/ruvector-core/Cargo.toml b/crates/ruvector-core/Cargo.toml index 52bb4f33a..748e6d336 100644 --- a/crates/ruvector-core/Cargo.toml +++ b/crates/ruvector-core/Cargo.toml @@ -97,6 +97,7 @@ harness = false [features] default = ["simd", "storage", "hnsw", "api-embeddings", "parallel"] simd = ["simsimd"] # SIMD acceleration (not available in WASM) +simd-avx512 = [] # Opt-in AVX-512 intrinsics (nightly-only); OFF by default so stable builds work parallel = ["rayon", "crossbeam"] # Parallel processing (not available in WASM) storage = ["redb", "memmap2"] # File-based storage (not available in WASM) hnsw = ["hnsw_rs"] # HNSW indexing (not available in WASM due to mmap dependency) diff --git a/crates/ruvector-core/src/simd_intrinsics.rs b/crates/ruvector-core/src/simd_intrinsics.rs index fcb7f8a79..74c61e105 100644 --- a/crates/ruvector-core/src/simd_intrinsics.rs +++ b/crates/ruvector-core/src/simd_intrinsics.rs @@ -42,9 +42,13 @@ const PREFETCH_DISTANCE: usize = 64; pub fn euclidean_distance_simd(a: &[f32], b: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] { - if is_x86_feature_detected!("avx512f") { - unsafe { euclidean_distance_avx512_impl(a, b) } - } else if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") { + #[cfg(feature = "simd-avx512")] + { + if is_x86_feature_detected!("avx512f") { + return unsafe { euclidean_distance_avx512_impl(a, b) }; + } + } + if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") { unsafe { euclidean_distance_avx2_fma_impl(a, b) } } else if is_x86_feature_detected!("avx2") { unsafe { euclidean_distance_avx2_impl(a, b) } @@ -192,7 +196,7 @@ unsafe fn euclidean_distance_avx2_fma_impl(a: &[f32], b: &[f32]) -> f32 { // ============================================================================ /// AVX-512 euclidean distance - processes 16 floats per iteration -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", feature = "simd-avx512"))] #[target_feature(enable = "avx512f")] unsafe fn euclidean_distance_avx512_impl(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len(), "Input arrays must have the same length"); @@ -223,7 +227,7 @@ unsafe fn euclidean_distance_avx512_impl(a: &[f32], b: &[f32]) -> f32 { } /// AVX-512 dot product - processes 16 floats per iteration -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", feature = "simd-avx512"))] #[target_feature(enable = "avx512f")] unsafe fn dot_product_avx512_impl(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len(), "Input arrays must have the same length"); @@ -249,7 +253,7 @@ unsafe fn dot_product_avx512_impl(a: &[f32], b: &[f32]) -> f32 { } /// AVX-512 cosine similarity - processes 16 floats per iteration -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", feature = "simd-avx512"))] #[target_feature(enable = "avx512f")] unsafe fn cosine_similarity_avx512_impl(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len(), "Input arrays must have the same length"); @@ -284,7 +288,7 @@ unsafe fn cosine_similarity_avx512_impl(a: &[f32], b: &[f32]) -> f32 { } /// AVX-512 Manhattan distance - processes 16 floats per iteration -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", feature = "simd-avx512"))] #[target_feature(enable = "avx512f")] unsafe fn manhattan_distance_avx512_impl(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len(), "Input arrays must have the same length"); @@ -783,9 +787,13 @@ unsafe fn manhattan_distance_neon_unrolled_impl(a: &[f32], b: &[f32]) -> f32 { pub fn dot_product_simd(a: &[f32], b: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] { - if is_x86_feature_detected!("avx512f") { - unsafe { dot_product_avx512_impl(a, b) } - } else if is_x86_feature_detected!("avx2") { + #[cfg(feature = "simd-avx512")] + { + if is_x86_feature_detected!("avx512f") { + return unsafe { dot_product_avx512_impl(a, b) }; + } + } + if is_x86_feature_detected!("avx2") { unsafe { dot_product_avx2_impl(a, b) } } else { dot_product_scalar(a, b) @@ -847,9 +855,13 @@ unsafe fn dot_product_avx2_impl(a: &[f32], b: &[f32]) -> f32 { pub fn cosine_similarity_simd(a: &[f32], b: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] { - if is_x86_feature_detected!("avx512f") { - unsafe { cosine_similarity_avx512_impl(a, b) } - } else if is_x86_feature_detected!("avx2") { + #[cfg(feature = "simd-avx512")] + { + if is_x86_feature_detected!("avx512f") { + return unsafe { cosine_similarity_avx512_impl(a, b) }; + } + } + if is_x86_feature_detected!("avx2") { unsafe { cosine_similarity_avx2_impl(a, b) } } else { cosine_similarity_scalar(a, b) @@ -883,9 +895,13 @@ pub fn cosine_similarity_avx2(a: &[f32], b: &[f32]) -> f32 { pub fn manhattan_distance_simd(a: &[f32], b: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] { - if is_x86_feature_detected!("avx512f") { - unsafe { manhattan_distance_avx512_impl(a, b) } - } else if is_x86_feature_detected!("avx2") { + #[cfg(feature = "simd-avx512")] + { + if is_x86_feature_detected!("avx512f") { + return unsafe { manhattan_distance_avx512_impl(a, b) }; + } + } + if is_x86_feature_detected!("avx2") { unsafe { manhattan_distance_avx2_impl(a, b) } } else { manhattan_distance_scalar(a, b) diff --git a/crates/ruvector-graph/src/graph.rs b/crates/ruvector-graph/src/graph.rs index 09340101a..c5b5ea31a 100644 --- a/crates/ruvector-graph/src/graph.rs +++ b/crates/ruvector-graph/src/graph.rs @@ -364,9 +364,7 @@ impl GraphDB { /// Delete all hyperedges that contain a given node pub fn delete_hyperedges_by_node(&self, node_id: &NodeId) -> Result { - let ids: Vec = self - .hyperedge_node_index - .get_hyperedges_by_node(node_id); + let ids: Vec = self.hyperedge_node_index.get_hyperedges_by_node(node_id); let mut deleted = 0; for id in &ids { if self.delete_hyperedge(id)? { diff --git a/crates/ruvector-hailo/src/device.rs b/crates/ruvector-hailo/src/device.rs index b36926e11..ff4bdb4c8 100644 --- a/crates/ruvector-hailo/src/device.rs +++ b/crates/ruvector-hailo/src/device.rs @@ -11,7 +11,6 @@ //! configured network group. Configuration changes still need external //! serialisation; we provide that via `Mutex` higher up in `lib.rs`. - use crate::error::HailoError; #[cfg(feature = "hailo")] use std::ptr; diff --git a/crates/ruvector-hailo/src/hef_embedder_pool.rs b/crates/ruvector-hailo/src/hef_embedder_pool.rs index 0c45fb793..8afdfc966 100644 --- a/crates/ruvector-hailo/src/hef_embedder_pool.rs +++ b/crates/ruvector-hailo/src/hef_embedder_pool.rs @@ -212,9 +212,7 @@ impl HefEmbedderPool { // pays a queue cost but doesn't lose throughput — slot 0 is // about to be free and the next inference is already in // flight on another slot. - let g = self.slots[0] - .lock() - .unwrap_or_else(|p| p.into_inner()); + let g = self.slots[0].lock().unwrap_or_else(|p| p.into_inner()); g.embed(text) } } diff --git a/crates/ruvector-hailo/src/inference.rs b/crates/ruvector-hailo/src/inference.rs index 3752f8c60..2eeaa0d53 100644 --- a/crates/ruvector-hailo/src/inference.rs +++ b/crates/ruvector-hailo/src/inference.rs @@ -22,7 +22,6 @@ //! → L2-normalise to unit vector //! → return Vec - use crate::device::HailoDevice; use crate::error::HailoError; use crate::tokenizer::WordPieceTokenizer; diff --git a/crates/ruvector-hailo/src/lib.rs b/crates/ruvector-hailo/src/lib.rs index 5d7618e7c..4be0fae4c 100644 --- a/crates/ruvector-hailo/src/lib.rs +++ b/crates/ruvector-hailo/src/lib.rs @@ -214,9 +214,9 @@ impl HailoEmbedder { )?, )) } else { - Some(HefBackend::Single( - crate::hef_embedder::HefEmbedder::open(dev, model_dir)?, - )) + Some(HefBackend::Single(crate::hef_embedder::HefEmbedder::open( + dev, model_dir, + )?)) } } else { None diff --git a/crates/ruvector-hailo/src/tokenizer.rs b/crates/ruvector-hailo/src/tokenizer.rs index c81dafe4c..a1eadddb3 100644 --- a/crates/ruvector-hailo/src/tokenizer.rs +++ b/crates/ruvector-hailo/src/tokenizer.rs @@ -15,7 +15,6 @@ //! Continuation pieces are prefixed `##`. //! 5. Wrap with `[CLS] … [SEP]`, pad/truncate to a fixed `max_seq`. - use crate::error::HailoError; use std::collections::HashMap; use std::path::Path; diff --git a/crates/ruvector-router-core/src/vector_db.rs b/crates/ruvector-router-core/src/vector_db.rs index eacb8035b..1fd5a2992 100644 --- a/crates/ruvector-router-core/src/vector_db.rs +++ b/crates/ruvector-router-core/src/vector_db.rs @@ -150,7 +150,8 @@ impl VectorDB { if deleted { self.index.remove(id)?; - self.stats.write().total_vectors = self.stats.write().total_vectors.saturating_sub(1); + let mut stats = self.stats.write(); + stats.total_vectors = stats.total_vectors.saturating_sub(1); } Ok(deleted) @@ -299,4 +300,50 @@ mod tests { assert!(db.delete("test1").unwrap()); assert_eq!(db.count().unwrap(), 0); } + + /// Regression test for #437: `delete` previously acquired `self.stats.write()` + /// twice in one expression, causing a parking_lot self-deadlock. Run on a + /// worker thread with a timeout so a regression fails the test instead of + /// hanging CI forever. + #[test] + fn delete_does_not_deadlock() { + use std::sync::mpsc; + use std::time::Duration; + + let (tx, rx) = mpsc::channel(); + let handle = std::thread::spawn(move || { + let dir = tempdir().unwrap(); + let path = dir.path().join("delete_test.db"); + + let db = VectorDB::builder() + .dimensions(3) + .storage_path(&path) + .build() + .unwrap(); + + db.insert(VectorEntry { + id: "v1".to_string(), + vector: vec![1.0, 0.0, 0.0], + metadata: std::collections::HashMap::new(), + timestamp: 0, + }) + .unwrap(); + assert_eq!(db.stats().total_vectors, 1); + + // This is the call that used to deadlock. + assert!(db.delete("v1").unwrap()); + assert_eq!(db.stats().total_vectors, 0); + + // Deleting a missing id must not underflow or change stats. + assert!(!db.delete("missing").unwrap()); + assert_eq!(db.stats().total_vectors, 0); + + tx.send(()).unwrap(); + }); + + match rx.recv_timeout(Duration::from_secs(10)) { + Ok(()) => handle.join().unwrap(), + Err(_) => panic!("VectorDB::delete deadlocked (regression of #437)"), + } + } } diff --git a/crates/ruvllm_retrieval_diffusion/examples/drum_patterns.rs b/crates/ruvllm_retrieval_diffusion/examples/drum_patterns.rs index f782172f3..17fec9e2f 100644 --- a/crates/ruvllm_retrieval_diffusion/examples/drum_patterns.rs +++ b/crates/ruvllm_retrieval_diffusion/examples/drum_patterns.rs @@ -123,7 +123,15 @@ fn main() { let t0 = std::time::Instant::now(); let ar = retriever.generate_fast(&seed, 64, &sampling, 0xC0_FFEE_42); let dt_ar = t0.elapsed(); - println!("seed : \"{}\"", String::from_utf8_lossy(&seed.iter().map(|&t| decode_token(t) as u8).collect::>())); + println!( + "seed : \"{}\"", + String::from_utf8_lossy( + &seed + .iter() + .map(|&t| decode_token(t) as u8) + .collect::>() + ) + ); println!("generated : {} tokens in {:.2?}", 64, dt_ar); println!(); println!("{}", render_bars(&ar, 16)); diff --git a/crates/ruvllm_retrieval_diffusion/src/lib.rs b/crates/ruvllm_retrieval_diffusion/src/lib.rs index 02100f3ee..619b8c543 100644 --- a/crates/ruvllm_retrieval_diffusion/src/lib.rs +++ b/crates/ruvllm_retrieval_diffusion/src/lib.rs @@ -605,15 +605,13 @@ impl<'a> Diffuser<'a> { if boot_len > 0 && corpus_len > boot_len { let corpus_off = (xorshift32(&mut state) as usize) % (corpus_len - boot_len); let work_off = (xorshift32(&mut state) as usize) % (n - boot_len); - working[work_off..work_off + boot_len].copy_from_slice( - &self.retriever.corpus[corpus_off..corpus_off + boot_len], - ); + working[work_off..work_off + boot_len] + .copy_from_slice(&self.retriever.corpus[corpus_off..corpus_off + boot_len]); } for t in 0..n_steps { let frac = ((t + 1) as f32) / (n_steps as f32); - let target_masked = - (n as f32 * (core::f32::consts::FRAC_PI_2 * frac).cos()) as usize; + let target_masked = (n as f32 * (core::f32::consts::FRAC_PI_2 * frac).cos()) as usize; let current_masked = working.iter().filter(|&&x| x == mask).count(); let to_unmask = current_masked.saturating_sub(target_masked).max(1); self.denoise_step(&mut working, to_unmask, sampling, &mut state); diff --git a/crates/ruvllm_sparse_attention/examples/sparse_mario.rs b/crates/ruvllm_sparse_attention/examples/sparse_mario.rs index c9e4fc313..538965eb5 100644 --- a/crates/ruvllm_sparse_attention/examples/sparse_mario.rs +++ b/crates/ruvllm_sparse_attention/examples/sparse_mario.rs @@ -40,7 +40,6 @@ M-------------------------------------[]----------\n\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - // Slice B — staircase, double-pipe, brick ceiling "\ --------------------------------------------------\n\ @@ -57,7 +56,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", - // Slice C — cannons, gap, coin shower "\ --------------------------------------------------\n\ @@ -78,21 +76,21 @@ XXXXXXXXXXXXXXXXXXBXXXXXXXXXXXXXXBXXXXX-----XXXXXX", /// Tile vocabulary in deterministic order. Index = token id. pub const VOCAB: &[char] = &[ - '-', // 0 sky - 'X', // 1 ground - 'S', // 2 breakable brick - '?', // 3 active ? block - 'Q', // 4 used ? block - 'o', // 5 coin - '<', // 6 pipe top-left - '>', // 7 pipe top-right - '[', // 8 pipe body-left - ']', // 9 pipe body-right - 'E', // 10 enemy (goomba) - 'B', // 11 cannon ball - 'b', // 12 cannon top - 'M', // 13 mario start - '\n',// 14 row separator + '-', // 0 sky + 'X', // 1 ground + 'S', // 2 breakable brick + '?', // 3 active ? block + 'Q', // 4 used ? block + 'o', // 5 coin + '<', // 6 pipe top-left + '>', // 7 pipe top-right + '[', // 8 pipe body-left + ']', // 9 pipe body-right + 'E', // 10 enemy (goomba) + 'B', // 11 cannon ball + 'b', // 12 cannon top + 'M', // 13 mario start + '\n', // 14 row separator ]; /// Char → token id. Returns None for unknown characters so the corpus stays clean. @@ -639,9 +637,7 @@ pub fn render_level(tokens: &[u8]) -> String { pub fn uniform_random_generate(n: usize, seed: u32) -> Vec { let mut state = seed.max(1); let v = VOCAB_SIZE as u32; - (0..n) - .map(|_| (xorshift32(&mut state) % v) as u8) - .collect() + (0..n).map(|_| (xorshift32(&mut state) % v) as u8).collect() } /// First-order Markov chain over the embedded corpus — the classical @@ -1211,9 +1207,8 @@ impl<'a> MarioDiffuser<'a> { if boot_len > 0 && corpus_len > boot_len { let corpus_off = (xorshift32(&mut state) as usize) % (corpus_len - boot_len); let work_off = (xorshift32(&mut state) as usize) % (n - boot_len); - working[work_off..work_off + boot_len].copy_from_slice( - &self.retriever.corpus[corpus_off..corpus_off + boot_len], - ); + working[work_off..work_off + boot_len] + .copy_from_slice(&self.retriever.corpus[corpus_off..corpus_off + boot_len]); } for t in 0..n_steps { @@ -1248,7 +1243,10 @@ fn main() { println!("levels : {}", LEVELS.len()); println!("total tokens : {}", tokens.len()); println!("vocab size : {}", VOCAB.len()); - println!("level widths : {:?}", LEVELS.iter().map(|l| level_width(l)).collect::>()); + println!( + "level widths : {:?}", + LEVELS.iter().map(|l| level_width(l)).collect::>() + ); println!(); println!("Tile distribution:"); let mut entries: Vec<_> = dist.iter().collect(); @@ -1256,7 +1254,11 @@ fn main() { let total = tokens.len() as f64; for (c, n) in entries { let pct = (*n as f64 / total) * 100.0; - let label = if *c == '\n' { "\\n".to_string() } else { c.to_string() }; + let label = if *c == '\n' { + "\\n".to_string() + } else { + c.to_string() + }; println!(" {:>3} {:>5} {:>5.1}%", label, n, pct); } @@ -1294,7 +1296,10 @@ fn main() { sampling.no_repeat_window, sampling.temperature ); - println!("generated : {} tokens in {:.2?} (KvCache + decode_step)", n_gen, dt); + println!( + "generated : {} tokens in {:.2?} (KvCache + decode_step)", + n_gen, dt + ); println!(); println!("{}", rendered); println!(); @@ -1318,12 +1323,12 @@ fn main() { println!("== Sparse-attention masked discrete diffusion =="); let diffuser = MarioDiffuser::new(&retriever); let n_diff = 50 * 14; // 14×50 grid, fully masked at start - // Iter 12 sweep winner: 24 denoising steps (vs the iter 7 default of 16) - // gave the lowest avg L2 distance to corpus across 3 seeds: - // steps=16 0.746 steps=24 0.723 steps=32 0.798 - // 24 is the cosine-schedule sweet-spot — enough late-stage steps for - // bidirectional context to settle, without spending budget on a flat - // tail. + // Iter 12 sweep winner: 24 denoising steps (vs the iter 7 default of 16) + // gave the lowest avg L2 distance to corpus across 3 seeds: + // steps=16 0.746 steps=24 0.723 steps=32 0.798 + // 24 is the cosine-schedule sweet-spot — enough late-stage steps for + // bidirectional context to settle, without spending budget on a flat + // tail. let n_steps = 24; let t0 = std::time::Instant::now(); let diffused = diffuser.diffuse(n_diff, n_steps, &sampling, 0xD1FF_5008); @@ -1420,8 +1425,7 @@ fn main() { for (name, cfg) in ar_configs.iter() { let mut total = 0.0f32; for &s in &seeds { - let toks = - retriever.generate_fast(&seed_chars, n_total - seed_chars.len(), cfg, s); + let toks = retriever.generate_fast(&seed_chars, n_total - seed_chars.len(), cfg, s); let m = compute_metrics(&toks, cols, rows); total += metric_distance(&m, &target); } @@ -1519,7 +1523,11 @@ mod tests { #[test] fn corpus_nonempty_and_known_tiles() { let toks = encode_corpus(); - assert!(toks.len() > 1000, "corpus should have at least 1k tokens, got {}", toks.len()); + assert!( + toks.len() > 1000, + "corpus should have at least 1k tokens, got {}", + toks.len() + ); for &t in &toks { assert!((t as usize) < VOCAB.len(), "out-of-range token {}", t); } @@ -1551,9 +1559,13 @@ mod tests { let w = level_width(lvl); for (r, row) in lvl.lines().enumerate() { assert_eq!( - row.chars().count(), w, + row.chars().count(), + w, "level {} row {} width mismatch (expected {}, got {})", - i, r, w, row.chars().count() + i, + r, + w, + row.chars().count() ); } } @@ -1685,12 +1697,7 @@ mod tests { let toks: Vec = (0..200).map(|i| (i % 3) as u8).collect(); let s = render_level_wrapped(&toks, 50); for (r, row) in s.lines().enumerate() { - assert_eq!( - row.chars().count(), - 50, - "row {} should have width 50", - r - ); + assert_eq!(row.chars().count(), 50, "row {} should have width 50", r); } assert_eq!(s.lines().count(), 4, "200 chars / 50 cols = 4 rows"); } @@ -1787,7 +1794,8 @@ mod tests { assert!( df > dn, "distance should grow with density gap: near={}, far={}", - dn, df + dn, + df ); } @@ -2087,7 +2095,9 @@ mod tests { assert!( ratio >= 5.0, "generate_fast should be ≥5× faster than generate; ratio={:.2} (slow={:?}, fast={:?})", - ratio, slow_ms, fast_ms + ratio, + slow_ms, + fast_ms ); } @@ -2161,7 +2171,10 @@ mod tests { s.len() >= 4, "diffusion should produce ≥4 distinct tiles, got {} ({:?})", s.len(), - out.iter().take(40).map(|&t| decode_token(t)).collect::() + out.iter() + .take(40) + .map(|&t| decode_token(t)) + .collect::() ); } @@ -2180,8 +2193,7 @@ mod tests { }; let out = d.diffuse(200, 8, &cfg, 0xDEAD); let dist = tile_distribution(&out); - let sky_ground = - *dist.get(&'-').unwrap_or(&0) + *dist.get(&'X').unwrap_or(&0); + let sky_ground = *dist.get(&'-').unwrap_or(&0) + *dist.get(&'X').unwrap_or(&0); let frac = sky_ground as f64 / out.len() as f64; assert!( frac > 0.30, @@ -2202,7 +2214,11 @@ mod tests { let mut state = 0xFEEDu32; d.denoise_step(&mut working, 5, &SamplingConfig::quality(), &mut state); let after = working.iter().filter(|&&t| t == MASK_SENTINEL).count(); - assert_eq!(before - after, 5, "should have unmasked exactly 5 positions"); + assert_eq!( + before - after, + 5, + "should have unmasked exactly 5 positions" + ); } #[test] diff --git a/crates/sona/src/training/federated.rs b/crates/sona/src/training/federated.rs index eaf76d053..33a7c35ea 100644 --- a/crates/sona/src/training/federated.rs +++ b/crates/sona/src/training/federated.rs @@ -232,7 +232,7 @@ impl EphemeralAgent { /// Get learned patterns from agent pub fn get_patterns(&self) -> Vec { - self.engine.find_patterns(&[], 0) + self.engine.get_all_patterns() } /// Export agent state for federation @@ -416,10 +416,9 @@ impl FederatedCoordinator { /// /// Returns learned patterns that new agents can use for warm start. pub fn get_initial_patterns(&self, k: usize) -> Vec { - // Find patterns similar to a general query (empty or average) - // Since we don't have a specific query, get all patterns + // Since we don't have a specific query, return up to `k` of all patterns. self.master_engine - .find_patterns(&[], 0) + .get_all_patterns() .into_iter() .take(k) .collect() @@ -427,7 +426,7 @@ impl FederatedCoordinator { /// Get all learned patterns pub fn get_all_patterns(&self) -> Vec { - self.master_engine.find_patterns(&[], 0) + self.master_engine.get_all_patterns() } /// Get coordinator statistics diff --git a/docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/tmux.js b/docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/tmux.lc.js similarity index 100% rename from docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/tmux.js rename to docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/tmux.lc.js diff --git a/docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/type.js b/docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/type.lc.js similarity index 100% rename from docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/type.js rename to docs/research/claude-code-rvsource/versions/v2.1.x/tree/react_memo_cache_sentinel/react_memo_cache_sentinel/type.lc.js diff --git a/npm/packages/ruvector/bin/cli.js b/npm/packages/ruvector/bin/cli.js index 31d86c670..f8ffb4095 100755 --- a/npm/packages/ruvector/bin/cli.js +++ b/npm/packages/ruvector/bin/cli.js @@ -45,6 +45,68 @@ function requireRuvector() { } } +// ============================================================================= +// Database metadata sidecar (#417) +// ----------------------------------------------------------------------------- +// `` is a redb (Rust binary) file managed by @ruvector/core — it is +// NOT a JSON document. The previous CLI parsed it with +// `JSON.parse(fs.readFileSync(dbPath))` and called wrapper methods that don't +// exist (`db.load`/`db.save`/`db.stats`). Instead, `create` writes a +// `.meta.json` sidecar with the construction args; insert/search/stats/ +// export/import read the sidecar and feed it straight to the wrapper. +// ============================================================================= + +const META_SCHEMA_VERSION = 1; + +function metaPathFor(dbPath) { + return `${dbPath}.meta.json`; +} + +function writeDbMeta(dbPath, meta) { + const payload = { + schemaVersion: META_SCHEMA_VERSION, + dimensions: meta.dimensions, + metric: meta.metric, + cliVersion: packageJson.version, + createdAt: new Date().toISOString(), + }; + fs.writeFileSync(metaPathFor(dbPath), JSON.stringify(payload, null, 2)); +} + +function readDbMeta(dbPath) { + const metaPath = metaPathFor(dbPath); + if (!fs.existsSync(metaPath)) { + if (!fs.existsSync(dbPath)) { + throw new Error(`Database not found: ${dbPath}\n Run "ruvector create ${dbPath}" first.`); + } + throw new Error( + `Database metadata sidecar not found: ${metaPath}\n` + + ` This database was created without a sidecar (e.g. before this fix).\n` + + ` Recreate it with "ruvector create ${dbPath} -d -m ".`, + ); + } + let parsed; + try { + parsed = JSON.parse(fs.readFileSync(metaPath, 'utf8')); + } catch (e) { + throw new Error(`Invalid sidecar at ${metaPath}: ${e.message}`); + } + if (typeof parsed.dimensions !== 'number' || parsed.dimensions <= 0) { + throw new Error(`Invalid sidecar at ${metaPath}: missing or invalid "dimensions"`); + } + return parsed; +} + +function openDbFromMeta(dbPath) { + const meta = readDbMeta(dbPath); + const db = new VectorDB({ + dimensions: meta.dimensions, + metric: meta.metric, + storagePath: dbPath, + }); + return { db, meta }; +} + // Lazy load GNN (optional - loaded on first use, not at startup) // Saves ~6ms startup time by deferring require('@ruvector/gnn') let _gnnModule = undefined; // undefined = not yet attempted, null = failed, object = loaded @@ -157,16 +219,24 @@ program const spinner = ora('Creating database...').start(); try { - const dimension = parseInt(options.dimension); - const db = new VectorDB({ - dimensions: dimension, + const dimensions = parseInt(options.dimension); + // Constructing the redb-backed DB creates the file at `dbPath`. + // Persistence is automatic via `storagePath`; there is no save() call. + // eslint-disable-next-line no-new + new VectorDB({ + dimensions, metric: options.metric, storagePath: dbPath, }); + // Persist construction args so later commands recover them without + // trying to JSON.parse() the redb binary (#417). + writeDbMeta(dbPath, { dimensions, metric: options.metric }); + spinner.succeed(chalk.green(`Database created: ${dbPath}`)); - console.log(chalk.gray(` Dimension: ${dimension}`)); + console.log(chalk.gray(` Dimension: ${dimensions}`)); console.log(chalk.gray(` Metric: ${options.metric}`)); + console.log(chalk.gray(` Sidecar: ${metaPathFor(dbPath)}`)); console.log(chalk.gray(` Implementation: ${getImplementationType()}`)); } catch (error) { spinner.fail(chalk.red('Failed to create database')); @@ -180,43 +250,35 @@ program .command('insert ') .description('Insert vectors from JSON file') .option('-b, --batch-size ', 'Batch size for insertion', '1000') - .action((dbPath, file, options) => { + .action(async (dbPath, file, options) => { requireRuvector(); const spinner = ora('Loading database...').start(); try { - // Read database metadata to get dimension - let dimension = 384; // default - if (fs.existsSync(dbPath)) { - const dbData = fs.readFileSync(dbPath, 'utf8'); - const parsed = JSON.parse(dbData); - dimension = parsed.dimension || 384; - } - - const db = new VectorDB({ dimension }); - - if (fs.existsSync(dbPath)) { - db.load(dbPath); - } + const { db } = openDbFromMeta(dbPath); spinner.text = 'Reading vectors...'; const data = JSON.parse(fs.readFileSync(file, 'utf8')); const vectors = Array.isArray(data) ? data : [data]; + // The native binding requires string ids — coerce numeric/missing ids. + vectors.forEach((v, idx) => { + if (v.id === undefined || v.id === null) v.id = String(idx); + else if (typeof v.id !== 'string') v.id = String(v.id); + }); + spinner.text = `Inserting ${vectors.length} vectors...`; const batchSize = parseInt(options.batchSize); for (let i = 0; i < vectors.length; i += batchSize) { const batch = vectors.slice(i, i + batchSize); - db.insertBatch(batch); + await db.insertBatch(batch); spinner.text = `Inserted ${Math.min(i + batchSize, vectors.length)}/${vectors.length} vectors...`; } - db.save(dbPath); + const total = await db.len(); spinner.succeed(chalk.green(`Inserted ${vectors.length} vectors`)); - - const stats = db.stats(); - console.log(chalk.gray(` Total vectors: ${stats.count}`)); + console.log(chalk.gray(` Total vectors: ${total}`)); } catch (error) { spinner.fail(chalk.red('Failed to insert vectors')); console.error(chalk.red(error.message)); @@ -232,18 +294,12 @@ program .option('-k, --top-k ', 'Number of results', '10') .option('-t, --threshold ', 'Similarity threshold', '0.0') .option('-f, --filter ', 'Metadata filter as JSON') - .action((dbPath, options) => { + .action(async (dbPath, options) => { requireRuvector(); const spinner = ora('Loading database...').start(); try { - // Read database metadata - const dbData = fs.readFileSync(dbPath, 'utf8'); - const parsed = JSON.parse(dbData); - const dimension = parsed.dimension || 384; - - const db = new VectorDB({ dimension }); - db.load(dbPath); + const { db } = openDbFromMeta(dbPath); spinner.text = 'Searching...'; @@ -251,18 +307,19 @@ program const query = { vector, k: parseInt(options.topK), - threshold: parseFloat(options.threshold) }; if (options.filter) { query.filter = JSON.parse(options.filter); } - const results = db.search(query); - spinner.succeed(chalk.green(`Found ${results.length} results`)); + const results = await db.search(query); + const threshold = parseFloat(options.threshold); + const filtered = threshold > 0 ? results.filter((r) => r.score >= threshold) : results; + spinner.succeed(chalk.green(`Found ${filtered.length} results`)); console.log(chalk.cyan('\nSearch Results:')); - results.forEach((result, i) => { + filtered.forEach((result, i) => { console.log(chalk.white(`\n${i + 1}. ID: ${result.id}`)); console.log(chalk.yellow(` Score: ${result.score.toFixed(4)}`)); if (result.metadata) { @@ -280,35 +337,27 @@ program program .command('stats ') .description('Show database statistics') - .action((dbPath) => { + .action(async (dbPath) => { requireRuvector(); const spinner = ora('Loading database...').start(); try { - const dbData = fs.readFileSync(dbPath, 'utf8'); - const parsed = JSON.parse(dbData); - const dimension = parsed.dimension || 384; + const { db, meta } = openDbFromMeta(dbPath); - const db = new VectorDB({ dimension }); - db.load(dbPath); - - const stats = db.stats(); + const count = await db.len(); spinner.succeed(chalk.green('Database statistics')); console.log(chalk.cyan('\nDatabase Stats:')); - console.log(chalk.white(` Vector Count: ${chalk.yellow(stats.count)}`)); - console.log(chalk.white(` Dimension: ${chalk.yellow(stats.dimension)}`)); - console.log(chalk.white(` Metric: ${chalk.yellow(stats.metric)}`)); + console.log(chalk.white(` Vector Count: ${chalk.yellow(count)}`)); + console.log(chalk.white(` Dimension: ${chalk.yellow(meta.dimensions)}`)); + console.log(chalk.white(` Metric: ${chalk.yellow(meta.metric || 'cosine')}`)); console.log(chalk.white(` Implementation: ${chalk.yellow(getImplementationType())}`)); - if (stats.memoryUsage) { - const mb = (stats.memoryUsage / (1024 * 1024)).toFixed(2); - console.log(chalk.white(` Memory Usage: ${chalk.yellow(mb + ' MB')}`)); + if (fs.existsSync(dbPath)) { + const fileStats = fs.statSync(dbPath); + const fileMb = (fileStats.size / (1024 * 1024)).toFixed(2); + console.log(chalk.white(` File Size: ${chalk.yellow(fileMb + ' MB')}`)); } - - const fileStats = fs.statSync(dbPath); - const fileMb = (fileStats.size / (1024 * 1024)).toFixed(2); - console.log(chalk.white(` File Size: ${chalk.yellow(fileMb + ' MB')}`)); } catch (error) { spinner.fail(chalk.red('Failed to load database')); console.error(chalk.red(error.message)); @@ -323,7 +372,7 @@ program .option('-d, --dimension ', 'Vector dimension', '384') .option('-n, --num-vectors ', 'Number of vectors', '10000') .option('-q, --num-queries ', 'Number of queries', '1000') - .action((options) => { + .action(async (options) => { requireRuvector(); console.log(chalk.cyan('\nruvector Performance Benchmark')); console.log(chalk.gray(`Implementation: ${getImplementationType()}\n`)); @@ -338,7 +387,7 @@ program const db = new VectorDB({ dimensions: dimension, metric: 'cosine' }); spinner.succeed(); - // Insert benchmark + // Insert benchmark — must await; the wrapper resolves on native completion. spinner = ora(`Inserting ${numVectors} vectors...`).start(); const insertStart = Date.now(); @@ -351,14 +400,15 @@ program }); } - db.insertBatch(vectors); + await db.insertBatch(vectors); const insertTime = Date.now() - insertStart; const insertRate = (numVectors / (insertTime / 1000)).toFixed(0); spinner.succeed(chalk.green(`Inserted ${numVectors} vectors in ${insertTime}ms`)); console.log(chalk.gray(` Rate: ${chalk.yellow(insertRate)} vectors/sec`)); - // Search benchmark + // Search benchmark — must await each query (otherwise promises are + // dropped on the floor and the reported rate is just spinner timing). spinner = ora(`Running ${numQueries} searches...`).start(); const searchStart = Date.now(); @@ -367,7 +417,7 @@ program vector: Array.from({ length: dimension }, () => Math.random()), k: 10 }; - db.search(query); + await db.search(query); } const searchTime = Date.now() - searchStart; @@ -1915,32 +1965,28 @@ program try { const outputFile = options.output || `${dbPath.replace(/\/$/, '')}_export.${options.format}`; - // Load database - const db = new VectorDB({ dimension: 384 }); // Will be overwritten by load - if (fs.existsSync(dbPath)) { - db.load(dbPath); - } else { - spinner.fail(chalk.red(`Database not found: ${dbPath}`)); - process.exit(1); - } + const { db, meta } = openDbFromMeta(dbPath); + const count = await db.len(); - const stats = db.getStats(); + // Note: @ruvector/core does not yet expose vector enumeration, so this + // export captures schema/metadata only — restore re-creates the schema. const data = { version: packageJson.version, exportedAt: new Date().toISOString(), - stats: stats, - vectors: [] // Would contain actual vector data + meta: { dimensions: meta.dimensions, metric: meta.metric || 'cosine' }, + stats: { count, dimensions: meta.dimensions, metric: meta.metric || 'cosine' }, + vectors: [], }; if (options.format === 'json') { fs.writeFileSync(outputFile, JSON.stringify(data, null, 2)); } else { - spinner.fail(chalk.yellow(`Format '${options.format}' not yet supported. Using JSON.`)); + spinner.warn(chalk.yellow(`Format '${options.format}' not yet supported. Using JSON.`)); fs.writeFileSync(outputFile.replace(/\.[^.]+$/, '.json'), JSON.stringify(data, null, 2)); } spinner.succeed(chalk.green(`Exported to: ${outputFile}`)); - console.log(chalk.gray(` Vectors: ${stats.count || 0}`)); + console.log(chalk.gray(` Vectors: ${count}`)); console.log(chalk.gray(` Format: ${options.format}`)); } catch (error) { spinner.fail(chalk.red('Export failed')); @@ -1967,21 +2013,34 @@ program const data = JSON.parse(fs.readFileSync(file, 'utf8')); const dbPath = options.database || file.replace(/_export\.json$/, ''); + const dimensions = data.meta?.dimensions || data.stats?.dimensions || data.stats?.dimension || 384; + const metric = data.meta?.metric || data.stats?.metric || 'cosine'; spinner.text = 'Creating database...'; - const db = new VectorDB({ - dimension: data.stats?.dimension || 384, - path: dbPath, - autoPersist: true + // Constructing with `storagePath` creates/opens the redb file. + const db = new VectorDB({ dimensions, metric, storagePath: dbPath }); + + // Restore vector rows if the export carried any (current exports do not — + // @ruvector/core has no enumeration API yet, see `export` above). + const vectors = Array.isArray(data.vectors) ? data.vectors : []; + vectors.forEach((v, idx) => { + if (v.id === undefined || v.id === null) v.id = String(idx); + else if (typeof v.id !== 'string') v.id = String(v.id); }); + if (vectors.length > 0) { + await db.insertBatch(vectors); + } - // Would import actual vectors here - db.save(dbPath); + // Recreate the metadata sidecar so the imported DB is usable by the + // other subcommands (#417). + writeDbMeta(dbPath, { dimensions, metric }); spinner.succeed(chalk.green(`Imported to: ${dbPath}`)); console.log(chalk.gray(` Source version: ${data.version}`)); console.log(chalk.gray(` Exported at: ${data.exportedAt}`)); + console.log(chalk.gray(` Vectors restored: ${vectors.length}`)); + console.log(chalk.gray(` Sidecar: ${metaPathFor(dbPath)}`)); } catch (error) { spinner.fail(chalk.red('Import failed')); console.error(chalk.red(error.message)); @@ -2044,7 +2103,15 @@ embedCmd } } } catch (e) { - console.error(chalk.red('Embedding failed:'), e.message); + const msg = e && e.message ? e.message : String(e); + if (/onnx wasm files not bundled|onnx\/pkg\/? directory is missing|cannot find module .*onnx/i.test(msg)) { + console.error(chalk.red('Embedding failed: the ONNX WASM runtime is not bundled with this install.')); + console.error(chalk.yellow(' This package ships the ONNX assets under dist/core/onnx/ — reinstall a recent')); + console.error(chalk.yellow(' release (>= 0.2.26): npm install ruvector@latest')); + } else { + console.error(chalk.red('Embedding failed:'), msg); + } + process.exitCode = 1; } }); diff --git a/npm/packages/ruvector/bin/mcp-server.js b/npm/packages/ruvector/bin/mcp-server.js index a900a6f1c..171179937 100644 --- a/npm/packages/ruvector/bin/mcp-server.js +++ b/npm/packages/ruvector/bin/mcp-server.js @@ -2302,13 +2302,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'hooks_route_enhanced': { try { - const safeTask = sanitizeShellArg(args.task); - let cmd = `npx ruvector hooks route-enhanced "${safeTask}"`; - if (args.file) cmd += ` --file "${sanitizeShellArg(args.file)}"`; - const output = execSync(cmd, { encoding: 'utf-8', timeout: 30000 }); + // Call the local CLI directly (no `npx` cold-start) — the routing + // logic itself runs in <200ms; the old `npx ruvector ...` form spent + // most of the 30s budget resolving the package and timed out (#422). + const cliPath = path.join(__dirname, 'cli.js'); + const cliArgs = ['hooks', 'route-enhanced', String(args.task)]; + if (args.file) cliArgs.push('--file', String(args.file)); + const output = execFileSync(process.execPath, [cliPath, ...cliArgs], { encoding: 'utf-8', timeout: 30000 }); return { content: [{ type: 'text', text: output }] }; } catch (e) { - return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }] }; + return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message, stdout: e.stdout, stderr: e.stderr }, null, 2) }] }; } } @@ -3239,7 +3242,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { // ── Brain Tool Handlers ───────────────────────────────────────────── case 'brain_search': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3247,7 +3250,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const results = await client.search(args.query, { limit: args.limit || 10, category: args.category }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...results }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3256,7 +3259,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_share': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3264,7 +3267,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.share({ title: args.title, content: args.content, category: args.category || 'pattern', tags: args.tags }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3273,7 +3276,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_get': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3281,7 +3284,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.get(args.id); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3290,7 +3293,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_vote': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3298,7 +3301,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.vote(args.id, args.direction); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3307,7 +3310,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_list': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3315,7 +3318,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const results = await client.list({ category: args.category, limit: args.limit || 20 }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...results }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3324,7 +3327,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_delete': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3332,7 +3335,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.delete(args.id); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3341,7 +3344,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_status': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3349,7 +3352,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.status(); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3358,7 +3361,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_drift': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3366,7 +3369,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.drift({ domain: args.domain }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3375,7 +3378,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_partition': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3383,7 +3386,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.partition({ domain: args.domain, min_cluster_size: args.min_cluster_size || 3 }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3392,7 +3395,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_transfer': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3400,7 +3403,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.transfer(args.source, args.target); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; @@ -3409,7 +3412,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case 'brain_sync': { try { - const piBrain = require('@ruvector/pi-brain'); + const piBrain = await import('@ruvector/pi-brain'); const PiBrainClient = piBrain.PiBrainClient || piBrain.default; const url = process.env.BRAIN_URL || 'https://pi.ruv.io'; const key = process.env.PI || ''; @@ -3417,7 +3420,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const result = await client.sync({ direction: args.direction || 'both' }); return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }, null, 2) }] }; } catch (e) { - if (e.code === 'MODULE_NOT_FOUND') { + if (e.code === 'MODULE_NOT_FOUND' || e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: 'Brain tools require @ruvector/pi-brain', hint: 'npm install @ruvector/pi-brain' }, null, 2) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: e.message }, null, 2) }], isError: true }; diff --git a/npm/packages/ruvector/package.json b/npm/packages/ruvector/package.json index 7af805604..5b54ac8fb 100644 --- a/npm/packages/ruvector/package.json +++ b/npm/packages/ruvector/package.json @@ -1,6 +1,6 @@ { "name": "ruvector", - "version": "0.2.25", + "version": "0.2.26", "description": "Self-learning vector database for Node.js — hybrid search, Graph RAG, FlashAttention-3, HNSW, 50+ attention mechanisms", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -8,7 +8,7 @@ "ruvector": "./bin/cli.js" }, "scripts": { - "build": "tsc && cp src/core/onnx/pkg/package.json dist/core/onnx/pkg/", + "build": "tsc && node scripts/copy-onnx.js", "verify-dist": "node scripts/verify-dist.js", "prepublishOnly": "npm run build && npm run verify-dist", "test": "node test/integration.js && node test/cli-commands.js" @@ -90,6 +90,7 @@ "files": [ "bin/", "dist/", + "src/core/onnx/", "src/decompiler/", "wasm/", "README.md", diff --git a/npm/packages/ruvector/scripts/copy-onnx.js b/npm/packages/ruvector/scripts/copy-onnx.js new file mode 100644 index 000000000..3ec13f931 --- /dev/null +++ b/npm/packages/ruvector/scripts/copy-onnx.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/** + * copy-onnx.js — copy the bundled ONNX WASM runtime assets into `dist/`. + * + * `tsc` only emits `.js`/`.d.ts` from TypeScript sources, so the prebuilt + * wasm-bindgen artifacts under `src/core/onnx/` (`loader.js`, the `pkg/*.js` + * glue, and `pkg/*.wasm`) never reach `dist/core/onnx/`. Without them the + * ONNX embedder throws "ONNX WASM files not bundled" on a clean install + * (issue #354). This recursively mirrors `src/core/onnx/` → `dist/core/onnx/`, + * skipping editor/tooling junk (`.claude-flow`, etc.). + */ + +const fs = require('fs'); +const path = require('path'); + +const pkgRoot = path.resolve(__dirname, '..'); +const srcDir = path.join(pkgRoot, 'src', 'core', 'onnx'); +const destDir = path.join(pkgRoot, 'dist', 'core', 'onnx'); + +const SKIP = new Set(['.claude-flow', '.DS_Store', 'node_modules']); + +function copyRecursive(src, dest) { + const stat = fs.statSync(src); + if (stat.isDirectory()) { + fs.mkdirSync(dest, { recursive: true }); + for (const entry of fs.readdirSync(src)) { + if (SKIP.has(entry)) continue; + copyRecursive(path.join(src, entry), path.join(dest, entry)); + } + } else { + fs.mkdirSync(path.dirname(dest), { recursive: true }); + fs.copyFileSync(src, dest); + } +} + +if (!fs.existsSync(srcDir)) { + console.error(`copy-onnx: source directory not found: ${srcDir}`); + process.exit(1); +} + +// Replace any stale copy so removed files don't linger. +fs.rmSync(destDir, { recursive: true, force: true }); +copyRecursive(srcDir, destDir); + +const required = [ + 'loader.js', + path.join('pkg', 'loader.js'), + path.join('pkg', 'ruvector_onnx_embeddings_wasm.js'), + path.join('pkg', 'ruvector_onnx_embeddings_wasm_bg.js'), + path.join('pkg', 'ruvector_onnx_embeddings_wasm_bg.wasm'), + path.join('pkg', 'package.json'), +]; +const missing = required.filter((rel) => !fs.existsSync(path.join(destDir, rel))); +if (missing.length > 0) { + console.error(`copy-onnx: expected ONNX assets missing after copy: ${missing.join(', ')}`); + process.exit(1); +} + +console.log(`copy-onnx: mirrored ${required.length}+ ONNX assets to dist/core/onnx/`); diff --git a/npm/packages/ruvector/scripts/verify-dist.js b/npm/packages/ruvector/scripts/verify-dist.js index ad24b6fe9..85598bec6 100644 --- a/npm/packages/ruvector/scripts/verify-dist.js +++ b/npm/packages/ruvector/scripts/verify-dist.js @@ -1,51 +1,89 @@ #!/usr/bin/env node /** - * verify-dist.js — pre-publish gate that fails the build if any file - * `bin/cli.js` requires from `../dist/...` is missing. + * verify-dist.js — pre-publish gate that fails the build if the published + * artifact would be incomplete. * - * Why: 0.2.23 was published without a `dist/` directory at all (issue #399), - * which silently broke `ruvector doctor`, the entire `embed` subsystem, and - * `rvf` commands. tsc was supposed to run via `prepublishOnly`, but the - * hook didn't fire (or the build failed silently). This script makes the - * publish itself fail loudly when the artifact is incomplete. + * History: + * - 0.2.23 was published without a `dist/` directory at all (issue #399), + * which silently broke `ruvector doctor`, the entire `embed` subsystem, + * and `rvf` commands. + * - A later publish slipped without `dist/index.js` (issue #376). + * - Clean installs were missing the ONNX WASM runtime assets (issue #354). + * + * tsc was supposed to run via `prepublishOnly`, but the hook didn't always + * fire (or the build failed silently). This script makes the publish itself + * fail loudly when the artifact is incomplete. It runs AFTER `npm run build`. */ const fs = require('fs'); const path = require('path'); const pkgRoot = path.resolve(__dirname, '..'); -const cliPath = path.join(pkgRoot, 'bin', 'cli.js'); -if (!fs.existsSync(cliPath)) { - console.error('verify-dist: bin/cli.js not found — package layout is broken.'); +function fail(lines) { + for (const line of lines) console.error(line); process.exit(1); } -const cliSource = fs.readFileSync(cliPath, 'utf8'); +// 1. Core entry points + ONNX runtime assets that MUST exist regardless of +// what bin/cli.js references. +const REQUIRED = [ + 'dist/index.js', + 'dist/index.d.ts', + 'bin/cli.js', + // ONNX WASM runtime assets (issue #354) — clean installs need these to embed. + 'dist/core/onnx/loader.js', + 'dist/core/onnx/pkg/loader.js', + 'dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm.js', + 'dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.js', + 'dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.wasm', + 'dist/core/onnx/pkg/package.json', +]; + +const missingRequired = REQUIRED.filter( + (rel) => !fs.existsSync(path.join(pkgRoot, rel)), +); +if (missingRequired.length > 0) { + fail([ + `verify-dist: ${missingRequired.length} required artifact(s) missing:`, + ...missingRequired.map((rel) => ` - ${rel}`), + '', + 'Run `npm run build` (tsc + scripts/copy-onnx.js) and re-check.', + ]); +} -// Collect every `require('../dist/...')` referenced by the CLI. +// 2. Every `require('../dist/...')` referenced by bin/cli.js must resolve. +const cliPath = path.join(pkgRoot, 'bin', 'cli.js'); +const cliSource = fs.readFileSync(cliPath, 'utf8'); const distRequires = Array.from( cliSource.matchAll(/require\(['"]\.\.\/(dist\/[^'"]+\.js)['"]\)/g), (m) => m[1], ); -const unique = Array.from(new Set(distRequires)).sort(); - -const missing = unique.filter( +const uniqueDistRequires = Array.from(new Set(distRequires)).sort(); +const missingDist = uniqueDistRequires.filter( (rel) => !fs.existsSync(path.join(pkgRoot, rel)), ); +if (missingDist.length > 0) { + fail([ + `verify-dist: ${missingDist.length} dist file(s) referenced by bin/cli.js are missing:`, + ...missingDist.map((rel) => ` - ${rel}`), + '', + 'Run `npm run build` and confirm tsc emitted under dist/. If a path was renamed,', + 'update bin/cli.js to match.', + ]); +} -if (missing.length > 0) { - console.error( - `verify-dist: ${missing.length} dist file(s) referenced by bin/cli.js are missing:`, - ); - for (const rel of missing) { - console.error(` - ${rel}`); - } - console.error( - "\nRun `npm run build` and confirm tsc emitted under dist/. If a path was renamed,", - ); - console.error('update bin/cli.js to match.'); - process.exit(1); +// 3. The npm tarball must actually carry `dist/` — i.e. it must be in `files`. +const pkgJson = require(path.join(pkgRoot, 'package.json')); +const files = Array.isArray(pkgJson.files) ? pkgJson.files : []; +const hasDist = files.some((f) => f === 'dist' || f === 'dist/' || f.startsWith('dist/')); +if (!hasDist) { + fail([ + 'verify-dist: package.json "files" does not include "dist/" — the built', + 'artifact would not be published. Add "dist/" to the files array.', + ]); } -console.log(`verify-dist: ${unique.length} dist path(s) present.`); +console.log( + `verify-dist: ${REQUIRED.length} required artifact(s) + ${uniqueDistRequires.length} cli dist path(s) present; "dist/" is published.`, +); diff --git a/npm/packages/ruvector/src/core/intelligence-engine.ts b/npm/packages/ruvector/src/core/intelligence-engine.ts index 064a9c00d..abc9e5373 100644 --- a/npm/packages/ruvector/src/core/intelligence-engine.ts +++ b/npm/packages/ruvector/src/core/intelligence-engine.ts @@ -275,20 +275,26 @@ export class IntelligenceEngine { * Generate embedding using ONNX, attention, or hash (in order of preference) */ embed(text: string): number[] { - const dim = this.config.embeddingDim; - - // Try ONNX semantic embeddings first (best quality) - if (this.onnxReady && this.onnxEmbedder) { - try { - // Note: This is sync wrapper for async ONNX - // For full async, use embedAsync - return this.hashEmbed(text, dim); // Fallback for sync context - } catch { - // Fall through - } + // If an ONNX embedder is configured we MUST NOT silently substitute a + // hash vector — sync `embed()` and async `embedAsync()` would otherwise + // produce values in completely different vector spaces (and, depending + // on `embeddingDim`, different lengths), which `cosineSimilarity` then + // either silently zeroes or computes nonsense for. Fail loudly so the + // caller switches to `embedAsync()` (or unsets the ONNX embedder). #316 + if (this.onnxEmbedder) { + throw new Error( + 'IntelligenceEngine.embed() is sync but an ONNX embedder is configured; ' + + 'call embedAsync(text) to get an ONNX-space vector, or construct the engine ' + + 'with useOnnx:false to use the synchronous hash embedder.' + ); } + return this.embedSyncNoOnnx(text); + } - // Try to use attention-based embedding + /** Sync attention/hash path used by both `embed()` and the `embedAsync()` + * ONNX-failure fallback. Never returns an ONNX-space vector. */ + private embedSyncNoOnnx(text: string): number[] { + const dim = this.config.embeddingDim; if (this.attention?.DotProductAttention) { try { return this.attentionEmbed(text, dim); @@ -296,8 +302,6 @@ export class IntelligenceEngine { // Fall through to hash embedding } } - - // Improved positional hash embedding return this.hashEmbed(text, dim); } @@ -314,12 +318,12 @@ export class IntelligenceEngine { } return await this.onnxEmbedder.embed(text); } catch { - // Fall through to sync methods + // Fall through to sync attention/hash — bypass the user-facing + // sync embed() which now throws when an ONNX embedder is configured. } } - // Fall back to sync embedding - return this.embed(text); + return this.embedSyncNoOnnx(text); } /** @@ -482,22 +486,31 @@ export class IntelligenceEngine { // Use async ONNX embeddings if available for better semantic quality const queryEmbed = await this.embedAsync(query); - // Try VectorDB search first (HNSW - 150x faster) + // Try VectorDB search first (HNSW - 150x faster). When the index has + // not been populated for this engine (e.g. after `import()` of a payload + // that lacked embeddings, or before any `remember()`), an empty result + // is NOT an error — it's just "nothing indexed". Fall through to the + // brute-force scan over `this.memories` instead of returning []. (#315) if (this.vectorDb) { try { const results = await this.vectorDb.search({ vector: new Float32Array(queryEmbed), k: topK, }); - - return results.map((r: any) => { - const entry = this.memories.get(r.id); - if (entry) { - entry.accessed++; - entry.score = 1 - r.score; // Convert distance to similarity - } - return entry; - }).filter((e: any): e is MemoryEntry => e !== null); + if (results && results.length > 0) { + const mapped = results + .map((r: any) => { + const entry = this.memories.get(r.id); + if (entry) { + entry.accessed++; + entry.score = 1 - r.score; // distance → similarity + } + return entry; + }) + .filter((e: any): e is MemoryEntry => e !== null && e !== undefined); + if (mapped.length > 0) return mapped; + } + // empty → fall through to brute force } catch { // Fall through to brute force } @@ -1075,10 +1088,28 @@ export class IntelligenceEngine { this.agentMappings.clear(); } - // Import memories + // Import memories — populate both the Map and (if present) the HNSW + // VectorDB. Without the vectorDb.insert() call, `recall()` after import + // would call vectorDb.search() against an empty index, get [] back, and + // return immediately — silently producing no results. (#315) if (data.memories) { for (const mem of data.memories) { this.memories.set(mem.id, mem); + if (this.vectorDb && mem.embedding && Array.isArray(mem.embedding)) { + try { + this.vectorDb.insert({ + id: mem.id, + vector: new Float32Array(mem.embedding), + metadata: JSON.stringify({ + content: mem.content, + type: mem.type, + created: mem.created, + }), + }); + } catch { + // Best-effort: brute-force recall fallback still covers this row. + } + } } } diff --git a/npm/packages/ruvector/src/core/onnx-embedder.js b/npm/packages/ruvector/src/core/onnx-embedder.js index 6150fbe66..76c3626a1 100644 --- a/npm/packages/ruvector/src/core/onnx-embedder.js +++ b/npm/packages/ruvector/src/core/onnx-embedder.js @@ -181,23 +181,30 @@ async function initOnnxEmbedder(config = {}) { loadPromise = (async () => { try { // Paths to bundled ONNX files - const pkgPath = path.join(__dirname, 'onnx', 'pkg', 'ruvector_onnx_embeddings_wasm.js'); + const pkgDir = path.join(__dirname, 'onnx', 'pkg'); + const bgJsPath = path.join(pkgDir, 'ruvector_onnx_embeddings_wasm_bg.js'); + const wasmPath = path.join(pkgDir, 'ruvector_onnx_embeddings_wasm_bg.wasm'); const loaderPath = path.join(__dirname, 'onnx', 'loader.js'); - if (!fs.existsSync(pkgPath)) { - throw new Error('ONNX WASM files not bundled. The onnx/ directory is missing.'); + if (!fs.existsSync(bgJsPath) || !fs.existsSync(wasmPath)) { + throw new Error('ONNX WASM files not bundled. The onnx/pkg/ directory is missing.'); } // Convert paths to file:// URLs for cross-platform ESM compatibility (Windows fix) - const pkgUrl = (0, url_1.pathToFileURL)(pkgPath).href; + const bgJsUrl = (0, url_1.pathToFileURL)(bgJsPath).href; const loaderUrl = (0, url_1.pathToFileURL)(loaderPath).href; - // Dynamic import of bundled modules using file:// URLs - wasmModule = await dynamicImport(pkgUrl); - // Initialize WASM module (loads the .wasm file) - const wasmPath = path.join(__dirname, 'onnx', 'pkg', 'ruvector_onnx_embeddings_wasm_bg.wasm'); - if (wasmModule.default && typeof wasmModule.default === 'function') { - // For bundler-style initialization, pass the wasm buffer - const wasmBytes = fs.readFileSync(wasmPath); - await wasmModule.default(wasmBytes); + // Instantiate the WASM module by reading its bytes directly rather than + // letting Node ESM resolve `import * as wasm from "./...wasm"` (the + // wasm-bindgen "bundler" target glue), which requires the + // `--experimental-wasm-modules` flag on Node 18-24 (issue #323). + const bgModule = await dynamicImport(bgJsUrl); + const wasmBytes = fs.readFileSync(wasmPath); + const { instance } = await WebAssembly.instantiate(wasmBytes, { + './ruvector_onnx_embeddings_wasm_bg.js': bgModule, + }); + bgModule.__wbg_set_wasm(instance.exports); + if (typeof instance.exports.__wbindgen_start === 'function') { + instance.exports.__wbindgen_start(); } + wasmModule = bgModule; const loaderModule = await dynamicImport(loaderUrl); const { ModelLoader } = loaderModule; // Create model loader with caching diff --git a/npm/packages/ruvector/src/core/onnx-embedder.ts b/npm/packages/ruvector/src/core/onnx-embedder.ts index 491195e8b..a3aa1f12b 100644 --- a/npm/packages/ruvector/src/core/onnx-embedder.ts +++ b/npm/packages/ruvector/src/core/onnx-embedder.ts @@ -177,27 +177,38 @@ export async function initOnnxEmbedder(config: OnnxEmbedderConfig = {}): Promise loadPromise = (async () => { try { // Paths to bundled ONNX files - const pkgPath = path.join(__dirname, 'onnx', 'pkg', 'ruvector_onnx_embeddings_wasm.js'); + const pkgDir = path.join(__dirname, 'onnx', 'pkg'); + const bgJsPath = path.join(pkgDir, 'ruvector_onnx_embeddings_wasm_bg.js'); + const wasmPath = path.join(pkgDir, 'ruvector_onnx_embeddings_wasm_bg.wasm'); const loaderPath = path.join(__dirname, 'onnx', 'loader.js'); - if (!fs.existsSync(pkgPath)) { - throw new Error('ONNX WASM files not bundled. The onnx/ directory is missing.'); + if (!fs.existsSync(bgJsPath) || !fs.existsSync(wasmPath)) { + throw new Error('ONNX WASM files not bundled. The onnx/pkg/ directory is missing.'); } // Convert paths to file:// URLs for cross-platform ESM compatibility (Windows fix) - const pkgUrl = pathToFileURL(pkgPath).href; + const bgJsUrl = pathToFileURL(bgJsPath).href; const loaderUrl = pathToFileURL(loaderPath).href; - // Dynamic import of bundled modules using file:// URLs - wasmModule = await dynamicImport(pkgUrl); - - // Initialize WASM module (loads the .wasm file) - const wasmPath = path.join(__dirname, 'onnx', 'pkg', 'ruvector_onnx_embeddings_wasm_bg.wasm'); - if (wasmModule.default && typeof wasmModule.default === 'function') { - // For bundler-style initialization, pass the wasm buffer - const wasmBytes = fs.readFileSync(wasmPath); - await wasmModule.default(wasmBytes); + // Instantiate the WASM module by reading its bytes directly rather than + // letting Node ESM resolve `import * as wasm from "./...wasm"` (the + // wasm-bindgen "bundler" target glue), which requires the + // `--experimental-wasm-modules` flag on Node 18-24 (issue #323). + const bgModule = await dynamicImport(bgJsUrl); + const wasmBytes = fs.readFileSync(wasmPath); + // `WebAssembly` is a JS global available in every Node/browser runtime, + // but isn't in the `lib: ["ES2020"]` TS typings, hence the cast. + const wasmRuntime = (globalThis as any).WebAssembly as { + instantiate(bytes: Uint8Array, imports: Record): Promise<{ instance: { exports: any } }>; + }; + const { instance } = await wasmRuntime.instantiate(wasmBytes, { + './ruvector_onnx_embeddings_wasm_bg.js': bgModule, + }); + bgModule.__wbg_set_wasm(instance.exports); + if (typeof instance.exports.__wbindgen_start === 'function') { + instance.exports.__wbindgen_start(); } + wasmModule = bgModule; const loaderModule = await dynamicImport(loaderUrl); const { ModelLoader } = loaderModule; diff --git a/npm/packages/ruvector/src/core/onnx-optimized.js b/npm/packages/ruvector/src/core/onnx-optimized.js index d46b53097..66fe664de 100644 --- a/npm/packages/ruvector/src/core/onnx-optimized.js +++ b/npm/packages/ruvector/src/core/onnx-optimized.js @@ -197,20 +197,26 @@ class OptimizedOnnxEmbedder { async doInit() { try { // Load bundled WASM module - const pkgPath = path.join(__dirname, 'onnx', 'pkg', 'ruvector_onnx_embeddings_wasm.js'); + const pkgDir = path.join(__dirname, 'onnx', 'pkg'); + const bgJsPath = path.join(pkgDir, 'ruvector_onnx_embeddings_wasm_bg.js'); + const wasmPath = path.join(pkgDir, 'ruvector_onnx_embeddings_wasm_bg.wasm'); const loaderPath = path.join(__dirname, 'onnx', 'loader.js'); - if (!fs.existsSync(pkgPath)) { + if (!fs.existsSync(bgJsPath) || !fs.existsSync(wasmPath)) { throw new Error('ONNX WASM files not bundled'); } - const pkgUrl = (0, url_1.pathToFileURL)(pkgPath).href; + const bgJsUrl = (0, url_1.pathToFileURL)(bgJsPath).href; const loaderUrl = (0, url_1.pathToFileURL)(loaderPath).href; - this.wasmModule = await dynamicImport(pkgUrl); - // Initialize WASM - const wasmPath = path.join(__dirname, 'onnx', 'pkg', 'ruvector_onnx_embeddings_wasm_bg.wasm'); - if (this.wasmModule.default && typeof this.wasmModule.default === 'function') { - const wasmBytes = fs.readFileSync(wasmPath); - await this.wasmModule.default(wasmBytes); + // Instantiate WASM from bytes (avoids `--experimental-wasm-modules`, issue #323) + const bgModule = await dynamicImport(bgJsUrl); + const wasmBytes = fs.readFileSync(wasmPath); + const { instance } = await WebAssembly.instantiate(wasmBytes, { + './ruvector_onnx_embeddings_wasm_bg.js': bgModule, + }); + bgModule.__wbg_set_wasm(instance.exports); + if (typeof instance.exports.__wbindgen_start === 'function') { + instance.exports.__wbindgen_start(); } + this.wasmModule = bgModule; const loaderModule = await dynamicImport(loaderUrl); const { ModelLoader } = loaderModule; // Select model URL based on quantization preference diff --git a/npm/packages/ruvector/src/core/onnx-optimized.ts b/npm/packages/ruvector/src/core/onnx-optimized.ts index 971e2a66b..c762a6711 100644 --- a/npm/packages/ruvector/src/core/onnx-optimized.ts +++ b/npm/packages/ruvector/src/core/onnx-optimized.ts @@ -218,24 +218,32 @@ export class OptimizedOnnxEmbedder { private async doInit(): Promise { try { // Load bundled WASM module - const pkgPath = path.join(__dirname, 'onnx', 'pkg', 'ruvector_onnx_embeddings_wasm.js'); + const pkgDir = path.join(__dirname, 'onnx', 'pkg'); + const bgJsPath = path.join(pkgDir, 'ruvector_onnx_embeddings_wasm_bg.js'); + const wasmPath = path.join(pkgDir, 'ruvector_onnx_embeddings_wasm_bg.wasm'); const loaderPath = path.join(__dirname, 'onnx', 'loader.js'); - if (!fs.existsSync(pkgPath)) { + if (!fs.existsSync(bgJsPath) || !fs.existsSync(wasmPath)) { throw new Error('ONNX WASM files not bundled'); } - const pkgUrl = pathToFileURL(pkgPath).href; + const bgJsUrl = pathToFileURL(bgJsPath).href; const loaderUrl = pathToFileURL(loaderPath).href; - this.wasmModule = await dynamicImport(pkgUrl); - - // Initialize WASM - const wasmPath = path.join(__dirname, 'onnx', 'pkg', 'ruvector_onnx_embeddings_wasm_bg.wasm'); - if (this.wasmModule.default && typeof this.wasmModule.default === 'function') { - const wasmBytes = fs.readFileSync(wasmPath); - await this.wasmModule.default(wasmBytes); + // Instantiate WASM from bytes (avoids `--experimental-wasm-modules`, issue #323) + const bgModule = await dynamicImport(bgJsUrl); + const wasmBytes = fs.readFileSync(wasmPath); + const wasmRuntime = (globalThis as any).WebAssembly as { + instantiate(bytes: Uint8Array, imports: Record): Promise<{ instance: { exports: any } }>; + }; + const { instance } = await wasmRuntime.instantiate(wasmBytes, { + './ruvector_onnx_embeddings_wasm_bg.js': bgModule, + }); + bgModule.__wbg_set_wasm(instance.exports); + if (typeof instance.exports.__wbindgen_start === 'function') { + instance.exports.__wbindgen_start(); } + this.wasmModule = bgModule; const loaderModule = await dynamicImport(loaderUrl); const { ModelLoader } = loaderModule; diff --git a/npm/packages/rvf-wasm/package.json b/npm/packages/rvf-wasm/package.json index 9cc2dcd41..92a6c5852 100644 --- a/npm/packages/rvf-wasm/package.json +++ b/npm/packages/rvf-wasm/package.json @@ -1,16 +1,20 @@ { "name": "@ruvector/rvf-wasm", - "version": "0.1.5", + "version": "0.1.7", "description": "RuVector Format WASM microkernel for browser and edge vector operations", - "main": "pkg/rvf_wasm.js", + "type": "module", + "main": "pkg/rvf_wasm.mjs", + "module": "pkg/rvf_wasm.mjs", "types": "pkg/rvf_wasm.d.ts", "exports": { ".": { "types": "./pkg/rvf_wasm.d.ts", "import": "./pkg/rvf_wasm.mjs", - "require": "./pkg/rvf_wasm.js", - "default": "./pkg/rvf_wasm.js" - } + "default": "./pkg/rvf_wasm.mjs" + }, + "./pkg/rvf_wasm.js": "./pkg/rvf_wasm.js", + "./pkg/rvf_wasm.mjs": "./pkg/rvf_wasm.mjs", + "./package.json": "./package.json" }, "files": [ "pkg/" diff --git a/npm/packages/rvf-wasm/pkg/rvf_wasm.js b/npm/packages/rvf-wasm/pkg/rvf_wasm.js index 61e589a2d..421fa5d7c 100644 --- a/npm/packages/rvf-wasm/pkg/rvf_wasm.js +++ b/npm/packages/rvf-wasm/pkg/rvf_wasm.js @@ -4,12 +4,15 @@ * Loads the .wasm binary and re-exports all C-ABI functions plus the * WASM linear memory object. * - * Works in Node.js (CJS/ESM) and browsers. + * This package is ESM-only ("type": "module"). Use it via: + * import init from '@ruvector/rvf-wasm'; + * or from CommonJS via dynamic import: + * const { default: init } = await import('@ruvector/rvf-wasm'); */ -var wasmInstance = null; +let wasmInstance = null; -var _isNode = typeof process !== 'undefined' && +const _isNode = typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions.node === 'string'; @@ -23,50 +26,49 @@ var _isNode = typeof process !== 'undefined' && async function init(input) { if (wasmInstance) return wasmInstance; - var wasmBytes; + let wasmBytes; if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) { wasmBytes = input; } else if (typeof WebAssembly !== 'undefined' && input instanceof WebAssembly.Module) { - var inst = await WebAssembly.instantiate(input, {}); + const inst = await WebAssembly.instantiate(input, {}); wasmInstance = inst.exports; return wasmInstance; } else if (_isNode) { // Node.js: always use readFile (fetch on file:// is unreliable) - var fs = await import('node:fs/promises'); - var url = await import('node:url'); - var path = await import('node:path'); - var wasmPath; + const fs = await import('node:fs/promises'); + const url = await import('node:url'); + const path = await import('node:path'); + let wasmPath; if (typeof input === 'string') { wasmPath = input; - } else if (typeof __dirname !== 'undefined') { - // CJS context - wasmPath = path.default.join(__dirname, 'rvf_wasm_bg.wasm'); } else { - // ESM context — import.meta.url available - var thisDir = path.default.dirname(url.default.fileURLToPath(import.meta.url)); + // ESM context — import.meta.url is available + const thisDir = path.default.dirname(url.default.fileURLToPath(import.meta.url)); wasmPath = path.default.join(thisDir, 'rvf_wasm_bg.wasm'); } wasmBytes = await fs.default.readFile(wasmPath); } else { // Browser: use fetch + instantiateStreaming - var wasmUrl = new URL('rvf_wasm_bg.wasm', import.meta.url); + const wasmUrl = new URL('rvf_wasm_bg.wasm', import.meta.url); if (typeof WebAssembly.instantiateStreaming === 'function') { - var resp = await fetch(wasmUrl); - var result = await WebAssembly.instantiateStreaming(resp, {}); + const resp = await fetch(wasmUrl); + const result = await WebAssembly.instantiateStreaming(resp, {}); wasmInstance = result.instance.exports; return wasmInstance; } - var resp2 = await fetch(wasmUrl); + const resp2 = await fetch(wasmUrl); wasmBytes = await resp2.arrayBuffer(); } - var compiled = await WebAssembly.instantiate(wasmBytes, {}); + const compiled = await WebAssembly.instantiate(wasmBytes, {}); wasmInstance = compiled.instance.exports; return wasmInstance; } -// Support both ESM (export default) and CJS (module.exports) +// Back-compat: `mod.default` resolves to the init function whether the caller +// reads the ESM default export or destructures it off the namespace object. init.default = init; -if (typeof module !== 'undefined') module.exports = init; + export default init; +export { init }; diff --git a/npm/packages/rvf-wasm/pkg/rvf_wasm.mjs b/npm/packages/rvf-wasm/pkg/rvf_wasm.mjs index d62265eb2..91842f7fd 100644 --- a/npm/packages/rvf-wasm/pkg/rvf_wasm.mjs +++ b/npm/packages/rvf-wasm/pkg/rvf_wasm.mjs @@ -1,6 +1,9 @@ /** * @ruvector/rvf-wasm ESM entry point. - * Re-exports the init function from the CJS-compatible module. + * + * Re-exports the init function. The package is ESM-only ("type": "module"), + * so both rvf_wasm.js and rvf_wasm.mjs are ES modules. */ import init from './rvf_wasm.js'; export default init; +export { init }; diff --git a/npm/packages/rvf/dist/backend.js b/npm/packages/rvf/dist/backend.js index 64ffa6c7c..bc204493a 100644 --- a/npm/packages/rvf/dist/backend.js +++ b/npm/packages/rvf/dist/backend.js @@ -37,6 +37,19 @@ exports.WasmBackend = exports.NodeBackend = void 0; exports.resolveBackend = resolveBackend; const errors_1 = require("./errors"); // --------------------------------------------------------------------------- +// ESM-only dynamic import helper. +// +// `@ruvector/rvf-wasm@>=0.1.7` is published as an ESM-only package +// (`"type": "module"`). When this file is transpiled to CommonJS, `tsc` +// rewrites `await import('pkg')` into `require('pkg')`, which fails on +// ESM-only packages on older Node (`ERR_REQUIRE_ESM`). Routing the import +// through `new Function` keeps it as a genuine `import()` expression in the +// emitted JS, so it works from both CJS and ESM consumers. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const dynamicImport = +// eslint-disable-next-line @typescript-eslint/no-implied-eval +new Function('specifier', 'return import(specifier);'); +// --------------------------------------------------------------------------- // NodeBackend — wraps @ruvector/rvf-node (N-API) // --------------------------------------------------------------------------- /** @@ -439,7 +452,9 @@ class WasmBackend { if (this.wasm) return; try { - const mod = await Promise.resolve().then(() => __importStar(require('@ruvector/rvf-wasm'))); + // @ruvector/rvf-wasm is ESM-only — use the non-transpiled dynamic + // import helper so this works from CommonJS consumers too. + const mod = await dynamicImport('@ruvector/rvf-wasm'); // wasm-pack default export is the init function if (typeof mod.default === 'function') { this.wasm = await mod.default(); diff --git a/npm/packages/rvf/dist/backend.js.map b/npm/packages/rvf/dist/backend.js.map index 274194344..ed9fdfda3 100644 --- a/npm/packages/rvf/dist/backend.js.map +++ b/npm/packages/rvf/dist/backend.js.map @@ -1 +1 @@ -{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/npm/packages/rvf/package.json b/npm/packages/rvf/package.json index 844c0c3cd..d6a2dc97a 100644 --- a/npm/packages/rvf/package.json +++ b/npm/packages/rvf/package.json @@ -1,6 +1,6 @@ { "name": "@ruvector/rvf", - "version": "0.2.0", + "version": "0.2.1", "description": "RuVector Format — unified TypeScript SDK for vector intelligence", "main": "dist/index.js", "module": "dist/index.js", @@ -26,7 +26,7 @@ "@ruvector/rvf-node": "^0.1.7" }, "optionalDependencies": { - "@ruvector/rvf-wasm": "^0.1.5", + "@ruvector/rvf-wasm": "^0.1.7", "@ruvector/rvf-solver": "^0.1.0" }, "devDependencies": { diff --git a/npm/packages/rvf/src/backend.ts b/npm/packages/rvf/src/backend.ts index 0a59409f6..a4f36b1f2 100644 --- a/npm/packages/rvf/src/backend.ts +++ b/npm/packages/rvf/src/backend.ts @@ -15,6 +15,23 @@ import type { } from './types'; import { RvfError, RvfErrorCode } from './errors'; +// --------------------------------------------------------------------------- +// ESM-only dynamic import helper. +// +// `@ruvector/rvf-wasm@>=0.1.7` is published as an ESM-only package +// (`"type": "module"`). When this file is transpiled to CommonJS, `tsc` +// rewrites `await import('pkg')` into `require('pkg')`, which fails on +// ESM-only packages on older Node (`ERR_REQUIRE_ESM`). Routing the import +// through `new Function` keeps it as a genuine `import()` expression in the +// emitted JS, so it works from both CJS and ESM consumers. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const dynamicImport: (specifier: string) => Promise = + // eslint-disable-next-line @typescript-eslint/no-implied-eval + new Function('specifier', 'return import(specifier);') as ( + specifier: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) => Promise; + // --------------------------------------------------------------------------- // Backend interface — every backend (node, wasm) must implement this. // --------------------------------------------------------------------------- @@ -479,7 +496,9 @@ export class WasmBackend implements RvfBackend { private async loadWasm(): Promise { if (this.wasm) return; try { - const mod = await import('@ruvector/rvf-wasm'); + // @ruvector/rvf-wasm is ESM-only — use the non-transpiled dynamic + // import helper so this works from CommonJS consumers too. + const mod = await dynamicImport('@ruvector/rvf-wasm'); // wasm-pack default export is the init function if (typeof mod.default === 'function') { this.wasm = await mod.default(); diff --git a/scripts/ci/check-intelligence-engine-invariants.mjs b/scripts/ci/check-intelligence-engine-invariants.mjs new file mode 100644 index 000000000..dcd01fa26 --- /dev/null +++ b/scripts/ci/check-intelligence-engine-invariants.mjs @@ -0,0 +1,75 @@ +#!/usr/bin/env node +// Regression guard for IntelligenceEngine behaviour: +// #315 — after `export()` → `import()` on a fresh engine, `recall()` must +// still return the indexed memories (the import path used to skip +// re-inserting into the HNSW VectorDB, and recall() returned [] in +// silence instead of falling back to the brute-force scan). +// #316 — sync `embed()` must NOT return a hash vector while an ONNX +// embedder is configured — that produces silent dimension/space +// mismatches with `embedAsync()`'s ONNX vectors. + +import assert from 'node:assert/strict'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..'); +const ENTRY = resolve(REPO_ROOT, 'npm/packages/ruvector/dist/core/intelligence-engine.js'); + +const mod = await import(ENTRY); +const IntelligenceEngine = mod.IntelligenceEngine || mod.default; +if (!IntelligenceEngine) { + console.error('✗ IntelligenceEngine export not found at', ENTRY); + process.exit(1); +} + +// ── #315 — import() must repopulate enough state for recall() to return hits. +{ + const e1 = new IntelligenceEngine({ embeddingDim: 64, useOnnx: false }); + await e1.remember('the cat sat on the mat', 'general'); + await e1.remember('quick brown fox jumps over lazy dog', 'general'); + const exported = e1.export(); + + const e2 = new IntelligenceEngine({ embeddingDim: 64, useOnnx: false }); + e2.import(exported); + const results = await e2.recall('cat mat', 5); + + assert.ok( + Array.isArray(results) && results.length > 0, + `#315 regression: recall() after import() returned ${JSON.stringify(results)} — ` + + 'import() must repopulate the vector index (or recall() must fall back to brute force).', + ); + console.log(`✓ #315 — import()→recall() returned ${results.length} hit(s)`); +} + +// ── #316 — sync embed() must refuse when an ONNX embedder is configured. +{ + const e = new IntelligenceEngine({ embeddingDim: 384, useOnnx: false }); + + // Force the "ONNX is configured" state without actually loading ONNX. + e.onnxEmbedder = { + init: async () => {}, + embed: async () => new Array(384).fill(0.1), + }; + e.onnxReady = true; + + let threw = false; + try { + e.embed('hello'); + } catch (err) { + threw = true; + assert.match( + String(err.message), + /embedAsync|ONNX/i, + `#316 regression: embed() threw, but the message doesn't direct the caller to embedAsync(): ${err.message}`, + ); + } + assert.ok(threw, '#316 regression: sync embed() did NOT throw while an ONNX embedder is configured.'); + console.log('✓ #316 — sync embed() throws when an ONNX embedder is configured'); + + // And embedAsync() must STILL work (graceful fallback to attention/hash). + const vec = await e.embedAsync('hello'); + assert.ok(Array.isArray(vec) && vec.length === 384, '#316 follow-up: embedAsync() should still return a vector'); + console.log(`✓ #316 — embedAsync() still returns a ${vec.length}-dim vector when ONNX embed succeeds`); +} + +console.log('\n✓ IntelligenceEngine invariants OK'); diff --git a/scripts/ci/check-npm-package-integrity.mjs b/scripts/ci/check-npm-package-integrity.mjs new file mode 100644 index 000000000..d3d0205a4 --- /dev/null +++ b/scripts/ci/check-npm-package-integrity.mjs @@ -0,0 +1,192 @@ +#!/usr/bin/env node +// Regression guard for npm publish hygiene. +// +// For each package listed below, this script: +// 1. runs `npm pack` to produce the exact tarball that would be published +// 2. extracts it to a temp dir +// 3. asserts every path in `requiredFiles` is present in the tarball +// 4. runs `node --check` on every JS/MJS/CJS entry point reachable via +// package.json `main` / `module` / `bin` / `exports` +// 5. attempts to actually load the main entry (require for CJS, import for +// ESM) and FAILS on packaging errors (SyntaxError, ERR_REQUIRE_ESM, +// ERR_PACKAGE_PATH_NOT_EXPORTED, MODULE_NOT_FOUND for a bundled file). +// Runtime errors that are NOT packaging problems (e.g. a native .node +// addon missing on this host, "fetch is not defined" in a browser path) +// are tolerated. +// +// This exists because issues #354, #372, #376, #415, #417 all shipped to npm +// as "the published artifact is structurally broken" — every one of them would +// have been caught by steps 3–5 above. Add new packages / required files here +// whenever a publish-hygiene bug is fixed so it can never regress. + +import { execFileSync } from 'node:child_process'; +import { mkdtempSync, readFileSync, existsSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join, resolve, dirname } from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..'); + +/** + * @typedef {Object} PkgSpec + * @property {string} dir path to the package, relative to repo root + * @property {string[]} requiredFiles paths (relative to package root) that MUST + * be present in the published tarball + * @property {boolean} [loadMain] if false, skip the load() step (e.g. the + * package's only entry is a native binary) + */ + +/** @type {PkgSpec[]} */ +const PACKAGES = [ + { + dir: 'npm/packages/ruvector', + requiredFiles: [ + 'dist/index.js', + 'bin/cli.js', + 'bin/mcp-server.js', + // ONNX runtime assets — regression guard for #354 / #323 + 'dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.wasm', + 'dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm.js', + 'dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.js', + 'dist/core/onnx/pkg/loader.js', + ], + }, + { + dir: 'npm/packages/rvf-wasm', + // After #415: ESM-only package; the .mjs must be present and must NOT + // transitively re-import a CJS-classified .js that uses import.meta. + requiredFiles: ['pkg/rvf_wasm.mjs', 'pkg/rvf_wasm_bg.wasm'], + }, + // NOTE: @ruvector/pi-brain's #372 regression is guarded in the + // `mcp-server-loads` workflow job (it must be loaded via `await import`, not + // `require`) rather than here — pi-brain ships TS-only and only materialises + // `dist/` via `tsc`, so packing it requires a build step that doesn't belong + // in this lightweight script. +]; + +let failures = 0; +const fail = (pkg, msg) => { + failures++; + console.error(` ✗ [${pkg}] ${msg}`); +}; +const ok = (pkg, msg) => console.log(` ✓ [${pkg}] ${msg}`); + +// Errors that mean "this package is structurally broken for consumers". +const PACKAGING_ERROR_CODES = new Set([ + 'ERR_REQUIRE_ESM', + 'ERR_PACKAGE_PATH_NOT_EXPORTED', + 'ERR_UNSUPPORTED_DIR_IMPORT', + 'ERR_MODULE_NOT_FOUND', +]); +function isPackagingError(err) { + if (!err) return false; + if (err instanceof SyntaxError) return true; + if (err.code && PACKAGING_ERROR_CODES.has(err.code)) return true; + // A bundled file that should have been in `files` is missing. + if (err.code === 'MODULE_NOT_FOUND' && !/node_modules/.test(String(err.message))) return true; + return false; +} + +function packAndExtract(pkgAbsDir, scratch) { + // `npm pack --json` prints [{ filename, files: [{path}], ... }] + const out = execFileSync('npm', ['pack', '--json', '--pack-destination', scratch], { + cwd: pkgAbsDir, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'inherit'], + }); + const meta = JSON.parse(out)[0]; + const tarball = join(scratch, meta.filename); + const extractDir = join(scratch, 'extracted'); + execFileSync('mkdir', ['-p', extractDir]); + execFileSync('tar', ['-xzf', tarball, '-C', extractDir, '--strip-components=1']); + return { extractDir, fileList: meta.files.map((f) => f.path.replace(/^\.\//, '')) }; +} + +function entryPoints(pkgJson) { + const eps = new Set(); + const add = (v) => { + if (typeof v === 'string') eps.add(v); + else if (v && typeof v === 'object') for (const sub of Object.values(v)) add(sub); + }; + add(pkgJson.main); + add(pkgJson.module); + add(pkgJson.bin); + add(pkgJson.exports); + // types / .d.ts files are not executable + return [...eps].filter((p) => /\.(c|m)?js$/.test(p)); +} + +async function tryLoadMain(extractDir, pkgJson) { + const isEsm = pkgJson.type === 'module'; + const mainRel = (pkgJson.exports && pkgJson.exports['.'] && + (pkgJson.exports['.'].require || pkgJson.exports['.'].import || pkgJson.exports['.'].default)) || + pkgJson.main || pkgJson.module; + if (!mainRel) return; // nothing declared + const mainAbs = join(extractDir, mainRel); + if (!existsSync(mainAbs)) { + throw Object.assign(new Error(`declared entry "${mainRel}" missing from tarball`), { code: 'MODULE_NOT_FOUND' }); + } + if (isEsm || /\.mjs$/.test(mainRel)) { + await import(pathToFileURL(mainAbs).href); + } else { + const require = (await import('node:module')).createRequire(import.meta.url); + require(mainAbs); + } +} + +for (const spec of PACKAGES) { + const pkgAbsDir = join(REPO_ROOT, spec.dir); + if (!existsSync(join(pkgAbsDir, 'package.json'))) { + fail(spec.dir, 'package.json not found — did the path change?'); + continue; + } + const pkgJson = JSON.parse(readFileSync(join(pkgAbsDir, 'package.json'), 'utf8')); + console.log(`\n── ${pkgJson.name}@${pkgJson.version} (${spec.dir})`); + + const scratch = mkdtempSync(join(tmpdir(), 'pkgcheck-')); + try { + let extractDir, fileList; + try { + ({ extractDir, fileList } = packAndExtract(pkgAbsDir, scratch)); + } catch (e) { + fail(pkgJson.name, `npm pack failed: ${e.message}`); + continue; + } + const fileSet = new Set(fileList); + + for (const req of spec.requiredFiles) { + if (fileSet.has(req) || existsSync(join(extractDir, req))) ok(pkgJson.name, `tarball contains ${req}`); + else fail(pkgJson.name, `tarball is MISSING required file: ${req}`); + } + + for (const ep of entryPoints(pkgJson)) { + const epAbs = join(extractDir, ep); + if (!existsSync(epAbs)) { fail(pkgJson.name, `entry point declared in package.json but not in tarball: ${ep}`); continue; } + try { + execFileSync(process.execPath, ['--check', epAbs], { stdio: 'pipe' }); + ok(pkgJson.name, `node --check ${ep}`); + } catch (e) { + fail(pkgJson.name, `node --check FAILED for ${ep}: ${String(e.stderr || e.message).trim().split('\n').slice(-3).join(' ')}`); + } + } + + if (spec.loadMain !== false) { + try { + await tryLoadMain(extractDir, pkgJson); + ok(pkgJson.name, 'main entry loads without packaging errors'); + } catch (e) { + if (isPackagingError(e)) fail(pkgJson.name, `loading main entry threw a PACKAGING error: ${e.code || e.name}: ${e.message}`); + else ok(pkgJson.name, `main entry load threw a non-packaging runtime error (tolerated): ${e.code || e.name}`); + } + } + } finally { + rmSync(scratch, { recursive: true, force: true }); + } +} + +console.log(''); +if (failures > 0) { + console.error(`✗ npm package integrity check FAILED with ${failures} problem(s).`); + process.exit(1); +} +console.log('✓ npm package integrity check passed.');