Skip to content

fix(ruvector-cli): real demo + binding-drift fixes for v0.2.25 (#400, #402)#404

Merged
ruvnet merged 1 commit intomainfrom
fix/ruvector-cli-binding-drift
Apr 27, 2026
Merged

fix(ruvector-cli): real demo + binding-drift fixes for v0.2.25 (#400, #402)#404
ruvnet merged 1 commit intomainfrom
fix/ruvector-cli-binding-drift

Conversation

@ruvnet
Copy link
Copy Markdown
Owner

@ruvnet ruvnet commented Apr 27, 2026

Follow-up to #403. Fixes the runtime-side issues from #400 (ruvector demo modes) and #402 §A/§B (VectorDB CRUD + GNN/attention typed-array errors) — the bits that needed binding-surface investigation.

Changes

VectorDBWrapper: normalize distance metric (§A root cause)

Native JsDistanceMetric enum is PascalCase (Cosine | Euclidean | DotProduct | Manhattan); CLI passes lowercase shorthand. Added normalizeMetric() in src/index.ts that maps casing + common aliases (l2, dot, dotproduct, innerproduct, l1) and accepts metric as a constructor alias for distanceMetric.

demo --basic: realign with current API (§A surface)

Old: db.insert('vec1', [1.0, 0.0, 0.0, 0.0], { label: ... }) (positional).
Now: await db.insert({ id, vector: new Float32Array(...), metadata: {...} }) — matching the wrapper's actual signature.

demo --gnn: typed-array fix + binding-bug surface

demo --graph: real round-trip via GraphDatabase

Was "coming soon" stub. Implemented Alice -[:KNOWS]-> Bob using createNode({id, embedding, properties}) / createEdge({from, to, description, embedding, confidence}).

demo --benchmark: real inline benchmark

Was a redirect to ruvector benchmark. Implemented 1000-insert / 100-query mini-bench. Pinned to dim=4 because ruvector-core@0.1.29 has a regression that ignores the dimensions constructor arg — once the binding is rebuilt, dim can scale up.

attention compute: align with current API (§B)

CLI invoked attn.forward([query], keys, values). Current @ruvector/attention classes expose compute(query, keys, values)forward doesn't exist. Replaced all five branches; query must be flat Float32Array, not [query]. Reproduces and fixes user's Undefined into rust type u32 / Get TypedArray info failed errors.

gnn layer --test / compress / search: typed-array conversion

All three previously passed number[] where the binding needs Float32Array. Converted at call sites + wired reportGnnBindingError so users see the upstream pointer when they hit the binding-side regression.

Verification

$ node bin/cli.js demo --basic
  Searching for nearest to [0.8, 0.6, 0, 0]:
    1. vec4 (score: 0.0101)  ✓ correct nearest
    2. vec1 (score: 0.2000)
    3. vec2 (score: 0.4000)
  Demo complete!

$ node bin/cli.js demo --graph
  ✓ Created nodes: Alice, Bob
  ✓ Created edge Alice -[:KNOWS]-> Bob
  Graph demo complete!

$ node bin/cli.js demo --benchmark
  ✓ Inserted 1000 vectors in 126ms (0.13ms/vec)
  ✓ 100× top-10 search in 51ms (0.51ms/query)

$ node bin/cli.js attention compute -q \"[1,0,0,0]\" -k keys.json -t dot
  ✔ Attention computed (dot)

Version bumped 0.2.24 → 0.2.25.

Out of scope (binding-side rebuilds tracked separately)

Two published native bindings have regressions that the CLI can detect but not fix:

Binding Symptom Action
@ruvector/gnn@0.1.25 (and platform pkgs) Every method throws Given napi value is not an array Needs rebuild from current source
ruvector-core@0.1.29 (and platform pkgs) Constructor dimensions arg ignored, all inserts pinned to dim=4 Needs rebuild from current source

The Rust source in this repo shows correct behavior — the published .node artifacts are stale. Rebuild + republish is tracked at #402.

Closes #400 (demo modes runtime). Partially addresses #402 (§A/§B CLI-side fixed; binding-side rebuilds tracked).

…402)

Follow-up to #403. Addresses the runtime-side issues from #400 (`ruvector
demo` modes) and #402 §A/§B (VectorDB CRUD + GNN/attention typed-array
errors) that needed binding-surface investigation.

## Changes

### `VectorDBWrapper`: normalize distance metric (§A root cause)

`@ruvector/core`'s `JsDistanceMetric` enum is PascalCase
(`Euclidean | Cosine | DotProduct | Manhattan`), but every CLI call site
passes lowercase shorthand (`'cosine'`, `'euclidean'`, `'dot'`). The
native binding rejects lowercase with:

  value `"cosine"` does not match any variant of enum `JsDistanceMetric`
  on JsDbOptions.distanceMetric

Add a `normalizeMetric()` helper in `src/index.ts` that maps both casing
*and* common aliases (`l2`, `dot`, `dotproduct`, `innerproduct`, `l1`)
to the enum variant. Also accept `metric` as a constructor alias for
`distanceMetric` so the CLI's existing `{metric: ...}` shape works
without changing every call site.

### `demo --basic`: realign with current `VectorDb` API (§A surface)

Old code:

  db.insert('vec1', [1.0, 0.0, 0.0, 0.0], { label: 'x-axis' });
  const r = db.search([0.8, 0.6, 0, 0], 3);

Current `VectorDBWrapper` (and the underlying `@ruvector/core` binding)
takes a single object:

  await db.insert({ id: 'vec1', vector: new Float32Array([...]),
                    metadata: { label: 'x-axis' } });
  const r = await db.search({ vector: new Float32Array([...]), k: 3 });

Updated all four insert calls + the search call accordingly. Verified
locally — vec4 (closest to [0.8, 0.6]) is correctly returned first.

### `demo --gnn`: Float32Array + binding-bug surfaceability

Two issues:

1. CLI passed plain `number[]`; binding requires `Float32Array`. Fixed.

2. `@ruvector/gnn-linux-x64-gnu@0.1.25` has a published-binding
   regression where every method (`differentiableSearch`,
   `RuvectorLayer.forward`, `TensorCompress.compress`) throws
   `Given napi value is not an array` regardless of input shape —
   verified with both `Array<Float32Array>` and `number[][]`. This is
   a binary-side bug, not fixable from the CLI.

Added `reportGnnBindingError()` helper that detects the error pattern
and surfaces a pointer at #402 so users don't waste time debugging
their own install. Wired it into all three GNN command error handlers
(`gnn layer --test`, `gnn compress`, `gnn search`) and the demo.

Also fixed `result.attention_weights` → `result.weights` (the wrapper
shape; `attention_weights` was the older binding shape) with a fallback
that handles both.

### `demo --graph`: real round-trip via `GraphDatabase`

Was a stub printing "Full graph demo coming soon". `@ruvector/graph-node`
exposes a `GraphDatabase` class with `createNode({ id, embedding,
properties })`, `createEdge({ from, to, description, embedding,
confidence })`, and `stats()` — all async. Implemented a tiny
Alice -[:KNOWS]-> Bob round-trip using the actual API surface.

### `demo --benchmark`: real inline benchmark (with workaround)

Was redirecting to `npx ruvector benchmark`. Implemented an inline
1000-vector / 100-query mini-benchmark. Pinned to `dim=4` because
`ruvector-core-linux-x64-gnu@0.1.29` has a regression where the
`dimensions` constructor arg is ignored — every `VectorDb` instance
reports `expected 4` regardless of what's passed (verified by
constructing fresh instances with various dims). Tracked at #402.
Once that binding is rebuilt, `dim` can scale up.

### `attention compute`: align with current `compute()` surface (§B)

The CLI's old switch invoked `attn.forward([query], keys, values)`,
but every current `@ruvector/attention` class exposes `compute(query,
keys, values)` instead — `forward` doesn't exist. Also the query
must be a flat `Float32Array`, not `[query]` matrix.

Reproduces the user's `Failed to convert napi value Undefined into
rust type u32` and `Get TypedArray info failed` errors directly.

Replaced all five branches (`dot | multi-head | flash | hyperbolic |
linear`) with the correct `compute()` invocation + Float32Array
conversion. Verified locally:

  $ node bin/cli.js attention compute -q "[1,0,0,0]" -k keys.json -t dot
  ✔ Attention computed (dot)
  Output: [0.6225, 0.3775, 0, 0...]

### `gnn layer --test` / `gnn compress` / `gnn search`: typed-array conversion

All three commands previously passed plain `number[]` where the binding
needs `Float32Array`. Converted at the call sites + added the
`reportGnnBindingError` hook so users see the upstream pointer when
they hit the binding-side regression.

## Verification

```
$ node bin/cli.js demo --basic
  Searching for nearest to [0.8, 0.6, 0, 0]:
    1. vec4 (score: 0.0101)  ✓ correct nearest
    2. vec1 (score: 0.2000)
    3. vec2 (score: 0.4000)
  Demo complete!

$ node bin/cli.js demo --gnn
  GNN demo failed: Given napi value is not an array
  Note: this is a known regression in the @ruvector/gnn native binding…
    #402

$ node bin/cli.js demo --graph
  ✓ GraphDatabase instance created
  ✓ Created nodes: Alice (alice), Bob (bob)
  ✓ Created edge Alice -[:KNOWS]-> Bob (uuid)
  Graph demo complete!

$ node bin/cli.js demo --benchmark
  ✓ Inserted 1000 vectors in 126ms (0.13ms/vec)
  ✓ 100× top-10 search in 51ms (0.51ms/query)

$ node bin/cli.js attention compute -q "[1,0,0,0]" -k keys.json -t dot
  ✔ Attention computed (dot)

$ npm run verify-dist
  verify-dist: 13 dist path(s) present.
```

Version bumped 0.2.24 → 0.2.25.

## Out of scope (binding-side rebuilds needed)

- `@ruvector/gnn` published bindings throw on every call (binding bug).
- `@ruvector/core` published bindings ignore `dimensions` constructor
  arg (binding bug).

Both need a rebuild from current source — the Rust source in this repo
shows correct independent state, but the published `.node` files have
the regression. Rebuild and republish are tracked separately.
@ruvnet ruvnet merged commit 8f3f0b3 into main Apr 27, 2026
19 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ruvector@0.2.23: ruvector demo is broken across all 4 modes (2 crash, 2 are stubs)

1 participant