@claude-flow/cli 3.10.25 → 3.10.26
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/cli",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.26",
|
|
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",
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// beir-bootstrap-significance.mjs — paired bootstrap significance test for
|
|
3
|
+
// BEIR retrieval results (ADR-086).
|
|
4
|
+
//
|
|
5
|
+
// Given a run JSON with per-query nDCG@10 and a baseline mean nDCG@10,
|
|
6
|
+
// estimates whether our result is significantly above the baseline.
|
|
7
|
+
//
|
|
8
|
+
// Method: paired bootstrap — resample N=10000 with replacement from our
|
|
9
|
+
// per-query nDCG scores, compute mean each time, take 2.5%-97.5% percentile
|
|
10
|
+
// for 95% CI. Compare baseline to our CI: if baseline < CI lower bound,
|
|
11
|
+
// the difference is significant at p < 0.05.
|
|
12
|
+
//
|
|
13
|
+
// For a paired test against ANOTHER per-query run (e.g. our pure-BM25 vs
|
|
14
|
+
// our BGE-base), resample paired DIFFERENCES instead. Pass --paired
|
|
15
|
+
// path/to/baseline-run.json.
|
|
16
|
+
//
|
|
17
|
+
// Usage:
|
|
18
|
+
// node scripts/beir-bootstrap-significance.mjs <run-json-path>
|
|
19
|
+
// node scripts/beir-bootstrap-significance.mjs <ours> --paired <baseline>
|
|
20
|
+
//
|
|
21
|
+
// Example:
|
|
22
|
+
// node scripts/beir-bootstrap-significance.mjs \
|
|
23
|
+
// docs/benchmarks/runs/beir-nfcorpus-bge-latest.json
|
|
24
|
+
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
import { performance } from 'node:perf_hooks';
|
|
27
|
+
|
|
28
|
+
const args = process.argv.slice(2);
|
|
29
|
+
if (args.length === 0) {
|
|
30
|
+
console.error('Usage: node beir-bootstrap-significance.mjs <run-json> [--paired <baseline-run-json>]');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
const RUN_PATH = args[0];
|
|
34
|
+
const PAIRED_IDX = args.indexOf('--paired');
|
|
35
|
+
const PAIRED_PATH = PAIRED_IDX > 0 ? args[PAIRED_IDX + 1] : null;
|
|
36
|
+
const ITERATIONS = Number(process.env.BOOTSTRAP_ITERATIONS) || 10000;
|
|
37
|
+
|
|
38
|
+
// Deterministic PRNG (Mulberry32) — so re-runs are reproducible.
|
|
39
|
+
const SEED = Number(process.env.BOOTSTRAP_SEED) || 42;
|
|
40
|
+
function mulberry32(a) {
|
|
41
|
+
return function () {
|
|
42
|
+
a |= 0; a = (a + 0x6D2B79F5) | 0;
|
|
43
|
+
let t = a;
|
|
44
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
45
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
46
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mean(arr) { let s = 0; for (const v of arr) s += v; return s / arr.length; }
|
|
51
|
+
|
|
52
|
+
function percentile(sortedArr, p) {
|
|
53
|
+
const idx = Math.max(0, Math.min(sortedArr.length - 1, Math.floor(p * sortedArr.length)));
|
|
54
|
+
return sortedArr[idx];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function pointEstimate(scores) {
|
|
58
|
+
return {
|
|
59
|
+
mean: mean(scores),
|
|
60
|
+
n: scores.length,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function bootstrapCI(scores, iterations, rng, percentiles = [0.025, 0.975]) {
|
|
65
|
+
const means = new Float64Array(iterations);
|
|
66
|
+
const n = scores.length;
|
|
67
|
+
for (let i = 0; i < iterations; i++) {
|
|
68
|
+
let s = 0;
|
|
69
|
+
for (let j = 0; j < n; j++) s += scores[Math.floor(rng() * n)];
|
|
70
|
+
means[i] = s / n;
|
|
71
|
+
}
|
|
72
|
+
const sorted = Array.from(means).sort((a, b) => a - b);
|
|
73
|
+
return {
|
|
74
|
+
point: mean(scores),
|
|
75
|
+
lower: percentile(sorted, percentiles[0]),
|
|
76
|
+
upper: percentile(sorted, percentiles[1]),
|
|
77
|
+
p_value_vs_baseline_zero: null,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Paired bootstrap: compare our per-query scores to a baseline's per-query
|
|
82
|
+
// scores. Resample (idx) and average paired differences. The 95% CI on the
|
|
83
|
+
// mean difference tells us if it's significantly non-zero.
|
|
84
|
+
function pairedBootstrap(oursByQid, baselineByQid, iterations, rng) {
|
|
85
|
+
const sharedIds = [...oursByQid.keys()].filter((id) => baselineByQid.has(id));
|
|
86
|
+
const diffs = sharedIds.map((id) => oursByQid.get(id) - baselineByQid.get(id));
|
|
87
|
+
const n = diffs.length;
|
|
88
|
+
const means = new Float64Array(iterations);
|
|
89
|
+
for (let i = 0; i < iterations; i++) {
|
|
90
|
+
let s = 0;
|
|
91
|
+
for (let j = 0; j < n; j++) s += diffs[Math.floor(rng() * n)];
|
|
92
|
+
means[i] = s / n;
|
|
93
|
+
}
|
|
94
|
+
const sorted = Array.from(means).sort((a, b) => a - b);
|
|
95
|
+
// One-sided p ≈ fraction of resamples where mean diff ≤ 0
|
|
96
|
+
let negCount = 0;
|
|
97
|
+
for (const m of means) if (m <= 0) negCount++;
|
|
98
|
+
return {
|
|
99
|
+
n_paired: n,
|
|
100
|
+
point_diff: mean(diffs),
|
|
101
|
+
lower_95: percentile(sorted, 0.025),
|
|
102
|
+
upper_95: percentile(sorted, 0.975),
|
|
103
|
+
p_one_sided: negCount / iterations,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function loadRun(path) {
|
|
108
|
+
const data = JSON.parse(readFileSync(path, 'utf-8'));
|
|
109
|
+
if (!data.perQuery || !Array.isArray(data.perQuery)) {
|
|
110
|
+
console.error(`Run JSON at ${path} has no perQuery array — re-run the bench with the updated harness.`);
|
|
111
|
+
process.exit(2);
|
|
112
|
+
}
|
|
113
|
+
return data;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const ours = loadRun(RUN_PATH);
|
|
117
|
+
console.log(`# BEIR significance test (ADR-086)`);
|
|
118
|
+
console.log(`Run: ${RUN_PATH}`);
|
|
119
|
+
console.log(`Dataset: ${ours.dataset ?? '?'} Model: ${ours.model ?? '?'} Queries: ${ours.perQuery.length}`);
|
|
120
|
+
console.log(`Bootstrap: ${ITERATIONS} iterations, seed=${SEED}`);
|
|
121
|
+
|
|
122
|
+
const rng = mulberry32(SEED);
|
|
123
|
+
const ourScores = ours.perQuery.map((q) => q.ndcg10);
|
|
124
|
+
|
|
125
|
+
const t0 = performance.now();
|
|
126
|
+
const oursCI = bootstrapCI(ourScores, ITERATIONS, rng);
|
|
127
|
+
console.log(`\n=== Our nDCG@10 (1-sample bootstrap CI) ===`);
|
|
128
|
+
console.log(` point: ${oursCI.point.toFixed(4)}`);
|
|
129
|
+
console.log(` 95% CI: [${oursCI.lower.toFixed(4)}, ${oursCI.upper.toFixed(4)}]`);
|
|
130
|
+
|
|
131
|
+
if (ours.baselines) {
|
|
132
|
+
console.log(`\n=== vs each published baseline (CI overlap) ===`);
|
|
133
|
+
for (const [name, score] of Object.entries(ours.baselines)) {
|
|
134
|
+
const ciDiff = oursCI.point - score;
|
|
135
|
+
const lowerDiff = oursCI.lower - score;
|
|
136
|
+
const upperDiff = oursCI.upper - score;
|
|
137
|
+
const direction = ciDiff > 0 ? '↑ above' : '↓ below';
|
|
138
|
+
const sig = (lowerDiff > 0 && upperDiff > 0) ? 'p<0.05 (95% CI excludes baseline)'
|
|
139
|
+
: (lowerDiff < 0 && upperDiff < 0) ? 'p<0.05 (95% CI below baseline)'
|
|
140
|
+
: 'n.s. (95% CI overlaps baseline)';
|
|
141
|
+
console.log(` ${score.toFixed(3)} ${name.padEnd(28)} Δ=${ciDiff >= 0 ? '+' : ''}${ciDiff.toFixed(4)} ${direction} [${sig}]`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (PAIRED_PATH) {
|
|
146
|
+
const baseline = loadRun(PAIRED_PATH);
|
|
147
|
+
console.log(`\n=== Paired bootstrap vs ${PAIRED_PATH} ===`);
|
|
148
|
+
const oursByQid = new Map(ours.perQuery.map((q) => [q.qid, q.ndcg10]));
|
|
149
|
+
const baseByQid = new Map(baseline.perQuery.map((q) => [q.qid, q.ndcg10]));
|
|
150
|
+
const rng2 = mulberry32(SEED + 1);
|
|
151
|
+
const paired = pairedBootstrap(oursByQid, baseByQid, ITERATIONS, rng2);
|
|
152
|
+
console.log(` paired queries: ${paired.n_paired}`);
|
|
153
|
+
console.log(` Δ (ours - baseline): ${paired.point_diff >= 0 ? '+' : ''}${paired.point_diff.toFixed(4)}`);
|
|
154
|
+
console.log(` 95% CI: [${paired.lower_95.toFixed(4)}, ${paired.upper_95.toFixed(4)}]`);
|
|
155
|
+
console.log(` one-sided p (Δ ≤ 0): ${paired.p_one_sided.toFixed(4)}`);
|
|
156
|
+
const sig = paired.lower_95 > 0 ? 'SIGNIFICANT improvement (p<0.025 one-sided)' :
|
|
157
|
+
paired.upper_95 < 0 ? 'SIGNIFICANT regression (p<0.025 one-sided)' :
|
|
158
|
+
'NOT SIGNIFICANT — CI overlaps zero';
|
|
159
|
+
console.log(` verdict: ${sig}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`\nBootstrap took ${((performance.now() - t0) / 1000).toFixed(1)}s.`);
|
package/scripts/run-beir-bge.mjs
CHANGED
|
@@ -218,25 +218,26 @@ async function main() {
|
|
|
218
218
|
|
|
219
219
|
console.log(`\nRunning ${evalQueryIds.length} queries...`);
|
|
220
220
|
let nSum = 0, mSum = 0, r10Sum = 0, r100Sum = 0, n = 0;
|
|
221
|
+
// ADR-086 — save per-query metrics for paired bootstrap significance testing.
|
|
222
|
+
const perQuery = [];
|
|
221
223
|
const tQ = performance.now();
|
|
222
224
|
for (const qid of evalQueryIds) {
|
|
223
225
|
const qtext = queriesById.get(qid);
|
|
224
226
|
if (!qtext) continue;
|
|
225
|
-
// BGE
|
|
226
|
-
// but only for v1.5 *en-icl* variants. v1.5 (non-icl) works without it.
|
|
227
|
+
// BGE v1.5 non-icl works without the query prefix.
|
|
227
228
|
const qEmb = await emb.embed(qtext);
|
|
228
|
-
// Score every doc, sort.
|
|
229
229
|
const scores = new Array(docEmbeds.length);
|
|
230
230
|
for (let i = 0; i < docEmbeds.length; i++) scores[i] = { id: docIds[i], score: cosine(qEmb, docEmbeds[i]) };
|
|
231
231
|
scores.sort((a, b) => b.score - a.score);
|
|
232
232
|
const top100 = scores.slice(0, 100).map((s) => s.id);
|
|
233
233
|
|
|
234
234
|
const qmap = qrels.get(qid);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
n++;
|
|
235
|
+
const qNdcg = ndcg(top100, qmap, 10);
|
|
236
|
+
const qMrr = mrr(top100, qmap, 10);
|
|
237
|
+
const qR10 = recall(top100, qmap, 10);
|
|
238
|
+
const qR100 = recall(top100, qmap, 100);
|
|
239
|
+
nSum += qNdcg; mSum += qMrr; r10Sum += qR10; r100Sum += qR100; n++;
|
|
240
|
+
perQuery.push({ qid, ndcg10: qNdcg, mrr10: qMrr, recall10: qR10, recall100: qR100 });
|
|
240
241
|
if (n % 50 === 0) {
|
|
241
242
|
const elapsed = (performance.now() - tQ) / 1000;
|
|
242
243
|
console.log(` ${n}/${evalQueryIds.length} in ${elapsed.toFixed(0)}s running nDCG@10=${(nSum / n).toFixed(4)}`);
|
|
@@ -276,6 +277,7 @@ async function main() {
|
|
|
276
277
|
queries: n,
|
|
277
278
|
corpusSize: corpus.length,
|
|
278
279
|
metrics: { ndcg10, mrr10, recall10, recall100, avgQueryLatencyMs: queryMs / n },
|
|
280
|
+
perQuery, // ADR-086: per-query metrics for paired bootstrap significance testing
|
|
279
281
|
baselines: BASELINES_NDCG10,
|
|
280
282
|
ourRank,
|
|
281
283
|
leaderboardLength: ranking.length,
|