@ruvector/rvf-node 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +252 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ruvector/rvf-node
|
|
2
2
|
|
|
3
|
-
Node.js
|
|
3
|
+
Native Node.js bindings for the [RuVector Format](https://github.com/ruvnet/ruvector/tree/main/crates/rvf) (RVF) vector database. Built with Rust via N-API for native speed with zero serialization overhead.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,52 +8,271 @@ Node.js native bindings for the RuVector Format (RVF) cognitive container.
|
|
|
8
8
|
npm install @ruvector/rvf-node
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Native Rust performance** via N-API (napi-rs), no FFI marshaling
|
|
14
|
+
- **Single-file vector database** — crash-safe, no WAL, append-only
|
|
15
|
+
- **k-NN search** with HNSW progressive indexing (recall 0.70 → 0.95)
|
|
16
|
+
- **Metadata filtering** — Eq, Ne, Lt, Gt, Range, In, And, Or, Not
|
|
17
|
+
- **Lineage tracking** — DNA-style parent/child derivation chains
|
|
18
|
+
- **Kernel & eBPF embedding** — embed compute alongside vector data
|
|
19
|
+
- **Segment inspection** — enumerate all segments in the file
|
|
20
|
+
- **Cross-platform** — Linux (x86_64, aarch64), macOS (x86_64, Apple Silicon), Windows (x86_64)
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
12
23
|
|
|
13
24
|
```javascript
|
|
14
25
|
const { RvfDatabase } = require('@ruvector/rvf-node');
|
|
15
26
|
|
|
16
|
-
// Create a
|
|
17
|
-
const db = RvfDatabase.create('vectors.rvf', {
|
|
27
|
+
// Create a store
|
|
28
|
+
const db = RvfDatabase.create('vectors.rvf', {
|
|
29
|
+
dimension: 384,
|
|
30
|
+
metric: 'cosine',
|
|
31
|
+
});
|
|
18
32
|
|
|
19
33
|
// Insert vectors
|
|
20
|
-
|
|
34
|
+
const vectors = new Float32Array(384 * 2); // 2 vectors, 384 dims each
|
|
35
|
+
vectors.fill(0.1);
|
|
36
|
+
db.ingestBatch(vectors, [1, 2]);
|
|
21
37
|
|
|
22
38
|
// Query nearest neighbors
|
|
23
|
-
const
|
|
24
|
-
|
|
39
|
+
const query = new Float32Array(384);
|
|
40
|
+
query.fill(0.15);
|
|
41
|
+
const results = db.query(query, 5);
|
|
42
|
+
// [{ id: 1, distance: 0.002 }, { id: 2, distance: 0.002 }]
|
|
43
|
+
|
|
44
|
+
db.close();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API Reference
|
|
48
|
+
|
|
49
|
+
### Store Lifecycle
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Create a new store
|
|
53
|
+
const db = RvfDatabase.create(path: string, options: RvfOptions);
|
|
25
54
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
console.log(db.dimension()); // 384
|
|
29
|
-
console.log(db.segments()); // [{ type, id, size }]
|
|
55
|
+
// Open existing store (read-write, acquires writer lock)
|
|
56
|
+
const db = RvfDatabase.open(path: string);
|
|
30
57
|
|
|
58
|
+
// Open read-only (no lock, concurrent readers allowed)
|
|
59
|
+
const db = RvfDatabase.openReadonly(path: string);
|
|
60
|
+
|
|
61
|
+
// Close and flush
|
|
31
62
|
db.close();
|
|
32
63
|
```
|
|
33
64
|
|
|
34
|
-
|
|
65
|
+
**RvfOptions:**
|
|
66
|
+
|
|
67
|
+
| Field | Type | Default | Description |
|
|
68
|
+
|-------|------|---------|-------------|
|
|
69
|
+
| `dimension` | `number` | required | Vector dimensionality |
|
|
70
|
+
| `metric` | `string` | `"l2"` | `"l2"`, `"cosine"`, or `"inner_product"` |
|
|
71
|
+
| `profile` | `number` | `0` | Hardware profile: 0=Generic, 1=Core, 2=Hot, 3=Full |
|
|
72
|
+
| `signing` | `boolean` | `false` | Enable segment signing |
|
|
73
|
+
| `m` | `number` | `16` | HNSW M parameter (neighbor count) |
|
|
74
|
+
| `efConstruction` | `number` | `200` | HNSW index build quality |
|
|
75
|
+
|
|
76
|
+
### Ingest Vectors
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const result = db.ingestBatch(
|
|
80
|
+
vectors: Float32Array, // flat array of n * dimension floats
|
|
81
|
+
ids: number[], // vector IDs
|
|
82
|
+
metadata?: RvfMetadataEntry[] // optional metadata per vector
|
|
83
|
+
);
|
|
84
|
+
// Returns: { accepted: number, rejected: number, epoch: number }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Metadata entry format:**
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
{ fieldId: 0, valueType: 'string', value: 'category_a' }
|
|
91
|
+
{ fieldId: 1, valueType: 'f64', value: '0.95' }
|
|
92
|
+
{ fieldId: 2, valueType: 'u64', value: '42' }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Query
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const results = db.query(
|
|
99
|
+
vector: Float32Array, // query vector
|
|
100
|
+
k: number, // number of neighbors
|
|
101
|
+
options?: RvfQueryOptions // optional search parameters
|
|
102
|
+
);
|
|
103
|
+
// Returns: [{ id: number, distance: number }, ...]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**RvfQueryOptions:**
|
|
107
|
+
|
|
108
|
+
| Field | Type | Default | Description |
|
|
109
|
+
|-------|------|---------|-------------|
|
|
110
|
+
| `efSearch` | `number` | `100` | HNSW search quality (higher = better recall, slower) |
|
|
111
|
+
| `filter` | `string` | — | Filter expression as JSON string |
|
|
112
|
+
| `timeoutMs` | `number` | `0` | Query timeout in ms (0 = no timeout) |
|
|
113
|
+
|
|
114
|
+
### Filter Expressions
|
|
115
|
+
|
|
116
|
+
Filters are passed as JSON strings. All leaf filters require `fieldId`, `valueType`, and `value`:
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
// Equality
|
|
120
|
+
db.query(vec, 10, {
|
|
121
|
+
filter: '{"op":"eq","fieldId":0,"valueType":"string","value":"science"}'
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Range
|
|
125
|
+
db.query(vec, 10, {
|
|
126
|
+
filter: '{"op":"range","fieldId":1,"valueType":"f64","low":"0.5","high":"1.0"}'
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// In-set
|
|
130
|
+
db.query(vec, 10, {
|
|
131
|
+
filter: '{"op":"in","fieldId":0,"valueType":"u64","values":["1","2","5"]}'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Boolean combinations
|
|
135
|
+
db.query(vec, 10, {
|
|
136
|
+
filter: JSON.stringify({
|
|
137
|
+
op: 'and',
|
|
138
|
+
children: [
|
|
139
|
+
{ op: 'eq', fieldId: 0, valueType: 'string', value: 'science' },
|
|
140
|
+
{ op: 'gt', fieldId: 1, valueType: 'f64', value: '0.8' }
|
|
141
|
+
]
|
|
142
|
+
})
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Negation
|
|
146
|
+
db.query(vec, 10, {
|
|
147
|
+
filter: '{"op":"not","child":{"op":"eq","fieldId":0,"valueType":"string","value":"spam"}}'
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Supported operators:** `eq`, `ne`, `lt`, `le`, `gt`, `ge`, `in`, `range`, `and`, `or`, `not`
|
|
152
|
+
|
|
153
|
+
**Supported value types:** `u64`, `i64`, `f64`, `string`, `bool`
|
|
154
|
+
|
|
155
|
+
### Delete
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// Delete by ID
|
|
159
|
+
const result = db.delete([1, 2, 3]);
|
|
160
|
+
// Returns: { deleted: number, epoch: number }
|
|
161
|
+
|
|
162
|
+
// Delete by filter
|
|
163
|
+
const result = db.deleteByFilter(
|
|
164
|
+
'{"op":"gt","fieldId":1,"valueType":"f64","value":"0.9"}'
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Compact
|
|
169
|
+
|
|
170
|
+
Reclaims space from deleted vectors:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const result = db.compact();
|
|
174
|
+
// Returns: { segmentsCompacted: number, bytesReclaimed: number, epoch: number }
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Status
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const status = db.status();
|
|
181
|
+
// {
|
|
182
|
+
// totalVectors: number,
|
|
183
|
+
// totalSegments: number,
|
|
184
|
+
// fileSize: number,
|
|
185
|
+
// currentEpoch: number,
|
|
186
|
+
// profileId: number,
|
|
187
|
+
// compactionState: 'idle' | 'running' | 'emergency',
|
|
188
|
+
// deadSpaceRatio: number,
|
|
189
|
+
// readOnly: boolean
|
|
190
|
+
// }
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Lineage & Derivation
|
|
194
|
+
|
|
195
|
+
RVF tracks parent/child relationships with cryptographic hashes:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
db.fileId(); // hex string — unique file identifier
|
|
199
|
+
db.parentId(); // hex string — parent's ID (zeros if root)
|
|
200
|
+
db.lineageDepth(); // 0 for root files
|
|
201
|
+
|
|
202
|
+
// Derive a child store (inherits dimensions and options)
|
|
203
|
+
const child = db.derive('/tmp/child.rvf');
|
|
204
|
+
child.lineageDepth(); // 1
|
|
205
|
+
child.parentId(); // matches parent's fileId()
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Kernel & eBPF Embedding
|
|
209
|
+
|
|
210
|
+
Embed compute segments alongside vector data:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Embed a Linux microkernel
|
|
214
|
+
db.embedKernel(
|
|
215
|
+
1, // arch: 0=x86_64, 1=aarch64
|
|
216
|
+
0, // kernel type
|
|
217
|
+
0, // flags
|
|
218
|
+
Buffer.from(kernelImage), // kernel binary
|
|
219
|
+
8080, // API port
|
|
220
|
+
'console=ttyS0 quiet' // kernel cmdline (optional)
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// Extract kernel
|
|
224
|
+
const kernel = db.extractKernel();
|
|
225
|
+
if (kernel) {
|
|
226
|
+
console.log(kernel.header); // Buffer: 128-byte KernelHeader
|
|
227
|
+
console.log(kernel.image); // Buffer: kernel image bytes
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Embed an eBPF XDP program
|
|
231
|
+
db.embedEbpf(
|
|
232
|
+
1, // program type (XDP distance)
|
|
233
|
+
2, // attach type (XDP ingress)
|
|
234
|
+
384, // max vector dimension
|
|
235
|
+
Buffer.from(bytecode), // BPF ELF object
|
|
236
|
+
Buffer.from(btf) // optional BTF section
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
// Extract eBPF
|
|
240
|
+
const ebpf = db.extractEbpf();
|
|
241
|
+
if (ebpf) {
|
|
242
|
+
console.log(ebpf.header); // Buffer: 64-byte EbpfHeader
|
|
243
|
+
console.log(ebpf.payload); // Buffer: bytecode + BTF
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Segment Inspection
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const segments = db.segments();
|
|
251
|
+
// [{ id: 1, offset: 0, payloadLength: 4096, segType: 'manifest' },
|
|
252
|
+
// { id: 2, offset: 4160, payloadLength: 51200, segType: 'vec' },
|
|
253
|
+
// { id: 3, offset: 55424, payloadLength: 12288, segType: 'index' }]
|
|
254
|
+
|
|
255
|
+
db.dimension(); // 384
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Build from Source
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# Prerequisites: Rust 1.87+, Node.js 18+
|
|
262
|
+
cd crates/rvf/rvf-node
|
|
263
|
+
npm install
|
|
264
|
+
npm run build
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Related Packages
|
|
35
268
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
## API
|
|
43
|
-
|
|
44
|
-
| Method | Description |
|
|
45
|
-
|--------|-------------|
|
|
46
|
-
| `RvfDatabase.create(path, options)` | Create a new RVF store |
|
|
47
|
-
| `RvfDatabase.open(path)` | Open an existing store |
|
|
48
|
-
| `db.ingestBatch(vectors, ids)` | Insert vectors |
|
|
49
|
-
| `db.query(vector, k)` | k-NN similarity search |
|
|
50
|
-
| `db.delete(ids)` | Delete vectors by ID |
|
|
51
|
-
| `db.compact()` | Reclaim deleted space |
|
|
52
|
-
| `db.status()` | Get store stats |
|
|
53
|
-
| `db.segments()` | List all segments |
|
|
54
|
-
| `db.fileId()` | Get unique file UUID |
|
|
55
|
-
| `db.close()` | Close and release lock |
|
|
269
|
+
| Package | Description |
|
|
270
|
+
|---------|-------------|
|
|
271
|
+
| [`@ruvector/rvf`](https://www.npmjs.com/package/@ruvector/rvf) | Unified TypeScript SDK |
|
|
272
|
+
| [`@ruvector/rvf-wasm`](https://www.npmjs.com/package/@ruvector/rvf-wasm) | Browser WASM package |
|
|
273
|
+
| [`@ruvector/rvf-mcp-server`](https://www.npmjs.com/package/@ruvector/rvf-mcp-server) | MCP server for AI agents |
|
|
274
|
+
| [`rvf-runtime`](https://crates.io/crates/rvf-runtime) | Rust runtime (powers this package) |
|
|
56
275
|
|
|
57
276
|
## License
|
|
58
277
|
|
|
59
|
-
MIT
|
|
278
|
+
MIT OR Apache-2.0
|