Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions crates/sona/src/napi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ impl SonaEngine {
#[napi]
pub fn apply_micro_lora(&self, input: Vec<f64>) -> Vec<f64> {
let input_f32: Vec<f32> = input.iter().map(|&x| x as f32).collect();
let mut output = vec![0.0f32; input_f32.len()];
// The Rust LoRA forward pass has residual semantics (it adds the
// learned delta into `output`), so `output` must start as a clone
// of `input` — not zeros — or callers only ever see the delta.
let mut output = input_f32.clone();
self.inner.apply_micro_lora(&input_f32, &mut output);
output.iter().map(|&x| x as f64).collect()
}
Expand All @@ -90,7 +93,8 @@ impl SonaEngine {
#[napi]
pub fn apply_base_lora(&self, layer_idx: u32, input: Vec<f64>) -> Vec<f64> {
let input_f32: Vec<f32> = input.iter().map(|&x| x as f32).collect();
let mut output = vec![0.0f32; input_f32.len()];
// Same residual semantics as apply_micro_lora — seed with input.
let mut output = input_f32.clone();
self.inner.apply_base_lora(layer_idx as usize, &input_f32, &mut output);
output.iter().map(|&x| x as f64).collect()
}
Expand Down
8 changes: 6 additions & 2 deletions crates/sona/src/napi_simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ impl SonaEngine {
#[napi]
pub fn apply_micro_lora(&self, input: Vec<f64>) -> Vec<f64> {
let input_f32: Vec<f32> = input.iter().map(|&x| x as f32).collect();
let mut output = vec![0.0f32; input_f32.len()];
// The Rust LoRA forward pass has residual semantics (it adds the
// learned delta into `output`), so `output` must start as a clone
// of `input` — not zeros — or callers only ever see the delta.
let mut output = input_f32.clone();
self.inner.apply_micro_lora(&input_f32, &mut output);
output.iter().map(|&x| x as f64).collect()
}
Expand All @@ -154,7 +157,8 @@ impl SonaEngine {
#[napi]
pub fn apply_base_lora(&self, layer_idx: u32, input: Vec<f64>) -> Vec<f64> {
let input_f32: Vec<f32> = input.iter().map(|&x| x as f32).collect();
let mut output = vec![0.0f32; input_f32.len()];
// Same residual semantics as apply_micro_lora — seed with input.
let mut output = input_f32.clone();
self.inner
.apply_base_lora(layer_idx as usize, &input_f32, &mut output);
output.iter().map(|&x| x as f64).collect()
Expand Down
7 changes: 5 additions & 2 deletions crates/sona/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ impl WasmSonaEngine {
/// ```
#[wasm_bindgen(js_name = applyLora)]
pub fn apply_lora(&self, input: Vec<f32>) -> Vec<f32> {
let mut output = vec![0.0; input.len()];
// Residual semantics: engine.apply_micro_lora adds the learned delta
// into `output`, so it must start as the input, not zeros.
let mut output = input.clone();
let engine = self.inner.read();
engine.apply_micro_lora(&input, &mut output);
output
Expand All @@ -265,7 +267,8 @@ impl WasmSonaEngine {
/// Transformed vector as Float32Array
#[wasm_bindgen(js_name = applyLoraLayer)]
pub fn apply_lora_layer(&self, layer_idx: usize, input: Vec<f32>) -> Vec<f32> {
let mut output = vec![0.0; input.len()];
// Same residual semantics as apply_lora — seed with input.
let mut output = input.clone();
let engine = self.inner.read();
engine.apply_base_lora(layer_idx, &input, &mut output);
output
Expand Down
23 changes: 17 additions & 6 deletions docs/api/NODEJS_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -607,10 +607,21 @@ interface QuantizationConfig {

## Advanced Features

### HybridSearch
> **Not yet available in the `ruvector` Node package.** `HybridSearch`,
> `FilteredSearch`, and `MMRSearch` below exist in the Rust core
> (`ruvector-core::advanced_features`) but are **not bound or exported** by
> `crates/ruvector-node` / the published `ruvector` npm package — see
> [#707](https://github.com/ruvnet/RuVector/issues/707). Importing any of
> these names from `ruvector` in Node.js currently returns `undefined`.
> If you need hybrid/keyword search from JavaScript today, use AgentDB's
> `HybridSearch` / `KeywordIndex` (`ruvnet/agentdb`) instead. The snippets
> below describe the intended shape of a future Node binding, not a
> currently shipped API.

### HybridSearch (Rust-only; not exported to Node)

```javascript
const { HybridSearch } = require('ruvector');
const { HybridSearch } = require('ruvector'); // NOT YET SHIPPED — see #707

const hybrid = new HybridSearch(db, {
vectorWeight: 0.7,
Expand All @@ -626,10 +637,10 @@ const results = await hybrid.search(
);
```

### FilteredSearch
### FilteredSearch (Rust-only; not exported to Node)

```javascript
const { FilteredSearch } = require('ruvector');
const { FilteredSearch } = require('ruvector'); // NOT YET SHIPPED — see #707

const filtered = new FilteredSearch(db, 'preFilter');

Expand All @@ -641,10 +652,10 @@ const results = await filtered.search(queryVector, 10, {
});
```

### MMRSearch
### MMRSearch (Rust-only; not exported to Node)

```javascript
const { MMRSearch } = require('ruvector');
const { MMRSearch } = require('ruvector'); // NOT YET SHIPPED — see #707

const mmr = new MMRSearch(db, {
lambda: 0.5,
Expand Down
31 changes: 27 additions & 4 deletions docs/guides/ADVANCED_FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,14 @@ fn hybrid_search_example(db: &VectorDB) -> Result<(), Box<dyn std::error::Error>

### Node.js

> **Not yet available in the `ruvector` Node package** — `HybridSearch` is
> implemented in `ruvector-core` but is not bound/exported by
> `crates/ruvector-node` yet ([#707](https://github.com/ruvnet/RuVector/issues/707)).
> `require('ruvector').HybridSearch` is `undefined` today; use AgentDB's
> `HybridSearch`/`KeywordIndex` from JavaScript in the meantime.

```javascript
const { HybridSearch } = require('ruvector');
const { HybridSearch } = require('ruvector'); // NOT YET SHIPPED — see #707

const hybrid = new HybridSearch(db, {
vectorWeight: 0.7,
Expand Down Expand Up @@ -153,8 +159,14 @@ FilterExpression::Not(Box::new(expr))

### Node.js

> **Not yet available in the `ruvector` Node package** — `FilteredSearch` is
> implemented in `ruvector-core` but is not bound/exported by
> `crates/ruvector-node` yet ([#707](https://github.com/ruvnet/RuVector/issues/707)).
> `require('ruvector').FilteredSearch` is `undefined` today. `VectorDB.search()`'s
> built-in `filter` option covers basic pre-filtering in the meantime.

```javascript
const { FilteredSearch } = require('ruvector');
const { FilteredSearch } = require('ruvector'); // NOT YET SHIPPED — see #707

const filtered = new FilteredSearch(db, 'preFilter');

Expand Down Expand Up @@ -203,8 +215,13 @@ fn mmr_example(db: &VectorDB) -> Result<(), Box<dyn std::error::Error>> {

### Node.js

> **Not yet available in the `ruvector` Node package** — `MMRSearch` is
> implemented in `ruvector-core` but is not bound/exported by
> `crates/ruvector-node` yet ([#707](https://github.com/ruvnet/RuVector/issues/707)).
> `require('ruvector').MMRSearch` is `undefined` today.

```javascript
const { MMRSearch } = require('ruvector');
const { MMRSearch } = require('ruvector'); // NOT YET SHIPPED — see #707

const mmr = new MMRSearch(db, {
lambda: 0.5,
Expand Down Expand Up @@ -322,8 +339,14 @@ fn conformal_prediction_example() -> Result<(), Box<dyn std::error::Error>> {

### Node.js

> **Not yet available in the `ruvector` Node package** — `ConformalPredictor`
> is implemented in `ruvector-core` but is not bound/exported by
> `crates/ruvector-node` yet (same gap as [#707](https://github.com/ruvnet/RuVector/issues/707),
> which covers `HybridSearch`/`FilteredSearch`/`MMRSearch`).
> `require('ruvector').ConformalPredictor` is `undefined` today.

```javascript
const { ConformalPredictor } = require('ruvector');
const { ConformalPredictor } = require('ruvector'); // NOT YET SHIPPED

const predictor = new ConformalPredictor({
alpha: 0.1, // 90% confidence
Expand Down
11 changes: 4 additions & 7 deletions examples/rvf/scripts/rvf-browser.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,9 @@ <h1>RVF Browser Example</h1>
# or load from CDN (see script below)</pre>

<script type="module">
// Import the WASM module
// Option 1: Local build
// import init, { WasmRvfStore } from '../../../npm/packages/rvf-wasm/pkg/rvf_runtime.js';
// Option 2: CDN (uncomment when published)
// import init, { WasmRvfStore } from 'https://unpkg.com/@ruvector/rvf-wasm/pkg/rvf_runtime.js';
// Import the SDK (wraps @ruvector/rvf-wasm's low-level C-ABI module —
// there is no WasmRvfStore class; see issue #705).
// import { RvfDatabase } from '@ruvector/rvf';

const log = document.getElementById('log');
function print(msg) { log.textContent += '\n' + msg; }
Expand All @@ -43,8 +41,7 @@ <h1>RVF Browser Example</h1>
window.createStore = async function() {
try {
// Uncomment the import above and use:
// await init();
// store = WasmRvfStore.create(128);
// const db = await RvfDatabase.create('unused-in-wasm', { dimensions: 128 }, 'wasm');
// print('[1] Store created (128-dim, cosine)');

// Demo fallback (no WASM loaded):
Expand Down
16 changes: 16 additions & 0 deletions npm/packages/rvf/dist/backend.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export interface RvfBackend {
extractEbpf(): Promise<RvfEbpfData | null>;
segments(): Promise<RvfSegmentInfo[]>;
dimension(): Promise<number>;
/** Serialize the store to an in-memory `.rvf` byte buffer. */
exportBytes(): Promise<Uint8Array>;
/** Load a store from an in-memory `.rvf` byte buffer. */
openBytes(bytes: Uint8Array): Promise<void>;
}
/**
* Backend that delegates to the `@ruvector/rvf-node` native N-API addon.
Expand Down Expand Up @@ -72,6 +76,8 @@ export declare class NodeBackend implements RvfBackend {
extractEbpf(): Promise<RvfEbpfData | null>;
segments(): Promise<RvfSegmentInfo[]>;
dimension(): Promise<number>;
exportBytes(): Promise<Uint8Array>;
openBytes(_bytes: Uint8Array): Promise<void>;
/**
* Get or allocate a numeric label for a string ID.
* If the ID was already seen, returns the existing label.
Expand Down Expand Up @@ -121,6 +127,16 @@ export declare class WasmBackend implements RvfBackend {
extractEbpf(): Promise<RvfEbpfData | null>;
segments(): Promise<RvfSegmentInfo[]>;
dimension(): Promise<number>;
/**
* Serialize the in-memory store to `.rvf` bytes via the `rvf_store_export`
* C-ABI export. `rvf_store_export` follows a probe-then-write pattern:
* called with a too-small (or zero-length) buffer it returns the negated
* required size, so we probe first, allocate exactly that much, then
* write for real.
*/
exportBytes(): Promise<Uint8Array>;
/** Load a store from `.rvf` bytes via the `rvf_store_open` C-ABI import. */
openBytes(bytes: Uint8Array): Promise<void>;
}
/**
* Resolve a `BackendType` to a concrete `RvfBackend` instance.
Expand Down
Loading
Loading