claude-flow 3.6.4 → 3.6.5
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/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/embeddings-tools.js +69 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +8 -3
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +1 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +23 -5
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +57 -2
- package/v3/@claude-flow/cli/dist/src/memory/rabitq-index.d.ts +60 -0
- package/v3/@claude-flow/cli/dist/src/memory/rabitq-index.js +226 -0
- package/v3/@claude-flow/cli/package.json +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.5",
|
|
4
4
|
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -804,5 +804,74 @@ export const embeddingsTools = [
|
|
|
804
804
|
};
|
|
805
805
|
},
|
|
806
806
|
},
|
|
807
|
+
// --- RaBitQ 1-bit quantized vector index ---
|
|
808
|
+
{
|
|
809
|
+
name: 'embeddings_rabitq_build',
|
|
810
|
+
description: 'Build RaBitQ 1-bit quantized index from stored embeddings (32× compression). Pre-filters candidates via Hamming scan before exact rerank.',
|
|
811
|
+
category: 'embeddings',
|
|
812
|
+
inputSchema: {
|
|
813
|
+
type: 'object',
|
|
814
|
+
properties: {
|
|
815
|
+
force: { type: 'boolean', description: 'Force rebuild even if index exists' },
|
|
816
|
+
},
|
|
817
|
+
},
|
|
818
|
+
handler: async (params) => {
|
|
819
|
+
const { buildRabitqIndex } = await import('../memory/rabitq-index.js');
|
|
820
|
+
return buildRabitqIndex({ force: params.force });
|
|
821
|
+
},
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
name: 'embeddings_rabitq_search',
|
|
825
|
+
description: 'Search via RaBitQ quantized index (fast Hamming scan). Returns candidate IDs for reranking.',
|
|
826
|
+
category: 'embeddings',
|
|
827
|
+
inputSchema: {
|
|
828
|
+
type: 'object',
|
|
829
|
+
properties: {
|
|
830
|
+
query: { type: 'string', description: 'Search query text' },
|
|
831
|
+
k: { type: 'number', description: 'Number of results (default: 10)' },
|
|
832
|
+
namespace: { type: 'string', description: 'Filter by namespace' },
|
|
833
|
+
},
|
|
834
|
+
required: ['query'],
|
|
835
|
+
},
|
|
836
|
+
handler: async (params) => {
|
|
837
|
+
const { validateText: vt } = await import('./validate-input.js');
|
|
838
|
+
const v = vt(params.query, 'query');
|
|
839
|
+
if (!v.valid)
|
|
840
|
+
return { success: false, error: v.error };
|
|
841
|
+
const { searchRabitq } = await import('../memory/rabitq-index.js');
|
|
842
|
+
const { generateEmbedding } = await import('../memory/memory-initializer.js');
|
|
843
|
+
const queryEmb = await generateEmbedding(params.query);
|
|
844
|
+
const results = await searchRabitq(queryEmb.embedding, {
|
|
845
|
+
k: params.k || 10,
|
|
846
|
+
namespace: params.namespace,
|
|
847
|
+
});
|
|
848
|
+
if (!results) {
|
|
849
|
+
return { success: false, error: 'RaBitQ index not built. Call embeddings_rabitq_build first.' };
|
|
850
|
+
}
|
|
851
|
+
return {
|
|
852
|
+
success: true,
|
|
853
|
+
results: results.map(r => ({
|
|
854
|
+
id: r.id.substring(0, 12),
|
|
855
|
+
key: r.key,
|
|
856
|
+
namespace: r.namespace,
|
|
857
|
+
distance: Math.round(r.distance * 10000) / 10000,
|
|
858
|
+
})),
|
|
859
|
+
count: results.length,
|
|
860
|
+
};
|
|
861
|
+
},
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
name: 'embeddings_rabitq_status',
|
|
865
|
+
description: 'Get RaBitQ quantized index status — availability, vector count, compression ratio',
|
|
866
|
+
category: 'embeddings',
|
|
867
|
+
inputSchema: {
|
|
868
|
+
type: 'object',
|
|
869
|
+
properties: {},
|
|
870
|
+
},
|
|
871
|
+
handler: async () => {
|
|
872
|
+
const { getRabitqStatus } = await import('../memory/rabitq-index.js');
|
|
873
|
+
return { success: true, ...getRabitqStatus() };
|
|
874
|
+
},
|
|
875
|
+
},
|
|
807
876
|
];
|
|
808
877
|
//# sourceMappingURL=embeddings-tools.js.map
|
|
@@ -2479,20 +2479,25 @@ export const hooksPatternStore = {
|
|
|
2479
2479
|
}
|
|
2480
2480
|
const success = reasoningResult?.success || storeResult.success;
|
|
2481
2481
|
const controller = reasoningResult?.controller || (storeResult.success ? 'bridge-store' : 'none');
|
|
2482
|
+
const hasEmbedding = !!storeResult.embedding || controller === 'reasoningBank' || controller === 'bridge-fallback';
|
|
2482
2483
|
return {
|
|
2483
2484
|
patternId: reasoningResult?.patternId || storeResult.id || patternId,
|
|
2484
2485
|
pattern,
|
|
2485
2486
|
type,
|
|
2486
2487
|
confidence,
|
|
2487
2488
|
indexed: success,
|
|
2488
|
-
hnswIndexed: success &&
|
|
2489
|
+
hnswIndexed: success && hasEmbedding,
|
|
2489
2490
|
embedding: storeResult.embedding,
|
|
2490
2491
|
timestamp,
|
|
2491
2492
|
controller,
|
|
2492
|
-
implementation: controller === 'reasoningBank'
|
|
2493
|
+
implementation: (controller === 'reasoningBank' || controller === 'bridge-fallback')
|
|
2494
|
+
? 'reasoning-bank-controller'
|
|
2495
|
+
: (storeResult.success ? 'real-hnsw-indexed' : 'memory-only'),
|
|
2493
2496
|
note: controller === 'reasoningBank'
|
|
2494
2497
|
? 'Pattern stored via ReasoningBank controller with HNSW indexing'
|
|
2495
|
-
:
|
|
2498
|
+
: controller === 'bridge-fallback'
|
|
2499
|
+
? 'Pattern stored via bridge with embedding and HNSW indexing'
|
|
2500
|
+
: (storeResult.success ? 'Pattern stored with vector embedding for semantic search' : (storeResult.error || 'Store function unavailable')),
|
|
2496
2501
|
};
|
|
2497
2502
|
},
|
|
2498
2503
|
};
|
|
@@ -381,6 +381,7 @@ export async function bridgeStoreEntry(options) {
|
|
|
381
381
|
success: true,
|
|
382
382
|
id,
|
|
383
383
|
embedding: embeddingJson ? { dimensions, model } : undefined,
|
|
384
|
+
rawEmbedding: embeddingJson ? JSON.parse(embeddingJson) : undefined,
|
|
384
385
|
guarded: true,
|
|
385
386
|
cached: true,
|
|
386
387
|
attested: true,
|
|
@@ -460,10 +461,11 @@ export async function bridgeSearchEntries(options) {
|
|
|
460
461
|
bm25ScoreVal = Math.min(bm25ScoreVal / 10, 1.0);
|
|
461
462
|
}
|
|
462
463
|
// Reciprocal rank fusion: combine semantic and BM25
|
|
463
|
-
// Weight: 0.7 semantic + 0.3 BM25
|
|
464
|
-
|
|
464
|
+
// Weight: 0.7 semantic + 0.3 BM25 when both embeddings present
|
|
465
|
+
// Fall back to BM25-only when either query or row lacks an embedding
|
|
466
|
+
const score = semanticScore > 0
|
|
465
467
|
? (0.7 * semanticScore + 0.3 * bm25ScoreVal)
|
|
466
|
-
: bm25ScoreVal;
|
|
468
|
+
: bm25ScoreVal;
|
|
467
469
|
if (score >= threshold) {
|
|
468
470
|
// Phase 4: ExplainableRecall provenance
|
|
469
471
|
const provenance = queryEmbedding
|
|
@@ -971,15 +973,31 @@ export async function bridgeStorePattern(options) {
|
|
|
971
973
|
return { success: true, patternId, controller: 'reasoningBank' };
|
|
972
974
|
}
|
|
973
975
|
// Fallback: store via bridge SQL
|
|
976
|
+
const patternValue = JSON.stringify({ pattern: options.pattern, type: options.type, confidence: options.confidence, metadata: options.metadata });
|
|
974
977
|
const result = await bridgeStoreEntry({
|
|
975
978
|
key: patternId,
|
|
976
|
-
value:
|
|
979
|
+
value: patternValue,
|
|
977
980
|
namespace: 'pattern',
|
|
978
981
|
generateEmbeddingFlag: true,
|
|
979
982
|
tags: [options.type, 'reasoning-pattern'],
|
|
980
983
|
dbPath: options.dbPath,
|
|
981
984
|
});
|
|
982
|
-
|
|
985
|
+
if (!result)
|
|
986
|
+
return null;
|
|
987
|
+
// Add to HNSW index for fast semantic search (bridgeStoreEntry stores SQL only)
|
|
988
|
+
if (result.rawEmbedding) {
|
|
989
|
+
try {
|
|
990
|
+
const { addToHNSWIndex } = await import('./memory-initializer.js');
|
|
991
|
+
await addToHNSWIndex(result.id, result.rawEmbedding, {
|
|
992
|
+
id: result.id,
|
|
993
|
+
key: patternId,
|
|
994
|
+
namespace: 'pattern',
|
|
995
|
+
content: patternValue,
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
catch { /* HNSW is best-effort */ }
|
|
999
|
+
}
|
|
1000
|
+
return { success: true, patternId: result.id, controller: 'bridge-fallback' };
|
|
983
1001
|
}
|
|
984
1002
|
catch {
|
|
985
1003
|
return null;
|
|
@@ -1689,8 +1689,19 @@ export async function storeEntry(options) {
|
|
|
1689
1689
|
const bridge = await getBridge();
|
|
1690
1690
|
if (bridge) {
|
|
1691
1691
|
const bridgeResult = await bridge.bridgeStoreEntry(options);
|
|
1692
|
-
if (bridgeResult)
|
|
1692
|
+
if (bridgeResult) {
|
|
1693
|
+
// Keep HNSW index in sync with bridge-stored entries
|
|
1694
|
+
if (bridgeResult.rawEmbedding && bridgeResult.success) {
|
|
1695
|
+
const ns = options.namespace || 'default';
|
|
1696
|
+
await addToHNSWIndex(bridgeResult.id, bridgeResult.rawEmbedding, {
|
|
1697
|
+
id: bridgeResult.id,
|
|
1698
|
+
key: options.key,
|
|
1699
|
+
namespace: ns,
|
|
1700
|
+
content: options.value,
|
|
1701
|
+
}).catch(() => { });
|
|
1702
|
+
}
|
|
1693
1703
|
return bridgeResult;
|
|
1704
|
+
}
|
|
1694
1705
|
}
|
|
1695
1706
|
// Fallback: raw sql.js
|
|
1696
1707
|
const { key, value, namespace = 'default', generateEmbeddingFlag = true, tags = [], ttl, dbPath: customPath, upsert = false } = options;
|
|
@@ -1799,7 +1810,51 @@ export async function searchEntries(options) {
|
|
|
1799
1810
|
// Generate query embedding
|
|
1800
1811
|
const queryEmb = await generateEmbedding(query);
|
|
1801
1812
|
const queryEmbedding = queryEmb.embedding;
|
|
1802
|
-
// Try
|
|
1813
|
+
// Try RaBitQ pre-filter first (32× compressed Hamming scan)
|
|
1814
|
+
try {
|
|
1815
|
+
const { searchRabitq } = await import('./rabitq-index.js');
|
|
1816
|
+
const rabitqCandidates = await searchRabitq(queryEmbedding, { k: limit * 2, namespace: effectiveNamespace });
|
|
1817
|
+
if (rabitqCandidates && rabitqCandidates.length > 0) {
|
|
1818
|
+
// Rerank candidates with exact cosine similarity from SQLite
|
|
1819
|
+
const initSqlJs = (await import('sql.js')).default;
|
|
1820
|
+
const SQL = await initSqlJs();
|
|
1821
|
+
const fileBuffer = fs.readFileSync(dbPath);
|
|
1822
|
+
const db = new SQL.Database(fileBuffer);
|
|
1823
|
+
const reranked = [];
|
|
1824
|
+
for (const candidate of rabitqCandidates) {
|
|
1825
|
+
const stmt = db.prepare('SELECT content, embedding FROM memory_entries WHERE id = ? AND status = ?');
|
|
1826
|
+
stmt.bind([candidate.id, 'active']);
|
|
1827
|
+
if (stmt.step()) {
|
|
1828
|
+
const [content, embeddingJson] = stmt.get();
|
|
1829
|
+
let score = 0;
|
|
1830
|
+
if (embeddingJson) {
|
|
1831
|
+
try {
|
|
1832
|
+
const embedding = JSON.parse(embeddingJson);
|
|
1833
|
+
score = cosineSim(queryEmbedding, embedding);
|
|
1834
|
+
}
|
|
1835
|
+
catch { /* skip */ }
|
|
1836
|
+
}
|
|
1837
|
+
if (score >= threshold) {
|
|
1838
|
+
reranked.push({
|
|
1839
|
+
id: candidate.id.substring(0, 12),
|
|
1840
|
+
key: candidate.key || candidate.id.substring(0, 15),
|
|
1841
|
+
content: (content || '').substring(0, 60) + ((content || '').length > 60 ? '...' : ''),
|
|
1842
|
+
score,
|
|
1843
|
+
namespace: candidate.namespace,
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
stmt.free();
|
|
1848
|
+
}
|
|
1849
|
+
db.close();
|
|
1850
|
+
if (reranked.length > 0) {
|
|
1851
|
+
reranked.sort((a, b) => b.score - a.score);
|
|
1852
|
+
return { success: true, results: reranked.slice(0, limit), searchTime: Date.now() - startTime };
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
catch { /* RaBitQ unavailable, fall through */ }
|
|
1857
|
+
// Try HNSW search (150x faster than brute-force)
|
|
1803
1858
|
const hnswResults = await searchHNSWIndex(queryEmbedding, { k: limit, namespace: effectiveNamespace });
|
|
1804
1859
|
if (hnswResults && hnswResults.length > 0) {
|
|
1805
1860
|
// Filter by threshold
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RaBitQ Index — 1-bit quantized vector pre-filter (32× compression)
|
|
3
|
+
*
|
|
4
|
+
* Wraps @ruvector/rabitq-wasm to provide Hamming-scan pre-filtering
|
|
5
|
+
* over quantized embeddings. Candidates are reranked with exact cosine
|
|
6
|
+
* similarity from the full-precision source (HNSW or SQLite).
|
|
7
|
+
*
|
|
8
|
+
* Lifecycle:
|
|
9
|
+
* 1. build() — bulk-load all embeddings from SQLite into the WASM index
|
|
10
|
+
* 2. search() — fast Hamming scan → candidate ids → caller reranks
|
|
11
|
+
* 3. rebuild() — called when entry count drifts >20% from last build
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Build or rebuild the RaBitQ index from SQLite embeddings.
|
|
15
|
+
* Returns entry count or 0 if RaBitQ is unavailable.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildRabitqIndex(options?: {
|
|
18
|
+
dbPath?: string;
|
|
19
|
+
dimensions?: number;
|
|
20
|
+
force?: boolean;
|
|
21
|
+
}): Promise<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
vectorCount: number;
|
|
24
|
+
dimensions: number;
|
|
25
|
+
compressionRatio: number;
|
|
26
|
+
buildTimeMs: number;
|
|
27
|
+
wasmVersion?: string;
|
|
28
|
+
error?: string;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Search the RaBitQ index for candidate IDs.
|
|
32
|
+
* Returns null if index not built or unavailable.
|
|
33
|
+
* Caller is responsible for reranking with exact similarity.
|
|
34
|
+
*/
|
|
35
|
+
export declare function searchRabitq(queryEmbedding: number[], options?: {
|
|
36
|
+
k?: number;
|
|
37
|
+
namespace?: string;
|
|
38
|
+
}): Promise<Array<{
|
|
39
|
+
id: string;
|
|
40
|
+
key: string;
|
|
41
|
+
namespace: string;
|
|
42
|
+
distance: number;
|
|
43
|
+
position: number;
|
|
44
|
+
}> | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Check if the RaBitQ index needs rebuilding.
|
|
47
|
+
*/
|
|
48
|
+
export declare function shouldRebuildRabitq(currentEntryCount: number): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Get RaBitQ index status.
|
|
51
|
+
*/
|
|
52
|
+
export declare function getRabitqStatus(): {
|
|
53
|
+
available: boolean;
|
|
54
|
+
initialized: boolean;
|
|
55
|
+
vectorCount: number;
|
|
56
|
+
dimensions: number;
|
|
57
|
+
builtAt: number | null;
|
|
58
|
+
compressionRatio: number;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=rabitq-index.d.ts.map
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RaBitQ Index — 1-bit quantized vector pre-filter (32× compression)
|
|
3
|
+
*
|
|
4
|
+
* Wraps @ruvector/rabitq-wasm to provide Hamming-scan pre-filtering
|
|
5
|
+
* over quantized embeddings. Candidates are reranked with exact cosine
|
|
6
|
+
* similarity from the full-precision source (HNSW or SQLite).
|
|
7
|
+
*
|
|
8
|
+
* Lifecycle:
|
|
9
|
+
* 1. build() — bulk-load all embeddings from SQLite into the WASM index
|
|
10
|
+
* 2. search() — fast Hamming scan → candidate ids → caller reranks
|
|
11
|
+
* 3. rebuild() — called when entry count drifts >20% from last build
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
const RABITQ_SEED = 42n;
|
|
16
|
+
const RABITQ_RERANK_FACTOR = 20;
|
|
17
|
+
const REBUILD_DRIFT_THRESHOLD = 0.2; // rebuild when count drifts >20%
|
|
18
|
+
let rabitqState = null;
|
|
19
|
+
let rabitqInitializing = false;
|
|
20
|
+
async function loadRabitqModule() {
|
|
21
|
+
try {
|
|
22
|
+
const mod = await import('@ruvector/rabitq-wasm');
|
|
23
|
+
// Node.js: use initSync with the WASM bytes
|
|
24
|
+
const { createRequire } = await import('module');
|
|
25
|
+
const require = createRequire(import.meta.url);
|
|
26
|
+
const wasmPath = require.resolve('@ruvector/rabitq-wasm/ruvector_rabitq_wasm_bg.wasm');
|
|
27
|
+
const wasmBytes = fs.readFileSync(wasmPath);
|
|
28
|
+
mod.initSync({ module: wasmBytes });
|
|
29
|
+
return {
|
|
30
|
+
RabitqIndex: mod.RabitqIndex,
|
|
31
|
+
initSync: mod.initSync,
|
|
32
|
+
version: mod.version,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build or rebuild the RaBitQ index from SQLite embeddings.
|
|
41
|
+
* Returns entry count or 0 if RaBitQ is unavailable.
|
|
42
|
+
*/
|
|
43
|
+
export async function buildRabitqIndex(options) {
|
|
44
|
+
if (rabitqInitializing) {
|
|
45
|
+
return { success: false, vectorCount: 0, dimensions: 0, compressionRatio: 0, buildTimeMs: 0, error: 'Build already in progress' };
|
|
46
|
+
}
|
|
47
|
+
rabitqInitializing = true;
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
try {
|
|
50
|
+
const mod = await loadRabitqModule();
|
|
51
|
+
if (!mod) {
|
|
52
|
+
rabitqInitializing = false;
|
|
53
|
+
return { success: false, vectorCount: 0, dimensions: 0, compressionRatio: 0, buildTimeMs: 0, error: '@ruvector/rabitq-wasm not available' };
|
|
54
|
+
}
|
|
55
|
+
const dimensions = options?.dimensions ?? 384;
|
|
56
|
+
const swarmDir = path.resolve(process.cwd(), '.swarm');
|
|
57
|
+
const dbPath = options?.dbPath ? path.resolve(options.dbPath) : path.join(swarmDir, 'memory.db');
|
|
58
|
+
if (!fs.existsSync(dbPath)) {
|
|
59
|
+
rabitqInitializing = false;
|
|
60
|
+
return { success: false, vectorCount: 0, dimensions, compressionRatio: 0, buildTimeMs: 0, error: 'Database not found' };
|
|
61
|
+
}
|
|
62
|
+
// Load embeddings from SQLite
|
|
63
|
+
const initSqlJs = (await import('sql.js')).default;
|
|
64
|
+
const SQL = await initSqlJs();
|
|
65
|
+
const fileBuffer = fs.readFileSync(dbPath);
|
|
66
|
+
const db = new SQL.Database(fileBuffer);
|
|
67
|
+
const result = db.exec(`
|
|
68
|
+
SELECT id, key, namespace, embedding
|
|
69
|
+
FROM memory_entries
|
|
70
|
+
WHERE status = 'active' AND embedding IS NOT NULL
|
|
71
|
+
LIMIT 50000
|
|
72
|
+
`);
|
|
73
|
+
const entries = [];
|
|
74
|
+
const vectors = [];
|
|
75
|
+
if (result[0]?.values) {
|
|
76
|
+
for (const row of result[0].values) {
|
|
77
|
+
const [id, key, ns, embeddingJson] = row;
|
|
78
|
+
if (!embeddingJson)
|
|
79
|
+
continue;
|
|
80
|
+
try {
|
|
81
|
+
const embedding = JSON.parse(embeddingJson);
|
|
82
|
+
if (embedding.length !== dimensions)
|
|
83
|
+
continue;
|
|
84
|
+
entries.push({ id: String(id), key: key || String(id), namespace: ns || 'default' });
|
|
85
|
+
vectors.push(...embedding);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// skip invalid
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
db.close();
|
|
93
|
+
if (entries.length < 2) {
|
|
94
|
+
rabitqInitializing = false;
|
|
95
|
+
return { success: false, vectorCount: entries.length, dimensions, compressionRatio: 0, buildTimeMs: Date.now() - startTime, error: 'Need at least 2 vectors to build RaBitQ index' };
|
|
96
|
+
}
|
|
97
|
+
// Build the RaBitQ index
|
|
98
|
+
const flatVectors = new Float32Array(vectors);
|
|
99
|
+
const index = mod.RabitqIndex.build(flatVectors, dimensions, RABITQ_SEED, RABITQ_RERANK_FACTOR);
|
|
100
|
+
// Free old index if exists
|
|
101
|
+
if (rabitqState?.index) {
|
|
102
|
+
try {
|
|
103
|
+
rabitqState.index.free();
|
|
104
|
+
}
|
|
105
|
+
catch { /* already freed */ }
|
|
106
|
+
}
|
|
107
|
+
rabitqState = {
|
|
108
|
+
index,
|
|
109
|
+
entries,
|
|
110
|
+
dimensions,
|
|
111
|
+
builtAt: Date.now(),
|
|
112
|
+
vectorCount: entries.length,
|
|
113
|
+
};
|
|
114
|
+
// Persist metadata for fast reload hint
|
|
115
|
+
try {
|
|
116
|
+
const metaPath = path.join(swarmDir, 'rabitq.meta.json');
|
|
117
|
+
fs.writeFileSync(metaPath, JSON.stringify({
|
|
118
|
+
vectorCount: entries.length,
|
|
119
|
+
dimensions,
|
|
120
|
+
builtAt: rabitqState.builtAt,
|
|
121
|
+
wasmVersion: mod.version(),
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
catch { /* best-effort */ }
|
|
125
|
+
const rawBytes = entries.length * dimensions * 4; // f32 = 4 bytes
|
|
126
|
+
const quantizedBytes = entries.length * Math.ceil(dimensions / 8); // 1 bit per dim
|
|
127
|
+
const compressionRatio = rawBytes / Math.max(quantizedBytes, 1);
|
|
128
|
+
rabitqInitializing = false;
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
vectorCount: entries.length,
|
|
132
|
+
dimensions,
|
|
133
|
+
compressionRatio: Math.round(compressionRatio * 10) / 10,
|
|
134
|
+
buildTimeMs: Date.now() - startTime,
|
|
135
|
+
wasmVersion: mod.version(),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
rabitqInitializing = false;
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
vectorCount: 0,
|
|
143
|
+
dimensions: 0,
|
|
144
|
+
compressionRatio: 0,
|
|
145
|
+
buildTimeMs: Date.now() - startTime,
|
|
146
|
+
error: error instanceof Error ? error.message : String(error),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Search the RaBitQ index for candidate IDs.
|
|
152
|
+
* Returns null if index not built or unavailable.
|
|
153
|
+
* Caller is responsible for reranking with exact similarity.
|
|
154
|
+
*/
|
|
155
|
+
export async function searchRabitq(queryEmbedding, options) {
|
|
156
|
+
if (!rabitqState?.index)
|
|
157
|
+
return null;
|
|
158
|
+
try {
|
|
159
|
+
const query = new Float32Array(queryEmbedding);
|
|
160
|
+
if (query.length !== rabitqState.dimensions)
|
|
161
|
+
return null;
|
|
162
|
+
const k = options?.k ?? 10;
|
|
163
|
+
// Get more candidates than needed for namespace filtering + rerank
|
|
164
|
+
const expandedK = Math.min(k * 3, rabitqState.vectorCount);
|
|
165
|
+
const rawResults = rabitqState.index.search(query, expandedK);
|
|
166
|
+
const results = [];
|
|
167
|
+
for (const hit of rawResults) {
|
|
168
|
+
const pos = hit.id; // row index from build()
|
|
169
|
+
const entry = rabitqState.entries[pos];
|
|
170
|
+
if (!entry)
|
|
171
|
+
continue;
|
|
172
|
+
// Namespace filter
|
|
173
|
+
if (options?.namespace && options.namespace !== 'all' && entry.namespace !== options.namespace) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
results.push({
|
|
177
|
+
id: entry.id,
|
|
178
|
+
key: entry.key,
|
|
179
|
+
namespace: entry.namespace,
|
|
180
|
+
distance: hit.distance,
|
|
181
|
+
position: pos,
|
|
182
|
+
});
|
|
183
|
+
// Free WASM SearchResult to prevent leak
|
|
184
|
+
try {
|
|
185
|
+
hit.free();
|
|
186
|
+
}
|
|
187
|
+
catch { /* already freed */ }
|
|
188
|
+
if (results.length >= k)
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
// Free remaining SearchResults
|
|
192
|
+
for (const hit of rawResults) {
|
|
193
|
+
try {
|
|
194
|
+
hit.free();
|
|
195
|
+
}
|
|
196
|
+
catch { /* already freed or used */ }
|
|
197
|
+
}
|
|
198
|
+
return results;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Check if the RaBitQ index needs rebuilding.
|
|
206
|
+
*/
|
|
207
|
+
export async function shouldRebuildRabitq(currentEntryCount) {
|
|
208
|
+
if (!rabitqState)
|
|
209
|
+
return currentEntryCount >= 10; // Build if we have enough vectors
|
|
210
|
+
const drift = Math.abs(currentEntryCount - rabitqState.vectorCount) / Math.max(rabitqState.vectorCount, 1);
|
|
211
|
+
return drift > REBUILD_DRIFT_THRESHOLD;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get RaBitQ index status.
|
|
215
|
+
*/
|
|
216
|
+
export function getRabitqStatus() {
|
|
217
|
+
return {
|
|
218
|
+
available: rabitqState !== null,
|
|
219
|
+
initialized: rabitqState !== null,
|
|
220
|
+
vectorCount: rabitqState?.vectorCount ?? 0,
|
|
221
|
+
dimensions: rabitqState?.dimensions ?? 384,
|
|
222
|
+
builtAt: rabitqState?.builtAt ?? null,
|
|
223
|
+
compressionRatio: rabitqState ? Math.round((rabitqState.dimensions * 4) / Math.ceil(rabitqState.dimensions / 8) * 10) / 10 : 0,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=rabitq-index.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -97,23 +97,24 @@
|
|
|
97
97
|
"@claude-flow/mcp": "^3.0.0-alpha.8",
|
|
98
98
|
"@claude-flow/shared": "^3.0.0-alpha.7",
|
|
99
99
|
"@noble/ed25519": "^2.1.0",
|
|
100
|
+
"@ruvector/rabitq-wasm": "^0.1.0",
|
|
100
101
|
"semver": "^7.6.0"
|
|
101
102
|
},
|
|
102
103
|
"optionalDependencies": {
|
|
103
104
|
"@claude-flow/aidefence": "^3.0.2",
|
|
104
|
-
"@claude-flow/security": "^3.0.0-alpha.1",
|
|
105
105
|
"@claude-flow/codex": "^3.0.0-alpha.8",
|
|
106
106
|
"@claude-flow/embeddings": "^3.0.0-alpha.12",
|
|
107
107
|
"@claude-flow/guidance": "^3.0.0-alpha.1",
|
|
108
108
|
"@claude-flow/memory": "^3.0.0-alpha.12",
|
|
109
109
|
"@claude-flow/plugin-gastown-bridge": "^0.1.3",
|
|
110
|
+
"@claude-flow/security": "^3.0.0-alpha.1",
|
|
110
111
|
"@ruvector/attention": "^0.1.32",
|
|
111
112
|
"@ruvector/attention-darwin-arm64": "0.1.32",
|
|
113
|
+
"@ruvector/diskann": "^0.1.0",
|
|
112
114
|
"@ruvector/learning-wasm": "^0.1.29",
|
|
113
115
|
"@ruvector/router": "^0.1.30",
|
|
114
116
|
"@ruvector/ruvllm-wasm": "^2.0.2",
|
|
115
117
|
"@ruvector/rvagent-wasm": "^0.1.0",
|
|
116
|
-
"@ruvector/diskann": "^0.1.0",
|
|
117
118
|
"@ruvector/sona": "^0.1.5",
|
|
118
119
|
"agentdb": "^3.0.0-alpha.11",
|
|
119
120
|
"agentic-flow": "^3.0.0-alpha.1"
|