agentic-qe 3.6.19 → 3.7.0
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/.claude/skills/skills-manifest.json +1 -1
- package/README.md +29 -1
- package/package.json +2 -1
- package/scripts/rvf-baseline-benchmark.ts +843 -0
- package/v3/CHANGELOG.md +30 -0
- package/v3/dist/audit/witness-chain.d.ts +148 -0
- package/v3/dist/audit/witness-chain.d.ts.map +1 -0
- package/v3/dist/audit/witness-chain.js +269 -0
- package/v3/dist/audit/witness-chain.js.map +1 -0
- package/v3/dist/cli/brain-commands.d.ts +39 -0
- package/v3/dist/cli/brain-commands.d.ts.map +1 -0
- package/v3/dist/cli/brain-commands.js +60 -0
- package/v3/dist/cli/brain-commands.js.map +1 -0
- package/v3/dist/cli/bundle.js +4509 -2452
- package/v3/dist/cli/command-registry.d.ts.map +1 -1
- package/v3/dist/cli/command-registry.js +3 -1
- package/v3/dist/cli/command-registry.js.map +1 -1
- package/v3/dist/cli/handlers/brain-handler.d.ts +22 -0
- package/v3/dist/cli/handlers/brain-handler.d.ts.map +1 -0
- package/v3/dist/cli/handlers/brain-handler.js +148 -0
- package/v3/dist/cli/handlers/brain-handler.js.map +1 -0
- package/v3/dist/cli/handlers/index.d.ts +1 -0
- package/v3/dist/cli/handlers/index.d.ts.map +1 -1
- package/v3/dist/cli/handlers/index.js +1 -0
- package/v3/dist/cli/handlers/index.js.map +1 -1
- package/v3/dist/domains/coverage-analysis/services/hnsw-index.d.ts +31 -44
- package/v3/dist/domains/coverage-analysis/services/hnsw-index.d.ts.map +1 -1
- package/v3/dist/domains/coverage-analysis/services/hnsw-index.js +73 -178
- package/v3/dist/domains/coverage-analysis/services/hnsw-index.js.map +1 -1
- package/v3/dist/domains/quality-assessment/coordinator.d.ts.map +1 -1
- package/v3/dist/domains/quality-assessment/coordinator.js +8 -0
- package/v3/dist/domains/quality-assessment/coordinator.js.map +1 -1
- package/v3/dist/domains/test-execution/coordinator.d.ts +14 -0
- package/v3/dist/domains/test-execution/coordinator.d.ts.map +1 -1
- package/v3/dist/domains/test-execution/coordinator.js +18 -0
- package/v3/dist/domains/test-execution/coordinator.js.map +1 -1
- package/v3/dist/domains/test-execution/services/index.d.ts +1 -0
- package/v3/dist/domains/test-execution/services/index.d.ts.map +1 -1
- package/v3/dist/domains/test-execution/services/index.js +1 -0
- package/v3/dist/domains/test-execution/services/index.js.map +1 -1
- package/v3/dist/domains/test-execution/services/mincut-test-optimizer.d.ts +118 -0
- package/v3/dist/domains/test-execution/services/mincut-test-optimizer.d.ts.map +1 -0
- package/v3/dist/domains/test-execution/services/mincut-test-optimizer.js +358 -0
- package/v3/dist/domains/test-execution/services/mincut-test-optimizer.js.map +1 -0
- package/v3/dist/integrations/ruvector/brain-exporter.d.ts +66 -0
- package/v3/dist/integrations/ruvector/brain-exporter.d.ts.map +1 -0
- package/v3/dist/integrations/ruvector/brain-exporter.js +537 -0
- package/v3/dist/integrations/ruvector/brain-exporter.js.map +1 -0
- package/v3/dist/integrations/ruvector/index.d.ts +4 -0
- package/v3/dist/integrations/ruvector/index.d.ts.map +1 -1
- package/v3/dist/integrations/ruvector/index.js +5 -0
- package/v3/dist/integrations/ruvector/index.js.map +1 -1
- package/v3/dist/integrations/ruvector/mincut-wrapper.d.ts +204 -0
- package/v3/dist/integrations/ruvector/mincut-wrapper.d.ts.map +1 -0
- package/v3/dist/integrations/ruvector/mincut-wrapper.js +310 -0
- package/v3/dist/integrations/ruvector/mincut-wrapper.js.map +1 -0
- package/v3/dist/integrations/ruvector/rvf-dual-writer.d.ts +141 -0
- package/v3/dist/integrations/ruvector/rvf-dual-writer.d.ts.map +1 -0
- package/v3/dist/integrations/ruvector/rvf-dual-writer.js +367 -0
- package/v3/dist/integrations/ruvector/rvf-dual-writer.js.map +1 -0
- package/v3/dist/integrations/ruvector/rvf-native-adapter.d.ts +60 -0
- package/v3/dist/integrations/ruvector/rvf-native-adapter.d.ts.map +1 -0
- package/v3/dist/integrations/ruvector/rvf-native-adapter.js +240 -0
- package/v3/dist/integrations/ruvector/rvf-native-adapter.js.map +1 -0
- package/v3/dist/kernel/hnsw-adapter.d.ts +99 -0
- package/v3/dist/kernel/hnsw-adapter.d.ts.map +1 -0
- package/v3/dist/kernel/hnsw-adapter.js +218 -0
- package/v3/dist/kernel/hnsw-adapter.js.map +1 -0
- package/v3/dist/kernel/hnsw-index-provider.d.ts +90 -0
- package/v3/dist/kernel/hnsw-index-provider.d.ts.map +1 -0
- package/v3/dist/kernel/hnsw-index-provider.js +23 -0
- package/v3/dist/kernel/hnsw-index-provider.js.map +1 -0
- package/v3/dist/kernel/progressive-hnsw-backend.d.ts +67 -0
- package/v3/dist/kernel/progressive-hnsw-backend.d.ts.map +1 -0
- package/v3/dist/kernel/progressive-hnsw-backend.js +266 -0
- package/v3/dist/kernel/progressive-hnsw-backend.js.map +1 -0
- package/v3/dist/kernel/unified-memory-hnsw.d.ts +71 -0
- package/v3/dist/kernel/unified-memory-hnsw.d.ts.map +1 -1
- package/v3/dist/kernel/unified-memory-hnsw.js +87 -0
- package/v3/dist/kernel/unified-memory-hnsw.js.map +1 -1
- package/v3/dist/kernel/unified-memory-schemas.d.ts +1 -0
- package/v3/dist/kernel/unified-memory-schemas.d.ts.map +1 -1
- package/v3/dist/kernel/unified-memory-schemas.js +16 -0
- package/v3/dist/kernel/unified-memory-schemas.js.map +1 -1
- package/v3/dist/learning/dream/dream-engine.d.ts +16 -0
- package/v3/dist/learning/dream/dream-engine.d.ts.map +1 -1
- package/v3/dist/learning/dream/dream-engine.js +70 -0
- package/v3/dist/learning/dream/dream-engine.js.map +1 -1
- package/v3/dist/learning/dream/index.d.ts +2 -0
- package/v3/dist/learning/dream/index.d.ts.map +1 -1
- package/v3/dist/learning/dream/index.js +8 -0
- package/v3/dist/learning/dream/index.js.map +1 -1
- package/v3/dist/learning/dream/rvcow-branch-manager.d.ts +170 -0
- package/v3/dist/learning/dream/rvcow-branch-manager.d.ts.map +1 -0
- package/v3/dist/learning/dream/rvcow-branch-manager.js +263 -0
- package/v3/dist/learning/dream/rvcow-branch-manager.js.map +1 -0
- package/v3/dist/learning/dream/speculative-dreamer.d.ts +129 -0
- package/v3/dist/learning/dream/speculative-dreamer.d.ts.map +1 -0
- package/v3/dist/learning/dream/speculative-dreamer.js +214 -0
- package/v3/dist/learning/dream/speculative-dreamer.js.map +1 -0
- package/v3/dist/learning/qe-reasoning-bank.d.ts +9 -0
- package/v3/dist/learning/qe-reasoning-bank.d.ts.map +1 -1
- package/v3/dist/learning/qe-reasoning-bank.js +50 -1
- package/v3/dist/learning/qe-reasoning-bank.js.map +1 -1
- package/v3/dist/mcp/bundle.js +8900 -7163
- package/v3/dist/mcp/services/mincut-routing-service.d.ts +148 -0
- package/v3/dist/mcp/services/mincut-routing-service.d.ts.map +1 -0
- package/v3/dist/mcp/services/mincut-routing-service.js +198 -0
- package/v3/dist/mcp/services/mincut-routing-service.js.map +1 -0
- package/v3/dist/mcp/services/task-router.d.ts +4 -0
- package/v3/dist/mcp/services/task-router.d.ts.map +1 -1
- package/v3/dist/mcp/services/task-router.js +57 -15
- package/v3/dist/mcp/services/task-router.js.map +1 -1
- package/v3/dist/monitoring/structural-health.d.ts +142 -0
- package/v3/dist/monitoring/structural-health.d.ts.map +1 -0
- package/v3/dist/monitoring/structural-health.js +201 -0
- package/v3/dist/monitoring/structural-health.js.map +1 -0
- package/v3/package.json +1 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RVF Dual-Writer Service
|
|
3
|
+
*
|
|
4
|
+
* Phase 3 Task 3.1: Writes learning data to both SQLite (existing) and
|
|
5
|
+
* an RVF container (new native backend). Ensures SQLite writes are never
|
|
6
|
+
* broken by RVF failures, and supports gradual promotion from SQLite-only
|
|
7
|
+
* to dual-write to RVF-primary mode.
|
|
8
|
+
*
|
|
9
|
+
* The dual-writer sits between the QEReasoningBank and the storage layer,
|
|
10
|
+
* intercepting pattern embedding writes/deletes/searches and routing them
|
|
11
|
+
* to one or both backends depending on the configured mode.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Wraps the real RvfNativeAdapter to conform to the simpler RvfStore interface.
|
|
15
|
+
*/
|
|
16
|
+
function wrapNativeAdapter(adapter, dim) {
|
|
17
|
+
return {
|
|
18
|
+
ingest(entries) {
|
|
19
|
+
adapter.ingest(entries);
|
|
20
|
+
},
|
|
21
|
+
search(query, k) {
|
|
22
|
+
return adapter.search(query, k).map((r) => ({ id: r.id, score: r.score }));
|
|
23
|
+
},
|
|
24
|
+
delete(ids) {
|
|
25
|
+
adapter.delete(ids);
|
|
26
|
+
},
|
|
27
|
+
status() {
|
|
28
|
+
const s = adapter.status();
|
|
29
|
+
return {
|
|
30
|
+
totalVectors: s.totalVectors,
|
|
31
|
+
dimensions: dim,
|
|
32
|
+
totalSegments: s.totalSegments,
|
|
33
|
+
fileSizeBytes: s.fileSizeBytes,
|
|
34
|
+
epoch: s.epoch,
|
|
35
|
+
witnessValid: s.witnessValid,
|
|
36
|
+
witnessEntries: s.witnessEntries,
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
close() {
|
|
40
|
+
adapter.close();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function tableExists(db, name) {
|
|
45
|
+
const row = db.prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name=?").get(name);
|
|
46
|
+
return (row?.cnt ?? 0) > 0;
|
|
47
|
+
}
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// RvfDualWriter
|
|
50
|
+
// ============================================================================
|
|
51
|
+
export class RvfDualWriter {
|
|
52
|
+
db;
|
|
53
|
+
config;
|
|
54
|
+
rvfStore = null;
|
|
55
|
+
rvfAvailable = false;
|
|
56
|
+
constructor(db, config) {
|
|
57
|
+
this.db = db;
|
|
58
|
+
this.config = {
|
|
59
|
+
...config,
|
|
60
|
+
dimensions: config.dimensions ?? 384,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Initialize the RVF store (create or open).
|
|
65
|
+
*
|
|
66
|
+
* If the native adapter is not available, the writer degrades gracefully
|
|
67
|
+
* to sqlite-only regardless of the configured mode.
|
|
68
|
+
*/
|
|
69
|
+
async initialize() {
|
|
70
|
+
if (this.config.mode === 'sqlite-only') {
|
|
71
|
+
this.rvfAvailable = false;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
// Dynamic import so the module is optional
|
|
76
|
+
const adapter = await import('./rvf-native-adapter.js');
|
|
77
|
+
if (!adapter.isRvfNativeAvailable()) {
|
|
78
|
+
this.rvfAvailable = false;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Try open first, create if it fails
|
|
82
|
+
let nativeAdapter;
|
|
83
|
+
try {
|
|
84
|
+
nativeAdapter = adapter.openRvfStore(this.config.rvfPath);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
nativeAdapter = adapter.createRvfStore(this.config.rvfPath, this.config.dimensions);
|
|
88
|
+
}
|
|
89
|
+
this.rvfStore = wrapNativeAdapter(nativeAdapter, this.config.dimensions);
|
|
90
|
+
this.rvfAvailable = true;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Native adapter module not available at all
|
|
94
|
+
this.rvfAvailable = false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Allow injecting an RVF store directly (useful for testing).
|
|
99
|
+
*/
|
|
100
|
+
setRvfStore(store) {
|
|
101
|
+
this.rvfStore = store;
|
|
102
|
+
this.rvfAvailable = true;
|
|
103
|
+
}
|
|
104
|
+
// --------------------------------------------------------------------------
|
|
105
|
+
// Write
|
|
106
|
+
// --------------------------------------------------------------------------
|
|
107
|
+
/**
|
|
108
|
+
* Write a pattern embedding to both stores.
|
|
109
|
+
*
|
|
110
|
+
* SQLite is always written first. RVF failures never propagate to SQLite.
|
|
111
|
+
*/
|
|
112
|
+
writePattern(patternId, embedding) {
|
|
113
|
+
const result = {
|
|
114
|
+
sqliteSuccess: false,
|
|
115
|
+
rvfSuccess: false,
|
|
116
|
+
};
|
|
117
|
+
// 1. Always write to SQLite qe_pattern_embeddings
|
|
118
|
+
try {
|
|
119
|
+
this.writeSqliteEmbedding(patternId, embedding);
|
|
120
|
+
result.sqliteSuccess = true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
result.sqliteSuccess = false;
|
|
124
|
+
}
|
|
125
|
+
// 2. Write to RVF if mode requires it and adapter is available
|
|
126
|
+
if (this.shouldWriteRvf()) {
|
|
127
|
+
try {
|
|
128
|
+
this.rvfStore.ingest([{
|
|
129
|
+
id: patternId,
|
|
130
|
+
vector: embedding instanceof Float32Array ? Array.from(embedding) : embedding,
|
|
131
|
+
}]);
|
|
132
|
+
result.rvfSuccess = true;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
result.rvfSuccess = false;
|
|
136
|
+
result.divergence = 'rvf-write-failed';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
// --------------------------------------------------------------------------
|
|
142
|
+
// Delete
|
|
143
|
+
// --------------------------------------------------------------------------
|
|
144
|
+
/**
|
|
145
|
+
* Delete a pattern embedding from both stores.
|
|
146
|
+
*/
|
|
147
|
+
deletePattern(patternId) {
|
|
148
|
+
const result = {
|
|
149
|
+
sqliteSuccess: false,
|
|
150
|
+
rvfSuccess: false,
|
|
151
|
+
};
|
|
152
|
+
// 1. Always delete from SQLite
|
|
153
|
+
try {
|
|
154
|
+
this.deleteSqliteEmbedding(patternId);
|
|
155
|
+
result.sqliteSuccess = true;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
result.sqliteSuccess = false;
|
|
159
|
+
}
|
|
160
|
+
// 2. Delete from RVF if applicable
|
|
161
|
+
if (this.shouldWriteRvf()) {
|
|
162
|
+
try {
|
|
163
|
+
this.rvfStore.delete([patternId]);
|
|
164
|
+
result.rvfSuccess = true;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
result.rvfSuccess = false;
|
|
168
|
+
result.divergence = 'rvf-delete-failed';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
// --------------------------------------------------------------------------
|
|
174
|
+
// Search
|
|
175
|
+
// --------------------------------------------------------------------------
|
|
176
|
+
/**
|
|
177
|
+
* Search for similar vectors. In rvf-primary mode, tries RVF first and
|
|
178
|
+
* falls back to SQLite on error. Otherwise uses SQLite.
|
|
179
|
+
*/
|
|
180
|
+
search(query, k) {
|
|
181
|
+
if (this.config.mode === 'rvf-primary' && this.rvfAvailable && this.rvfStore) {
|
|
182
|
+
try {
|
|
183
|
+
return this.rvfStore.search(query, k);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// Fall back to SQLite
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// SQLite-based search (brute-force cosine similarity)
|
|
190
|
+
return this.searchSqlite(query, k);
|
|
191
|
+
}
|
|
192
|
+
// --------------------------------------------------------------------------
|
|
193
|
+
// Divergence
|
|
194
|
+
// --------------------------------------------------------------------------
|
|
195
|
+
/**
|
|
196
|
+
* Generate a divergence report between SQLite embeddings and RVF vectors.
|
|
197
|
+
*/
|
|
198
|
+
getDivergenceReport() {
|
|
199
|
+
const report = {
|
|
200
|
+
totalChecked: 0,
|
|
201
|
+
divergences: 0,
|
|
202
|
+
details: [],
|
|
203
|
+
};
|
|
204
|
+
// Get SQLite embedding count
|
|
205
|
+
const sqliteCount = this.getSqliteEmbeddingCount();
|
|
206
|
+
report.totalChecked = sqliteCount;
|
|
207
|
+
if (!this.rvfAvailable || !this.rvfStore) {
|
|
208
|
+
// If RVF is not available, every SQLite embedding is "missing in RVF"
|
|
209
|
+
if (this.config.mode !== 'sqlite-only' && sqliteCount > 0) {
|
|
210
|
+
report.divergences = 1;
|
|
211
|
+
report.details.push({
|
|
212
|
+
patternId: '*',
|
|
213
|
+
issue: 'count-mismatch',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return report;
|
|
217
|
+
}
|
|
218
|
+
// Get RVF vector count
|
|
219
|
+
const rvfStatus = this.rvfStore.status();
|
|
220
|
+
const rvfCount = rvfStatus.totalVectors;
|
|
221
|
+
if (sqliteCount !== rvfCount) {
|
|
222
|
+
report.divergences = 1;
|
|
223
|
+
report.details.push({
|
|
224
|
+
patternId: '*',
|
|
225
|
+
issue: 'count-mismatch',
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return report;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Returns true when promotion from dual-write to rvf-primary is safe
|
|
232
|
+
* (zero divergences between the two stores).
|
|
233
|
+
*/
|
|
234
|
+
isPromotionSafe() {
|
|
235
|
+
const report = this.getDivergenceReport();
|
|
236
|
+
return report.divergences === 0;
|
|
237
|
+
}
|
|
238
|
+
// --------------------------------------------------------------------------
|
|
239
|
+
// Status
|
|
240
|
+
// --------------------------------------------------------------------------
|
|
241
|
+
/**
|
|
242
|
+
* Combined status from both backends.
|
|
243
|
+
*/
|
|
244
|
+
status() {
|
|
245
|
+
const sqlitePatternCount = this.getSqlitePatternCount();
|
|
246
|
+
const sqliteVectorCount = this.getSqliteEmbeddingCount();
|
|
247
|
+
let rvfStatus = null;
|
|
248
|
+
if (this.rvfAvailable && this.rvfStore) {
|
|
249
|
+
try {
|
|
250
|
+
rvfStatus = this.rvfStore.status();
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
rvfStatus = null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
sqlite: {
|
|
258
|
+
patternCount: sqlitePatternCount,
|
|
259
|
+
vectorCount: sqliteVectorCount,
|
|
260
|
+
},
|
|
261
|
+
rvf: rvfStatus,
|
|
262
|
+
mode: this.config.mode,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
// --------------------------------------------------------------------------
|
|
266
|
+
// Lifecycle
|
|
267
|
+
// --------------------------------------------------------------------------
|
|
268
|
+
/**
|
|
269
|
+
* Close the RVF store. SQLite is managed externally.
|
|
270
|
+
*/
|
|
271
|
+
close() {
|
|
272
|
+
if (this.rvfStore) {
|
|
273
|
+
try {
|
|
274
|
+
this.rvfStore.close();
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// Ignore close errors
|
|
278
|
+
}
|
|
279
|
+
this.rvfStore = null;
|
|
280
|
+
this.rvfAvailable = false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// --------------------------------------------------------------------------
|
|
284
|
+
// Private helpers
|
|
285
|
+
// --------------------------------------------------------------------------
|
|
286
|
+
shouldWriteRvf() {
|
|
287
|
+
return ((this.config.mode === 'dual-write' || this.config.mode === 'rvf-primary') &&
|
|
288
|
+
this.rvfAvailable &&
|
|
289
|
+
this.rvfStore !== null);
|
|
290
|
+
}
|
|
291
|
+
writeSqliteEmbedding(patternId, embedding) {
|
|
292
|
+
const blob = Buffer.from(embedding instanceof Float32Array
|
|
293
|
+
? embedding.buffer
|
|
294
|
+
: new Float32Array(embedding).buffer);
|
|
295
|
+
const dimension = embedding.length;
|
|
296
|
+
// Upsert into qe_pattern_embeddings
|
|
297
|
+
if (tableExists(this.db, 'qe_pattern_embeddings')) {
|
|
298
|
+
this.db.prepare(`
|
|
299
|
+
INSERT INTO qe_pattern_embeddings (pattern_id, embedding, dimension, model, created_at)
|
|
300
|
+
VALUES (?, ?, ?, 'all-MiniLM-L6-v2', datetime('now'))
|
|
301
|
+
ON CONFLICT(pattern_id) DO UPDATE SET
|
|
302
|
+
embedding = excluded.embedding,
|
|
303
|
+
dimension = excluded.dimension,
|
|
304
|
+
created_at = datetime('now')
|
|
305
|
+
`).run(patternId, blob, dimension);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
deleteSqliteEmbedding(patternId) {
|
|
309
|
+
if (tableExists(this.db, 'qe_pattern_embeddings')) {
|
|
310
|
+
this.db.prepare('DELETE FROM qe_pattern_embeddings WHERE pattern_id = ?').run(patternId);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
getSqliteEmbeddingCount() {
|
|
314
|
+
if (!tableExists(this.db, 'qe_pattern_embeddings'))
|
|
315
|
+
return 0;
|
|
316
|
+
const row = this.db.prepare('SELECT COUNT(*) as cnt FROM qe_pattern_embeddings').get();
|
|
317
|
+
return row?.cnt ?? 0;
|
|
318
|
+
}
|
|
319
|
+
getSqlitePatternCount() {
|
|
320
|
+
if (!tableExists(this.db, 'qe_patterns'))
|
|
321
|
+
return 0;
|
|
322
|
+
const row = this.db.prepare('SELECT COUNT(*) as cnt FROM qe_patterns').get();
|
|
323
|
+
return row?.cnt ?? 0;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Brute-force cosine similarity search over SQLite embeddings.
|
|
327
|
+
* This is the fallback when RVF is unavailable.
|
|
328
|
+
*/
|
|
329
|
+
searchSqlite(query, k) {
|
|
330
|
+
if (!tableExists(this.db, 'qe_pattern_embeddings'))
|
|
331
|
+
return [];
|
|
332
|
+
const rows = this.db.prepare('SELECT pattern_id, embedding, dimension FROM qe_pattern_embeddings').all();
|
|
333
|
+
const queryArr = query instanceof Float32Array ? Array.from(query) : query;
|
|
334
|
+
const queryMag = Math.sqrt(queryArr.reduce((s, v) => s + v * v, 0));
|
|
335
|
+
if (queryMag === 0)
|
|
336
|
+
return [];
|
|
337
|
+
const scored = [];
|
|
338
|
+
for (const row of rows) {
|
|
339
|
+
const float32 = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.dimension);
|
|
340
|
+
const vec = Array.from(float32);
|
|
341
|
+
let dot = 0;
|
|
342
|
+
let mag = 0;
|
|
343
|
+
for (let i = 0; i < vec.length && i < queryArr.length; i++) {
|
|
344
|
+
dot += vec[i] * queryArr[i];
|
|
345
|
+
mag += vec[i] * vec[i];
|
|
346
|
+
}
|
|
347
|
+
mag = Math.sqrt(mag);
|
|
348
|
+
const score = mag > 0 ? dot / (queryMag * mag) : 0;
|
|
349
|
+
scored.push({ id: row.pattern_id, score });
|
|
350
|
+
}
|
|
351
|
+
scored.sort((a, b) => b.score - a.score);
|
|
352
|
+
return scored.slice(0, k);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// ============================================================================
|
|
356
|
+
// Factory
|
|
357
|
+
// ============================================================================
|
|
358
|
+
/**
|
|
359
|
+
* Create a new RvfDualWriter instance.
|
|
360
|
+
*
|
|
361
|
+
* @param db - better-sqlite3 Database instance
|
|
362
|
+
* @param config - Dual-write configuration
|
|
363
|
+
*/
|
|
364
|
+
export function createDualWriter(db, config) {
|
|
365
|
+
return new RvfDualWriter(db, config);
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=rvf-dual-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvf-dual-writer.js","sourceRoot":"","sources":["../../../src/integrations/ruvector/rvf-dual-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RVF Native Adapter — thin wrapper around @ruvector/rvf-node N-API binding.
|
|
3
|
+
*
|
|
4
|
+
* Bypasses the buggy @ruvector/rvf SDK wrapper and talks directly to the
|
|
5
|
+
* native layer. Adds string-ID mapping on top of the numeric labels the
|
|
6
|
+
* native API requires, and persists the mapping as a sidecar JSON file.
|
|
7
|
+
*/
|
|
8
|
+
export interface RvfSearchResult {
|
|
9
|
+
/** String ID (mapped back from numeric label) */
|
|
10
|
+
id: string;
|
|
11
|
+
/** Raw distance from the native HNSW index */
|
|
12
|
+
distance: number;
|
|
13
|
+
/** Similarity score: 1 / (1 + distance) — cosine-compatible */
|
|
14
|
+
score: number;
|
|
15
|
+
}
|
|
16
|
+
export interface RvfStatus {
|
|
17
|
+
totalVectors: number;
|
|
18
|
+
totalSegments: number;
|
|
19
|
+
fileSizeBytes: number;
|
|
20
|
+
epoch: number;
|
|
21
|
+
witnessValid: boolean;
|
|
22
|
+
witnessEntries: number;
|
|
23
|
+
}
|
|
24
|
+
export interface RvfNativeAdapter {
|
|
25
|
+
/** Insert vectors with string IDs */
|
|
26
|
+
ingest(entries: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
vector: Float32Array | number[];
|
|
29
|
+
}>): {
|
|
30
|
+
accepted: number;
|
|
31
|
+
rejected: number;
|
|
32
|
+
};
|
|
33
|
+
/** Search k nearest neighbours */
|
|
34
|
+
search(query: Float32Array | number[], k: number): RvfSearchResult[];
|
|
35
|
+
/** Delete by string IDs — returns count deleted */
|
|
36
|
+
delete(ids: string[]): number;
|
|
37
|
+
/** COW fork — creates an independent copy at `childPath` */
|
|
38
|
+
fork(childPath: string): RvfNativeAdapter;
|
|
39
|
+
/** Database status including witness-chain verification */
|
|
40
|
+
status(): RvfStatus;
|
|
41
|
+
/** Vector dimensionality */
|
|
42
|
+
dimension(): number;
|
|
43
|
+
/** Total vector count */
|
|
44
|
+
size(): number;
|
|
45
|
+
/** Compact to reclaim space from deleted vectors */
|
|
46
|
+
compact(): void;
|
|
47
|
+
/** Close and release native resources */
|
|
48
|
+
close(): void;
|
|
49
|
+
/** Whether the underlying database handle is still open */
|
|
50
|
+
isOpen(): boolean;
|
|
51
|
+
/** Filesystem path of the RVF container */
|
|
52
|
+
path(): string;
|
|
53
|
+
}
|
|
54
|
+
/** Create a new RVF container */
|
|
55
|
+
export declare function createRvfStore(path: string, dimensions: number): RvfNativeAdapter;
|
|
56
|
+
/** Open an existing RVF container (loads persisted ID mapping) */
|
|
57
|
+
export declare function openRvfStore(path: string): RvfNativeAdapter;
|
|
58
|
+
/** Check whether the native binding is loadable on this platform */
|
|
59
|
+
export declare function isRvfNativeAvailable(): boolean;
|
|
60
|
+
//# sourceMappingURL=rvf-native-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rvf-native-adapter.d.ts","sourceRoot":"","sources":["../../../src/integrations/ruvector/rvf-native-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA4BH,MAAM,WAAW,eAAe;IAC9B,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,CAAA;KAAE,CAAC,GAAG;QACvE,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IAEF,kCAAkC;IAClC,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,CAAC;IAErE,mDAAmD;IACnD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE9B,4DAA4D;IAC5D,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAE1C,2DAA2D;IAC3D,MAAM,IAAI,SAAS,CAAC;IAEpB,4BAA4B;IAC5B,SAAS,IAAI,MAAM,CAAC;IAEpB,yBAAyB;IACzB,IAAI,IAAI,MAAM,CAAC;IAEf,oDAAoD;IACpD,OAAO,IAAI,IAAI,CAAC;IAEhB,yCAAyC;IACzC,KAAK,IAAI,IAAI,CAAC;IAEd,2DAA2D;IAC3D,MAAM,IAAI,OAAO,CAAC;IAElB,2CAA2C;IAC3C,IAAI,IAAI,MAAM,CAAC;CAChB;AA+PD,iCAAiC;AACjC,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAiBjF;AAED,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAW3D;AAED,oEAAoE;AACpE,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RVF Native Adapter — thin wrapper around @ruvector/rvf-node N-API binding.
|
|
3
|
+
*
|
|
4
|
+
* Bypasses the buggy @ruvector/rvf SDK wrapper and talks directly to the
|
|
5
|
+
* native layer. Adds string-ID mapping on top of the numeric labels the
|
|
6
|
+
* native API requires, and persists the mapping as a sidecar JSON file.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, writeFileSync, copyFileSync, existsSync } from 'fs';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Native binding — lazy-loaded, failure-safe
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
let _native = null;
|
|
14
|
+
let _nativeChecked = false;
|
|
15
|
+
function getNative() {
|
|
16
|
+
if (_nativeChecked)
|
|
17
|
+
return _native;
|
|
18
|
+
_nativeChecked = true;
|
|
19
|
+
try {
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
21
|
+
_native = require('@ruvector/rvf-node');
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
_native = null;
|
|
25
|
+
}
|
|
26
|
+
return _native;
|
|
27
|
+
}
|
|
28
|
+
function idMapPath(rvfPath) {
|
|
29
|
+
return `${rvfPath}.idmap.json`;
|
|
30
|
+
}
|
|
31
|
+
function loadIdMap(rvfPath) {
|
|
32
|
+
const p = idMapPath(rvfPath);
|
|
33
|
+
const strToNum = new Map();
|
|
34
|
+
const numToStr = new Map();
|
|
35
|
+
let nextLabel = 1;
|
|
36
|
+
if (existsSync(p)) {
|
|
37
|
+
try {
|
|
38
|
+
const raw = JSON.parse(readFileSync(p, 'utf-8'));
|
|
39
|
+
nextLabel = raw.nextLabel;
|
|
40
|
+
for (const [str, num] of raw.entries) {
|
|
41
|
+
strToNum.set(str, num);
|
|
42
|
+
numToStr.set(num, str);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// corrupted file — start fresh
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { strToNum, numToStr, nextLabel };
|
|
50
|
+
}
|
|
51
|
+
function saveIdMap(rvfPath, strToNum, nextLabel) {
|
|
52
|
+
const data = {
|
|
53
|
+
nextLabel,
|
|
54
|
+
entries: Array.from(strToNum.entries()),
|
|
55
|
+
};
|
|
56
|
+
writeFileSync(idMapPath(rvfPath), JSON.stringify(data), 'utf-8');
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Implementation
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
class RvfNativeAdapterImpl {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
db;
|
|
64
|
+
_path;
|
|
65
|
+
_dimension;
|
|
66
|
+
strToNum;
|
|
67
|
+
numToStr;
|
|
68
|
+
nextLabel;
|
|
69
|
+
_open;
|
|
70
|
+
constructor(
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
+
db, filePath, dim, strToNum, numToStr, nextLabel) {
|
|
73
|
+
this.db = db;
|
|
74
|
+
this._path = filePath;
|
|
75
|
+
this._dimension = dim;
|
|
76
|
+
this.strToNum = strToNum;
|
|
77
|
+
this.numToStr = numToStr;
|
|
78
|
+
this.nextLabel = nextLabel;
|
|
79
|
+
this._open = true;
|
|
80
|
+
}
|
|
81
|
+
// -- ingest ---------------------------------------------------------------
|
|
82
|
+
ingest(entries) {
|
|
83
|
+
this.ensureOpen();
|
|
84
|
+
if (entries.length === 0)
|
|
85
|
+
return { accepted: 0, rejected: 0 };
|
|
86
|
+
const dim = this._dimension;
|
|
87
|
+
const flat = new Float32Array(entries.length * dim);
|
|
88
|
+
const ids = [];
|
|
89
|
+
for (let i = 0; i < entries.length; i++) {
|
|
90
|
+
const { id, vector } = entries[i];
|
|
91
|
+
// Resolve or assign numeric label
|
|
92
|
+
let label = this.strToNum.get(id);
|
|
93
|
+
if (label === undefined) {
|
|
94
|
+
label = this.nextLabel++;
|
|
95
|
+
this.strToNum.set(id, label);
|
|
96
|
+
this.numToStr.set(label, id);
|
|
97
|
+
}
|
|
98
|
+
ids.push(label);
|
|
99
|
+
// Copy vector data into flat buffer
|
|
100
|
+
const src = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
|
101
|
+
flat.set(src.subarray(0, dim), i * dim);
|
|
102
|
+
}
|
|
103
|
+
const result = this.db.ingestBatch(flat, ids);
|
|
104
|
+
this.persistIdMap();
|
|
105
|
+
return { accepted: result.accepted, rejected: result.rejected };
|
|
106
|
+
}
|
|
107
|
+
// -- search ---------------------------------------------------------------
|
|
108
|
+
search(query, k) {
|
|
109
|
+
this.ensureOpen();
|
|
110
|
+
const q = query instanceof Float32Array ? query : new Float32Array(query);
|
|
111
|
+
const raw = this.db.query(q, k);
|
|
112
|
+
return raw
|
|
113
|
+
.map((r) => {
|
|
114
|
+
const strId = this.numToStr.get(r.id);
|
|
115
|
+
if (strId === undefined)
|
|
116
|
+
return null;
|
|
117
|
+
return {
|
|
118
|
+
id: strId,
|
|
119
|
+
distance: r.distance,
|
|
120
|
+
score: 1 / (1 + r.distance),
|
|
121
|
+
};
|
|
122
|
+
})
|
|
123
|
+
.filter((r) => r !== null);
|
|
124
|
+
}
|
|
125
|
+
// -- delete ---------------------------------------------------------------
|
|
126
|
+
delete(ids) {
|
|
127
|
+
this.ensureOpen();
|
|
128
|
+
const numericIds = [];
|
|
129
|
+
for (const id of ids) {
|
|
130
|
+
const label = this.strToNum.get(id);
|
|
131
|
+
if (label !== undefined) {
|
|
132
|
+
numericIds.push(label);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (numericIds.length === 0)
|
|
136
|
+
return 0;
|
|
137
|
+
const result = this.db.delete(numericIds);
|
|
138
|
+
// Remove from maps
|
|
139
|
+
for (const id of ids) {
|
|
140
|
+
const label = this.strToNum.get(id);
|
|
141
|
+
if (label !== undefined) {
|
|
142
|
+
this.strToNum.delete(id);
|
|
143
|
+
this.numToStr.delete(label);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
this.persistIdMap();
|
|
147
|
+
return result.deleted;
|
|
148
|
+
}
|
|
149
|
+
// -- fork -----------------------------------------------------------------
|
|
150
|
+
fork(childPath) {
|
|
151
|
+
this.ensureOpen();
|
|
152
|
+
// Copy the RVF file to get an independent snapshot with all data
|
|
153
|
+
copyFileSync(this._path, childPath);
|
|
154
|
+
// Copy the ID map
|
|
155
|
+
const childStrToNum = new Map(this.strToNum);
|
|
156
|
+
const childNumToStr = new Map(this.numToStr);
|
|
157
|
+
saveIdMap(childPath, childStrToNum, this.nextLabel);
|
|
158
|
+
const native = getNative();
|
|
159
|
+
const childDb = native.RvfDatabase.open(childPath);
|
|
160
|
+
return new RvfNativeAdapterImpl(childDb, childPath, this._dimension, childStrToNum, childNumToStr, this.nextLabel);
|
|
161
|
+
}
|
|
162
|
+
// -- status ---------------------------------------------------------------
|
|
163
|
+
status() {
|
|
164
|
+
this.ensureOpen();
|
|
165
|
+
const s = this.db.status();
|
|
166
|
+
// Count witness segments from the segment list
|
|
167
|
+
const segs = this.db.segments();
|
|
168
|
+
const witnessSegs = segs.filter((seg) => seg.segType === 'witness');
|
|
169
|
+
return {
|
|
170
|
+
totalVectors: s.totalVectors,
|
|
171
|
+
totalSegments: s.totalSegments,
|
|
172
|
+
fileSizeBytes: s.fileSize,
|
|
173
|
+
epoch: s.currentEpoch,
|
|
174
|
+
witnessValid: witnessSegs.length > 0,
|
|
175
|
+
witnessEntries: witnessSegs.length,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// -- accessors ------------------------------------------------------------
|
|
179
|
+
dimension() {
|
|
180
|
+
return this._dimension;
|
|
181
|
+
}
|
|
182
|
+
size() {
|
|
183
|
+
this.ensureOpen();
|
|
184
|
+
return this.db.status().totalVectors;
|
|
185
|
+
}
|
|
186
|
+
compact() {
|
|
187
|
+
this.ensureOpen();
|
|
188
|
+
this.db.compact();
|
|
189
|
+
}
|
|
190
|
+
close() {
|
|
191
|
+
if (this._open) {
|
|
192
|
+
this.db.close();
|
|
193
|
+
this._open = false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
isOpen() {
|
|
197
|
+
return this._open;
|
|
198
|
+
}
|
|
199
|
+
path() {
|
|
200
|
+
return this._path;
|
|
201
|
+
}
|
|
202
|
+
// -- internal -------------------------------------------------------------
|
|
203
|
+
ensureOpen() {
|
|
204
|
+
if (!this._open) {
|
|
205
|
+
throw new Error('RVF database is closed');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
persistIdMap() {
|
|
209
|
+
saveIdMap(this._path, this.strToNum, this.nextLabel);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// Factory functions
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
/** Create a new RVF container */
|
|
216
|
+
export function createRvfStore(path, dimensions) {
|
|
217
|
+
const native = getNative();
|
|
218
|
+
if (!native) {
|
|
219
|
+
throw new Error('@ruvector/rvf-node is not available — install the package or check platform compatibility');
|
|
220
|
+
}
|
|
221
|
+
const db = native.RvfDatabase.create(path, { dimension: dimensions });
|
|
222
|
+
const dim = db.dimension();
|
|
223
|
+
return new RvfNativeAdapterImpl(db, path, dim, new Map(), new Map(), 1);
|
|
224
|
+
}
|
|
225
|
+
/** Open an existing RVF container (loads persisted ID mapping) */
|
|
226
|
+
export function openRvfStore(path) {
|
|
227
|
+
const native = getNative();
|
|
228
|
+
if (!native) {
|
|
229
|
+
throw new Error('@ruvector/rvf-node is not available — install the package or check platform compatibility');
|
|
230
|
+
}
|
|
231
|
+
const db = native.RvfDatabase.open(path);
|
|
232
|
+
const dim = db.dimension();
|
|
233
|
+
const { strToNum, numToStr, nextLabel } = loadIdMap(path);
|
|
234
|
+
return new RvfNativeAdapterImpl(db, path, dim, strToNum, numToStr, nextLabel);
|
|
235
|
+
}
|
|
236
|
+
/** Check whether the native binding is loadable on this platform */
|
|
237
|
+
export function isRvfNativeAvailable() {
|
|
238
|
+
return getNative() !== null;
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=rvf-native-adapter.js.map
|