claude-flow 3.7.0-alpha.51 → 3.7.0-alpha.53
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.7.0-alpha.
|
|
3
|
+
"version": "3.7.0-alpha.53",
|
|
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",
|
|
@@ -1280,6 +1280,282 @@ export const embeddingsTools = [
|
|
|
1280
1280
|
},
|
|
1281
1281
|
},
|
|
1282
1282
|
// ============================================================
|
|
1283
|
+
// ADR-121 Phase 12 — HyDE embedding-level fusion (alpha.53 CLI)
|
|
1284
|
+
// ============================================================
|
|
1285
|
+
//
|
|
1286
|
+
// HyDE (Gao, Ma, Lin, Callan 2022 — "Precise Zero-Shot Dense
|
|
1287
|
+
// Retrieval without Relevance Labels"): question embeddings live in
|
|
1288
|
+
// "question space" while documents embed into "answer space", so
|
|
1289
|
+
// cosine search systematically underweights relevant docs. The
|
|
1290
|
+
// standard fix: have an LLM generate N hypothetical answers, embed
|
|
1291
|
+
// each, AVERAGE the embeddings into a single query vector, search
|
|
1292
|
+
// once with that.
|
|
1293
|
+
//
|
|
1294
|
+
// Distinct from `embeddings_search_text_ensemble` (Phase 11):
|
|
1295
|
+
// - HyDE fuses at the EMBEDDING level (1 search after average).
|
|
1296
|
+
// Cheaper, finds the centroid hit, interpolates between
|
|
1297
|
+
// hypothetical answers.
|
|
1298
|
+
// - RRF fuses at the RANK level (N searches then merge ranks).
|
|
1299
|
+
// More expensive, preserves intent boundaries between variants.
|
|
1300
|
+
//
|
|
1301
|
+
// Both are useful. Production systems often combine them — HyDE
|
|
1302
|
+
// inside one ranked list, RRF across multiple lists.
|
|
1303
|
+
{
|
|
1304
|
+
name: 'embeddings_search_text_hyde',
|
|
1305
|
+
description: "Embed N hypothetical-answer texts, AVERAGE their embeddings into a single query vector, and search a named AnnRouter handle once. Implements the HyDE recipe (Gao et al. 2022) for zero-shot dense retrieval — the LLM-generated hypothetical answers live in the same answer-space as the corpus, so the averaged vector lands near the true relevant docs. Distinct from embeddings_search_text_ensemble (which fuses at the rank level via RRF — more expensive, preserves intent boundaries); HyDE fuses at the embedding level (cheaper, one search, finds the centroid hit). Optional `weights` per text (e.g. weight the user's original question 0.5× and LLM answers 1.0× each — the paper's recommended recipe). Returns hits + averaged-vector metadata (unitNorm assertion + contributing-text count) for transparency.",
|
|
1306
|
+
category: 'embeddings',
|
|
1307
|
+
inputSchema: {
|
|
1308
|
+
type: 'object',
|
|
1309
|
+
properties: {
|
|
1310
|
+
texts: {
|
|
1311
|
+
type: 'array',
|
|
1312
|
+
items: { type: 'string' },
|
|
1313
|
+
description: 'Array of hypothetical-answer texts (LLM-generated) to fuse. The caller is responsible for generating these — typically via a few-shot prompt to an LLM asking it to answer the user question.',
|
|
1314
|
+
},
|
|
1315
|
+
name: { type: 'string', description: 'AnnRouter handle name (set by embeddings_ann_router_build).' },
|
|
1316
|
+
k: { type: 'number', description: 'Number of nearest neighbors to return.' },
|
|
1317
|
+
weights: {
|
|
1318
|
+
type: 'array',
|
|
1319
|
+
items: { type: 'number' },
|
|
1320
|
+
description: 'Per-text weights for the embedding average. Length must equal texts.length. Default uniform.',
|
|
1321
|
+
},
|
|
1322
|
+
},
|
|
1323
|
+
required: ['texts', 'name', 'k'],
|
|
1324
|
+
},
|
|
1325
|
+
handler: async (input) => {
|
|
1326
|
+
const config = loadConfig();
|
|
1327
|
+
if (!config) {
|
|
1328
|
+
return { success: false, error: 'Embeddings not initialized. Run embeddings_init first.' };
|
|
1329
|
+
}
|
|
1330
|
+
const texts = input.texts;
|
|
1331
|
+
const name = input.name;
|
|
1332
|
+
const k = input.k;
|
|
1333
|
+
const weights = Array.isArray(input.weights) ? input.weights : undefined;
|
|
1334
|
+
if (!Array.isArray(texts) || texts.length === 0) {
|
|
1335
|
+
return { success: false, error: 'texts must be a non-empty array' };
|
|
1336
|
+
}
|
|
1337
|
+
if (!Number.isInteger(k) || k < 1) {
|
|
1338
|
+
return { success: false, error: 'k must be a positive integer' };
|
|
1339
|
+
}
|
|
1340
|
+
for (let i = 0; i < texts.length; i++) {
|
|
1341
|
+
if (typeof texts[i] !== 'string') {
|
|
1342
|
+
return { success: false, error: `texts[${i}] is not a string` };
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (weights && weights.length !== texts.length) {
|
|
1346
|
+
return {
|
|
1347
|
+
success: false,
|
|
1348
|
+
error: `weights.length (${weights.length}) must match texts.length (${texts.length})`,
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
if (weights) {
|
|
1352
|
+
for (let i = 0; i < weights.length; i++) {
|
|
1353
|
+
if (weights[i] < 0) {
|
|
1354
|
+
return { success: false, error: `weights[${i}] is negative` };
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
const { getAnnRouterRegistry } = await import('../memory/ann-router-registry.js');
|
|
1359
|
+
const registry = getAnnRouterRegistry();
|
|
1360
|
+
// Stage 1 — embed each hypothetical text in parallel.
|
|
1361
|
+
const embedT0 = Date.now();
|
|
1362
|
+
let embeddings;
|
|
1363
|
+
try {
|
|
1364
|
+
embeddings = await Promise.all(texts.map(t => generateRealEmbedding(t, config.dimension)));
|
|
1365
|
+
}
|
|
1366
|
+
catch (err) {
|
|
1367
|
+
return { success: false, name, error: `batch embed failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
1368
|
+
}
|
|
1369
|
+
const embeddingMs = Date.now() - embedT0;
|
|
1370
|
+
// Stage 2 — average the embeddings (HyDE fusion).
|
|
1371
|
+
const { averageEmbeddings, isUnitNorm } = await import('@claude-flow/embeddings/embedding-fusion');
|
|
1372
|
+
const fuseT0 = Date.now();
|
|
1373
|
+
const avgVec = averageEmbeddings(embeddings, {
|
|
1374
|
+
weights,
|
|
1375
|
+
normalizeInputs: true,
|
|
1376
|
+
normalizeOutput: true,
|
|
1377
|
+
});
|
|
1378
|
+
const fuseMs = Date.now() - fuseT0;
|
|
1379
|
+
const fusedUnit = isUnitNorm(avgVec);
|
|
1380
|
+
// Stage 3 — single search with the averaged vector.
|
|
1381
|
+
const searchT0 = Date.now();
|
|
1382
|
+
let hits;
|
|
1383
|
+
try {
|
|
1384
|
+
hits = await registry.search(name, avgVec, k);
|
|
1385
|
+
}
|
|
1386
|
+
catch (err) {
|
|
1387
|
+
return {
|
|
1388
|
+
success: false,
|
|
1389
|
+
name,
|
|
1390
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1391
|
+
latency: { embeddingMs, fuseMs, searchMs: 0 },
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
const searchMs = Date.now() - searchT0;
|
|
1395
|
+
return {
|
|
1396
|
+
success: true,
|
|
1397
|
+
name,
|
|
1398
|
+
k,
|
|
1399
|
+
hits,
|
|
1400
|
+
hyde: {
|
|
1401
|
+
textsFused: texts.length,
|
|
1402
|
+
weights: weights ?? null,
|
|
1403
|
+
averagedVectorUnitNorm: fusedUnit,
|
|
1404
|
+
dimension: avgVec.length,
|
|
1405
|
+
},
|
|
1406
|
+
latency: {
|
|
1407
|
+
embeddingMs,
|
|
1408
|
+
fuseMs,
|
|
1409
|
+
searchMs,
|
|
1410
|
+
totalMs: embeddingMs + fuseMs + searchMs,
|
|
1411
|
+
},
|
|
1412
|
+
};
|
|
1413
|
+
},
|
|
1414
|
+
},
|
|
1415
|
+
// ============================================================
|
|
1416
|
+
// ADR-121 Phase 11 — RRF ensemble retrieval (alpha.52 CLI)
|
|
1417
|
+
// ============================================================
|
|
1418
|
+
//
|
|
1419
|
+
// Question-reformulation pipelines produce N parallel result lists.
|
|
1420
|
+
// Reciprocal Rank Fusion (Cormack-Clarke-Büttcher 2009) fuses them
|
|
1421
|
+
// into a single ranking without needing score comparability across
|
|
1422
|
+
// lists. Composes `embeddings_search_text_batch` (N parallel
|
|
1423
|
+
// searches) with `reciprocalRankFusion` (rank-level merge).
|
|
1424
|
+
//
|
|
1425
|
+
// Standard production ensemble-RAG pattern. Pairs naturally with
|
|
1426
|
+
// LLM query rewriting upstream (expand "how does auth work?" into
|
|
1427
|
+
// {"how does authentication work?", "what's the login flow?",
|
|
1428
|
+
// "describe the OAuth2 handshake"} → batch search each →
|
|
1429
|
+
// RRF-fuse) — recovers more relevant docs than a single search.
|
|
1430
|
+
{
|
|
1431
|
+
name: 'embeddings_search_text_ensemble',
|
|
1432
|
+
description: "Embed N text query variants, search a named AnnRouter handle for each in parallel, then RRF-fuse (Reciprocal Rank Fusion, Cormack-Clarke-Büttcher 2009) the N hit-lists into a single merged top-k ranking. Standard production shape for question-reformulation RAG: agents expand one user question into N variants, retrieve top-k for each, get a single fused list back — items appearing high in MORE variants outrank items appearing high in only one. Returns fused hits with per-list ranks for transparency + aggregate latency. λ-equivalent here is `kRrf` (default 60 per SIGIR 2009). Per-list `listWeights` available for biased ensemble (e.g. weight the original-query list 2× over reformulations). For non-fused multi-query results use embeddings_search_text_batch; for single-query diverse retrieval use embeddings_search_text_diverse.",
|
|
1433
|
+
category: 'embeddings',
|
|
1434
|
+
inputSchema: {
|
|
1435
|
+
type: 'object',
|
|
1436
|
+
properties: {
|
|
1437
|
+
texts: {
|
|
1438
|
+
type: 'array',
|
|
1439
|
+
items: { type: 'string' },
|
|
1440
|
+
description: 'Array of query text variants. Order preserved in per-list ranks.',
|
|
1441
|
+
},
|
|
1442
|
+
name: { type: 'string', description: 'AnnRouter handle name (set by embeddings_ann_router_build).' },
|
|
1443
|
+
k: { type: 'number', description: 'Number of fused results to return.' },
|
|
1444
|
+
perQueryK: {
|
|
1445
|
+
type: 'number',
|
|
1446
|
+
description: 'Top-k per query before fusion. Default 2*k. Larger = wider candidate pool, more compute.',
|
|
1447
|
+
},
|
|
1448
|
+
kRrf: {
|
|
1449
|
+
type: 'number',
|
|
1450
|
+
description: 'RRF smoothing constant. Default 60 (SIGIR 2009). Smaller = top-rank dominance.',
|
|
1451
|
+
},
|
|
1452
|
+
listWeights: {
|
|
1453
|
+
type: 'array',
|
|
1454
|
+
items: { type: 'number' },
|
|
1455
|
+
description: 'Per-query weights. Length must equal texts.length. Default = 1 each.',
|
|
1456
|
+
},
|
|
1457
|
+
},
|
|
1458
|
+
required: ['texts', 'name', 'k'],
|
|
1459
|
+
},
|
|
1460
|
+
handler: async (input) => {
|
|
1461
|
+
const config = loadConfig();
|
|
1462
|
+
if (!config) {
|
|
1463
|
+
return { success: false, error: 'Embeddings not initialized. Run embeddings_init first.' };
|
|
1464
|
+
}
|
|
1465
|
+
const texts = input.texts;
|
|
1466
|
+
const name = input.name;
|
|
1467
|
+
const k = input.k;
|
|
1468
|
+
const perQueryK = typeof input.perQueryK === 'number' && input.perQueryK >= 1
|
|
1469
|
+
? input.perQueryK
|
|
1470
|
+
: Math.max(k * 2, k);
|
|
1471
|
+
const kRrf = typeof input.kRrf === 'number' && input.kRrf > 0 ? input.kRrf : 60;
|
|
1472
|
+
const listWeights = Array.isArray(input.listWeights) ? input.listWeights : undefined;
|
|
1473
|
+
if (!Array.isArray(texts) || texts.length === 0) {
|
|
1474
|
+
return { success: false, error: 'texts must be a non-empty array' };
|
|
1475
|
+
}
|
|
1476
|
+
if (!Number.isInteger(k) || k < 1) {
|
|
1477
|
+
return { success: false, error: 'k must be a positive integer' };
|
|
1478
|
+
}
|
|
1479
|
+
for (let i = 0; i < texts.length; i++) {
|
|
1480
|
+
if (typeof texts[i] !== 'string') {
|
|
1481
|
+
return { success: false, error: `texts[${i}] is not a string` };
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
if (listWeights && listWeights.length !== texts.length) {
|
|
1485
|
+
return {
|
|
1486
|
+
success: false,
|
|
1487
|
+
error: `listWeights.length (${listWeights.length}) must match texts.length (${texts.length})`,
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
const { getAnnRouterRegistry } = await import('../memory/ann-router-registry.js');
|
|
1491
|
+
const registry = getAnnRouterRegistry();
|
|
1492
|
+
// Stage 1 — embed all queries in parallel.
|
|
1493
|
+
const embedT0 = Date.now();
|
|
1494
|
+
let embeddings;
|
|
1495
|
+
try {
|
|
1496
|
+
embeddings = await Promise.all(texts.map(t => generateRealEmbedding(t, config.dimension)));
|
|
1497
|
+
}
|
|
1498
|
+
catch (err) {
|
|
1499
|
+
return { success: false, name, error: `batch embed failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
1500
|
+
}
|
|
1501
|
+
const embeddingMs = Date.now() - embedT0;
|
|
1502
|
+
// Stage 2 — search each variant in parallel. Per-query errors
|
|
1503
|
+
// become empty result lists (the variant just doesn't contribute
|
|
1504
|
+
// to the fusion) rather than aborting the ensemble.
|
|
1505
|
+
const searchT0 = Date.now();
|
|
1506
|
+
const perQueryResults = await Promise.all(embeddings.map(async (emb, i) => {
|
|
1507
|
+
try {
|
|
1508
|
+
const hits = await registry.search(name, new Float32Array(emb), perQueryK);
|
|
1509
|
+
return { index: i, text: texts[i], hits, success: true };
|
|
1510
|
+
}
|
|
1511
|
+
catch (err) {
|
|
1512
|
+
return {
|
|
1513
|
+
index: i,
|
|
1514
|
+
text: texts[i],
|
|
1515
|
+
hits: [],
|
|
1516
|
+
success: false,
|
|
1517
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
}));
|
|
1521
|
+
const searchMs = Date.now() - searchT0;
|
|
1522
|
+
// Stage 3 — RRF fusion across the per-query lists.
|
|
1523
|
+
const { reciprocalRankFusion } = await import('@claude-flow/embeddings/rrf');
|
|
1524
|
+
const fuseT0 = Date.now();
|
|
1525
|
+
const lists = perQueryResults.map(r => r.hits.map(h => ({ id: h.id, payload: { score: h.score } })));
|
|
1526
|
+
const fused = reciprocalRankFusion(lists, { k, kRrf, listWeights });
|
|
1527
|
+
const fuseMs = Date.now() - fuseT0;
|
|
1528
|
+
const successCount = perQueryResults.filter(r => r.success).length;
|
|
1529
|
+
return {
|
|
1530
|
+
success: successCount === perQueryResults.length,
|
|
1531
|
+
name,
|
|
1532
|
+
k,
|
|
1533
|
+
queryCount: texts.length,
|
|
1534
|
+
perQueryK,
|
|
1535
|
+
kRrf,
|
|
1536
|
+
listWeights: listWeights ?? null,
|
|
1537
|
+
successCount,
|
|
1538
|
+
failureCount: perQueryResults.length - successCount,
|
|
1539
|
+
hits: fused,
|
|
1540
|
+
perQuery: perQueryResults.map(r => ({
|
|
1541
|
+
index: r.index,
|
|
1542
|
+
text: r.text,
|
|
1543
|
+
success: r.success,
|
|
1544
|
+
hitCount: r.hits.length,
|
|
1545
|
+
error: r.success ? undefined : r.error,
|
|
1546
|
+
})),
|
|
1547
|
+
latency: {
|
|
1548
|
+
embeddingMs,
|
|
1549
|
+
searchMs,
|
|
1550
|
+
fuseMs,
|
|
1551
|
+
totalMs: embeddingMs + searchMs + fuseMs,
|
|
1552
|
+
avgPerQueryMs: Math.round(((embeddingMs + searchMs) / texts.length) * 100) / 100,
|
|
1553
|
+
},
|
|
1554
|
+
embeddingDimension: embeddings[0]?.length,
|
|
1555
|
+
};
|
|
1556
|
+
},
|
|
1557
|
+
},
|
|
1558
|
+
// ============================================================
|
|
1283
1559
|
// ADR-121 Phase 10 — MMR diversity rerank (alpha.51 CLI)
|
|
1284
1560
|
// ============================================================
|
|
1285
1561
|
//
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.7.0-alpha.
|
|
3
|
+
"version": "3.7.0-alpha.53",
|
|
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",
|
|
@@ -104,14 +104,14 @@
|
|
|
104
104
|
"semver": "^7.6.0",
|
|
105
105
|
"yaml": "^2.8.0",
|
|
106
106
|
"@claude-flow/memory": "^3.0.0-alpha.17",
|
|
107
|
-
"@claude-flow/embeddings": "^3.0.0-alpha.
|
|
107
|
+
"@claude-flow/embeddings": "^3.0.0-alpha.33",
|
|
108
108
|
"@claude-flow/security": "^3.0.0-alpha.8",
|
|
109
109
|
"@claude-flow/swarm": "^3.0.0-alpha.8"
|
|
110
110
|
},
|
|
111
111
|
"optionalDependencies": {
|
|
112
112
|
"@claude-flow/aidefence": "^3.0.2",
|
|
113
113
|
"@claude-flow/codex": "^3.0.0-alpha.8",
|
|
114
|
-
"@claude-flow/embeddings": "^3.0.0-alpha.
|
|
114
|
+
"@claude-flow/embeddings": "^3.0.0-alpha.33",
|
|
115
115
|
"@claude-flow/guidance": "^3.0.0-alpha.1",
|
|
116
116
|
"@claude-flow/memory": "^3.0.0-alpha.17",
|
|
117
117
|
"@claude-flow/plugin-gastown-bridge": "^0.1.3",
|