claude-flow 3.7.0-alpha.50 → 3.7.0-alpha.52

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.50",
3
+ "version": "3.7.0-alpha.52",
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",
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=__probe.d.ts.map
@@ -0,0 +1,5 @@
1
+ import * as e from '@claude-flow/embeddings';
2
+ const fn = e.mmrRerank;
3
+ const f2 = e.averagePairwiseSimilarity;
4
+ console.log(fn, f2);
5
+ //# sourceMappingURL=__probe.js.map
@@ -1279,5 +1279,290 @@ export const embeddingsTools = [
1279
1279
  };
1280
1280
  },
1281
1281
  },
1282
+ // ============================================================
1283
+ // ADR-121 Phase 11 — RRF ensemble retrieval (alpha.52 CLI)
1284
+ // ============================================================
1285
+ //
1286
+ // Question-reformulation pipelines produce N parallel result lists.
1287
+ // Reciprocal Rank Fusion (Cormack-Clarke-Büttcher 2009) fuses them
1288
+ // into a single ranking without needing score comparability across
1289
+ // lists. Composes `embeddings_search_text_batch` (N parallel
1290
+ // searches) with `reciprocalRankFusion` (rank-level merge).
1291
+ //
1292
+ // Standard production ensemble-RAG pattern. Pairs naturally with
1293
+ // LLM query rewriting upstream (expand "how does auth work?" into
1294
+ // {"how does authentication work?", "what's the login flow?",
1295
+ // "describe the OAuth2 handshake"} → batch search each →
1296
+ // RRF-fuse) — recovers more relevant docs than a single search.
1297
+ {
1298
+ name: 'embeddings_search_text_ensemble',
1299
+ 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.",
1300
+ category: 'embeddings',
1301
+ inputSchema: {
1302
+ type: 'object',
1303
+ properties: {
1304
+ texts: {
1305
+ type: 'array',
1306
+ items: { type: 'string' },
1307
+ description: 'Array of query text variants. Order preserved in per-list ranks.',
1308
+ },
1309
+ name: { type: 'string', description: 'AnnRouter handle name (set by embeddings_ann_router_build).' },
1310
+ k: { type: 'number', description: 'Number of fused results to return.' },
1311
+ perQueryK: {
1312
+ type: 'number',
1313
+ description: 'Top-k per query before fusion. Default 2*k. Larger = wider candidate pool, more compute.',
1314
+ },
1315
+ kRrf: {
1316
+ type: 'number',
1317
+ description: 'RRF smoothing constant. Default 60 (SIGIR 2009). Smaller = top-rank dominance.',
1318
+ },
1319
+ listWeights: {
1320
+ type: 'array',
1321
+ items: { type: 'number' },
1322
+ description: 'Per-query weights. Length must equal texts.length. Default = 1 each.',
1323
+ },
1324
+ },
1325
+ required: ['texts', 'name', 'k'],
1326
+ },
1327
+ handler: async (input) => {
1328
+ const config = loadConfig();
1329
+ if (!config) {
1330
+ return { success: false, error: 'Embeddings not initialized. Run embeddings_init first.' };
1331
+ }
1332
+ const texts = input.texts;
1333
+ const name = input.name;
1334
+ const k = input.k;
1335
+ const perQueryK = typeof input.perQueryK === 'number' && input.perQueryK >= 1
1336
+ ? input.perQueryK
1337
+ : Math.max(k * 2, k);
1338
+ const kRrf = typeof input.kRrf === 'number' && input.kRrf > 0 ? input.kRrf : 60;
1339
+ const listWeights = Array.isArray(input.listWeights) ? input.listWeights : undefined;
1340
+ if (!Array.isArray(texts) || texts.length === 0) {
1341
+ return { success: false, error: 'texts must be a non-empty array' };
1342
+ }
1343
+ if (!Number.isInteger(k) || k < 1) {
1344
+ return { success: false, error: 'k must be a positive integer' };
1345
+ }
1346
+ for (let i = 0; i < texts.length; i++) {
1347
+ if (typeof texts[i] !== 'string') {
1348
+ return { success: false, error: `texts[${i}] is not a string` };
1349
+ }
1350
+ }
1351
+ if (listWeights && listWeights.length !== texts.length) {
1352
+ return {
1353
+ success: false,
1354
+ error: `listWeights.length (${listWeights.length}) must match texts.length (${texts.length})`,
1355
+ };
1356
+ }
1357
+ const { getAnnRouterRegistry } = await import('../memory/ann-router-registry.js');
1358
+ const registry = getAnnRouterRegistry();
1359
+ // Stage 1 — embed all queries in parallel.
1360
+ const embedT0 = Date.now();
1361
+ let embeddings;
1362
+ try {
1363
+ embeddings = await Promise.all(texts.map(t => generateRealEmbedding(t, config.dimension)));
1364
+ }
1365
+ catch (err) {
1366
+ return { success: false, name, error: `batch embed failed: ${err instanceof Error ? err.message : String(err)}` };
1367
+ }
1368
+ const embeddingMs = Date.now() - embedT0;
1369
+ // Stage 2 — search each variant in parallel. Per-query errors
1370
+ // become empty result lists (the variant just doesn't contribute
1371
+ // to the fusion) rather than aborting the ensemble.
1372
+ const searchT0 = Date.now();
1373
+ const perQueryResults = await Promise.all(embeddings.map(async (emb, i) => {
1374
+ try {
1375
+ const hits = await registry.search(name, new Float32Array(emb), perQueryK);
1376
+ return { index: i, text: texts[i], hits, success: true };
1377
+ }
1378
+ catch (err) {
1379
+ return {
1380
+ index: i,
1381
+ text: texts[i],
1382
+ hits: [],
1383
+ success: false,
1384
+ error: err instanceof Error ? err.message : String(err),
1385
+ };
1386
+ }
1387
+ }));
1388
+ const searchMs = Date.now() - searchT0;
1389
+ // Stage 3 — RRF fusion across the per-query lists.
1390
+ const { reciprocalRankFusion } = await import('@claude-flow/embeddings/rrf');
1391
+ const fuseT0 = Date.now();
1392
+ const lists = perQueryResults.map(r => r.hits.map(h => ({ id: h.id, payload: { score: h.score } })));
1393
+ const fused = reciprocalRankFusion(lists, { k, kRrf, listWeights });
1394
+ const fuseMs = Date.now() - fuseT0;
1395
+ const successCount = perQueryResults.filter(r => r.success).length;
1396
+ return {
1397
+ success: successCount === perQueryResults.length,
1398
+ name,
1399
+ k,
1400
+ queryCount: texts.length,
1401
+ perQueryK,
1402
+ kRrf,
1403
+ listWeights: listWeights ?? null,
1404
+ successCount,
1405
+ failureCount: perQueryResults.length - successCount,
1406
+ hits: fused,
1407
+ perQuery: perQueryResults.map(r => ({
1408
+ index: r.index,
1409
+ text: r.text,
1410
+ success: r.success,
1411
+ hitCount: r.hits.length,
1412
+ error: r.success ? undefined : r.error,
1413
+ })),
1414
+ latency: {
1415
+ embeddingMs,
1416
+ searchMs,
1417
+ fuseMs,
1418
+ totalMs: embeddingMs + searchMs + fuseMs,
1419
+ avgPerQueryMs: Math.round(((embeddingMs + searchMs) / texts.length) * 100) / 100,
1420
+ },
1421
+ embeddingDimension: embeddings[0]?.length,
1422
+ };
1423
+ },
1424
+ },
1425
+ // ============================================================
1426
+ // ADR-121 Phase 10 — MMR diversity rerank (alpha.51 CLI)
1427
+ // ============================================================
1428
+ //
1429
+ // Plain top-k often returns near-duplicate chunks. MMR picks a
1430
+ // diversified top-k by trading off relevance against redundancy
1431
+ // (Carbonell & Goldstein 1998). Fetches `fetchMultiplier * k`
1432
+ // candidates from AnnRouter, then reranks to k via mmrRerank.
1433
+ //
1434
+ // Pairs with embeddings_search_text (relevance only). Same caller
1435
+ // contract — text + handle name + k — plus optional `lambda` and
1436
+ // `fetchMultiplier`. Returns diversity stats so callers can
1437
+ // confirm the rerank actually spread the result.
1438
+ {
1439
+ name: 'embeddings_search_text_diverse',
1440
+ description: "Embed a text query, fetch a wider candidate pool from a named AnnRouter handle, and rerank with MMR (Maximal Marginal Relevance) to return a diverse top-k. Use when plain top-k tends to return near-duplicates (e.g. corpora with many paraphrased chunks). λ controls relevance/diversity tradeoff: 1.0 = same as embeddings_search_text, 0.5 = balanced (default), 0.0 = pure diversity. fetchMultiplier controls how many candidates to consider before reranking (default 5×k). Returns hits + diversification stats (averagePairwiseSimilarity — lower is more diverse). For plain (non-diversified) RAG use embeddings_search_text.",
1441
+ category: 'embeddings',
1442
+ inputSchema: {
1443
+ type: 'object',
1444
+ properties: {
1445
+ text: { type: 'string', description: 'Query text. Will be embedded inline.' },
1446
+ name: { type: 'string', description: 'AnnRouter handle name (set by embeddings_ann_router_build).' },
1447
+ k: { type: 'number', description: 'Number of diverse nearest neighbors to return.' },
1448
+ lambda: {
1449
+ type: 'number',
1450
+ description: 'Relevance/diversity tradeoff in [0,1]. 1=pure relevance, 0=pure diversity. Default 0.5.',
1451
+ },
1452
+ fetchMultiplier: {
1453
+ type: 'number',
1454
+ description: 'Candidate pool size = fetchMultiplier * k. Larger = more candidates to diversify from, more compute. Default 5.',
1455
+ },
1456
+ },
1457
+ required: ['text', 'name', 'k'],
1458
+ },
1459
+ handler: async (input) => {
1460
+ const config = loadConfig();
1461
+ if (!config) {
1462
+ return { success: false, error: 'Embeddings not initialized. Run embeddings_init first.' };
1463
+ }
1464
+ const text = input.text;
1465
+ const name = input.name;
1466
+ const k = input.k;
1467
+ const lambda = typeof input.lambda === 'number' ? input.lambda : 0.5;
1468
+ const fetchMultiplier = typeof input.fetchMultiplier === 'number' && input.fetchMultiplier >= 1
1469
+ ? input.fetchMultiplier
1470
+ : 5;
1471
+ const tv = validateText(text, 'text');
1472
+ if (!tv.valid)
1473
+ return { success: false, error: tv.error };
1474
+ if (!Number.isInteger(k) || k < 1) {
1475
+ return { success: false, error: 'k must be a positive integer' };
1476
+ }
1477
+ // Stage 1 — embed the query.
1478
+ const embedT0 = Date.now();
1479
+ let embedding;
1480
+ try {
1481
+ embedding = await generateRealEmbedding(text, config.dimension);
1482
+ }
1483
+ catch (err) {
1484
+ return { success: false, error: `embed failed: ${err instanceof Error ? err.message : String(err)}` };
1485
+ }
1486
+ const embeddingMs = Date.now() - embedT0;
1487
+ const queryVec = new Float32Array(embedding);
1488
+ // Stage 2 — fetch a wider pool of candidates from the router.
1489
+ const { getAnnRouterRegistry } = await import('../memory/ann-router-registry.js');
1490
+ const registry = getAnnRouterRegistry();
1491
+ const fetchK = Math.max(k, Math.floor(k * fetchMultiplier));
1492
+ const searchT0 = Date.now();
1493
+ let candidatesRaw;
1494
+ try {
1495
+ candidatesRaw = await registry.search(name, queryVec, fetchK);
1496
+ }
1497
+ catch (err) {
1498
+ return {
1499
+ success: false,
1500
+ name,
1501
+ error: err instanceof Error ? err.message : String(err),
1502
+ latency: { embeddingMs, searchMs: 0, rerankMs: 0 },
1503
+ };
1504
+ }
1505
+ const searchMs = Date.now() - searchT0;
1506
+ // Filter to candidates that have a vector (MMR needs it).
1507
+ // Routers that don't surface vectors (rare) degrade to
1508
+ // plain top-k for safety rather than throwing.
1509
+ const candidatesWithVec = candidatesRaw.filter(c => c.vector != null);
1510
+ if (candidatesWithVec.length === 0) {
1511
+ return {
1512
+ success: true,
1513
+ name,
1514
+ k,
1515
+ hits: candidatesRaw.slice(0, k),
1516
+ mmr: { applied: false, reason: 'no candidate vectors available — degraded to plain top-k' },
1517
+ latency: { embeddingMs, searchMs, rerankMs: 0, totalMs: embeddingMs + searchMs },
1518
+ };
1519
+ }
1520
+ // Stage 3 — MMR rerank.
1521
+ // Sub-path import bypasses the index barrel — TS resolves the
1522
+ // mmr.d.ts directly via the './*' export condition, which
1523
+ // sidesteps a stale-cache issue with the aggregate index.d.ts.
1524
+ const { mmrRerank, averagePairwiseSimilarity } = await import('@claude-flow/embeddings/mmr');
1525
+ const rerankT0 = Date.now();
1526
+ const picked = mmrRerank(candidatesWithVec.map(c => ({
1527
+ id: c.id,
1528
+ vector: c.vector,
1529
+ score: c.score,
1530
+ payload: c.payload,
1531
+ })), queryVec, { k, lambda });
1532
+ const rerankMs = Date.now() - rerankT0;
1533
+ const avgPairSim = averagePairwiseSimilarity(picked);
1534
+ // Strip vectors from the response to keep stdout sane.
1535
+ // Callers wanting the vectors can re-fetch via search.
1536
+ const hits = picked.map(p => ({
1537
+ id: p.id,
1538
+ score: p.relevance,
1539
+ mmrScore: p.mmrScore,
1540
+ relevance: p.relevance,
1541
+ redundancy: p.redundancy,
1542
+ pickOrder: p.pickOrder,
1543
+ payload: p.payload,
1544
+ }));
1545
+ return {
1546
+ success: true,
1547
+ name,
1548
+ k,
1549
+ hits,
1550
+ mmr: {
1551
+ applied: true,
1552
+ lambda,
1553
+ fetchMultiplier,
1554
+ candidatesConsidered: candidatesWithVec.length,
1555
+ averagePairwiseSimilarity: avgPairSim,
1556
+ },
1557
+ latency: {
1558
+ embeddingMs,
1559
+ searchMs,
1560
+ rerankMs,
1561
+ totalMs: embeddingMs + searchMs + rerankMs,
1562
+ },
1563
+ embeddingDimension: embedding.length,
1564
+ };
1565
+ },
1566
+ },
1282
1567
  ];
1283
1568
  //# sourceMappingURL=embeddings-tools.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.7.0-alpha.50",
3
+ "version": "3.7.0-alpha.52",
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.26",
107
+ "@claude-flow/embeddings": "^3.0.0-alpha.32",
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.26",
114
+ "@claude-flow/embeddings": "^3.0.0-alpha.32",
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",