@tangle-network/agent-eval 0.126.7 → 0.128.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/CHANGELOG.md +59 -0
- package/README.md +6 -2
- package/dist/analyst/index.d.ts +52 -43
- package/dist/analyst/index.js +4 -4
- package/dist/belief-state/index.d.ts +35 -40
- package/dist/belief-state/index.js +2 -2
- package/dist/benchmarks/index.d.ts +30 -12
- package/dist/benchmarks/index.js +12 -11
- package/dist/builder-eval/index.js +1 -1
- package/dist/campaign/index.d.ts +441 -160
- package/dist/campaign/index.js +11 -10
- package/dist/{chunk-IILEIWGW.js → chunk-2JX3CFMB.js} +100 -40
- package/dist/chunk-2JX3CFMB.js.map +1 -0
- package/dist/chunk-2MKQIFS4.js +183 -0
- package/dist/chunk-2MKQIFS4.js.map +1 -0
- package/dist/{chunk-NUKSVU3W.js → chunk-5DTSBUL2.js} +2 -2
- package/dist/{chunk-H5UD2323.js → chunk-7ZZMD7UK.js} +102 -2
- package/dist/chunk-7ZZMD7UK.js.map +1 -0
- package/dist/{chunk-474LBSOX.js → chunk-BYT7ELPS.js} +45 -18
- package/dist/chunk-BYT7ELPS.js.map +1 -0
- package/dist/{chunk-LUNF2SEL.js → chunk-DJKY2TSY.js} +3 -3
- package/dist/chunk-DPUHNQLN.js +232 -0
- package/dist/chunk-DPUHNQLN.js.map +1 -0
- package/dist/chunk-DRYIUNWY.js +622 -0
- package/dist/chunk-DRYIUNWY.js.map +1 -0
- package/dist/{chunk-IPYXE555.js → chunk-EJGRPCO3.js} +36 -13
- package/dist/chunk-EJGRPCO3.js.map +1 -0
- package/dist/{chunk-OCFJACJU.js → chunk-EOSZT7PL.js} +82 -18
- package/dist/chunk-EOSZT7PL.js.map +1 -0
- package/dist/{chunk-ZVCHKKOP.js → chunk-EZJEIH2R.js} +39 -15
- package/dist/chunk-EZJEIH2R.js.map +1 -0
- package/dist/{chunk-M7AH34KV.js → chunk-IHQDPH7D.js} +32 -16
- package/dist/chunk-IHQDPH7D.js.map +1 -0
- package/dist/{chunk-PJQFMIOX.js → chunk-MHELPNRP.js} +31 -1
- package/dist/chunk-MHELPNRP.js.map +1 -0
- package/dist/{chunk-K6IAZZ6L.js → chunk-NACAGYSY.js} +153 -122
- package/dist/chunk-NACAGYSY.js.map +1 -0
- package/dist/{chunk-BTACVO2W.js → chunk-NKAGIDE2.js} +542 -258
- package/dist/chunk-NKAGIDE2.js.map +1 -0
- package/dist/{chunk-ARU2PZFM.js → chunk-NYLOYM6N.js} +43 -23
- package/dist/chunk-NYLOYM6N.js.map +1 -0
- package/dist/{chunk-QOTFXW5L.js → chunk-P5W7RQKK.js} +2 -2
- package/dist/{chunk-IR3KBHOY.js → chunk-P6FYH6K4.js} +39 -3
- package/dist/chunk-P6FYH6K4.js.map +1 -0
- package/dist/{chunk-J5SQWP6Y.js → chunk-PBE2LOSS.js} +18 -2
- package/dist/chunk-PBE2LOSS.js.map +1 -0
- package/dist/{chunk-X4UCIOTZ.js → chunk-S5YLIBFX.js} +2 -2
- package/dist/{chunk-7VYEGHWF.js → chunk-SZLVEKMJ.js} +41 -20
- package/dist/chunk-SZLVEKMJ.js.map +1 -0
- package/dist/{chunk-P22LJ3Y2.js → chunk-TBL77AUT.js} +8 -8
- package/dist/chunk-TBL77AUT.js.map +1 -0
- package/dist/{chunk-LKKT3IVV.js → chunk-TSN7JT6D.js} +6 -4
- package/dist/{chunk-LKKT3IVV.js.map → chunk-TSN7JT6D.js.map} +1 -1
- package/dist/{chunk-E3IADPLG.js → chunk-UB2LOJ6Q.js} +51 -227
- package/dist/chunk-UB2LOJ6Q.js.map +1 -0
- package/dist/{chunk-MAX3TN3C.js → chunk-UWZZKKU7.js} +7 -19
- package/dist/chunk-UWZZKKU7.js.map +1 -0
- package/dist/{chunk-DPZAEKA6.js → chunk-VLOATJQ2.js} +85 -57
- package/dist/chunk-VLOATJQ2.js.map +1 -0
- package/dist/{chunk-MOXWMGPC.js → chunk-XDWDC2MP.js} +197 -79
- package/dist/chunk-XDWDC2MP.js.map +1 -0
- package/dist/{chunk-CGG5SLH3.js → chunk-XPRT64IE.js} +2 -2
- package/dist/{chunk-FO7HEH76.js → chunk-YJBNWCAA.js} +2 -2
- package/dist/{chunk-KE2VWPZX.js → chunk-ZUUWPZCV.js} +3 -3
- package/dist/cli.js +4 -4
- package/dist/contract/index.d.ts +438 -138
- package/dist/contract/index.js +259 -113
- package/dist/contract/index.js.map +1 -1
- package/dist/control.d.ts +56 -39
- package/dist/control.js +2 -3
- package/dist/hosted/index.d.ts +152 -51
- package/dist/hosted/index.js +24 -1
- package/dist/index.d.ts +399 -277
- package/dist/index.js +363 -244
- package/dist/index.js.map +1 -1
- package/dist/meta-eval/index.d.ts +33 -36
- package/dist/meta-eval/index.js +2 -2
- package/dist/multishot/index.d.ts +1 -0
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.js +2 -2
- package/dist/reporting.d.ts +101 -80
- package/dist/reporting.js +7 -5
- package/dist/rl.d.ts +198 -142
- package/dist/rl.js +235 -127
- package/dist/rl.js.map +1 -1
- package/dist/rollout/index.d.ts +54 -67
- package/dist/rollout/index.js +6 -5
- package/dist/{run-campaign-FFRM3RH5.js → run-campaign-ISHFZ7FJ.js} +6 -3
- package/dist/supervisor-run/index.d.ts +7 -15
- package/dist/supervisor-run/index.js +2 -2
- package/dist/traces.d.ts +72 -50
- package/dist/traces.js +9 -6
- package/dist/wire/index.d.ts +1 -0
- package/dist/wire/index.js +2 -2
- package/docs/concepts.md +18 -1
- package/docs/customer-journeys.md +7 -6
- package/docs/hosted-ingest-spec.md +37 -40
- package/docs/insight-report.md +78 -7
- package/docs/research-report-methodology.md +20 -21
- package/docs/rollout.md +13 -8
- package/package.json +4 -3
- package/dist/chunk-474LBSOX.js.map +0 -1
- package/dist/chunk-7VYEGHWF.js.map +0 -1
- package/dist/chunk-ARU2PZFM.js.map +0 -1
- package/dist/chunk-BTACVO2W.js.map +0 -1
- package/dist/chunk-DPZAEKA6.js.map +0 -1
- package/dist/chunk-E3IADPLG.js.map +0 -1
- package/dist/chunk-H5UD2323.js.map +0 -1
- package/dist/chunk-IILEIWGW.js.map +0 -1
- package/dist/chunk-IPYXE555.js.map +0 -1
- package/dist/chunk-IR3KBHOY.js.map +0 -1
- package/dist/chunk-J5SQWP6Y.js.map +0 -1
- package/dist/chunk-K6IAZZ6L.js.map +0 -1
- package/dist/chunk-M7AH34KV.js.map +0 -1
- package/dist/chunk-MAX3TN3C.js.map +0 -1
- package/dist/chunk-MOXWMGPC.js.map +0 -1
- package/dist/chunk-OCFJACJU.js.map +0 -1
- package/dist/chunk-P22LJ3Y2.js.map +0 -1
- package/dist/chunk-PJQFMIOX.js.map +0 -1
- package/dist/chunk-VSMTAMNK.js +0 -53
- package/dist/chunk-VSMTAMNK.js.map +0 -1
- package/dist/chunk-ZVCHKKOP.js.map +0 -1
- package/dist/chunk-ZZUXHH3R.js +0 -99
- package/dist/chunk-ZZUXHH3R.js.map +0 -1
- /package/dist/{chunk-NUKSVU3W.js.map → chunk-5DTSBUL2.js.map} +0 -0
- /package/dist/{chunk-LUNF2SEL.js.map → chunk-DJKY2TSY.js.map} +0 -0
- /package/dist/{chunk-QOTFXW5L.js.map → chunk-P5W7RQKK.js.map} +0 -0
- /package/dist/{chunk-X4UCIOTZ.js.map → chunk-S5YLIBFX.js.map} +0 -0
- /package/dist/{chunk-CGG5SLH3.js.map → chunk-XPRT64IE.js.map} +0 -0
- /package/dist/{chunk-FO7HEH76.js.map → chunk-YJBNWCAA.js.map} +0 -0
- /package/dist/{chunk-KE2VWPZX.js.map → chunk-ZUUWPZCV.js.map} +0 -0
- /package/dist/{run-campaign-FFRM3RH5.js.map → run-campaign-ISHFZ7FJ.js.map} +0 -0
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
summarizeBackendIntegrity
|
|
3
|
-
} from "./chunk-ZVCHKKOP.js";
|
|
4
1
|
import {
|
|
5
2
|
paretoChart
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VLOATJQ2.js";
|
|
4
|
+
import {
|
|
5
|
+
pairRunRecords
|
|
6
|
+
} from "./chunk-DPUHNQLN.js";
|
|
7
7
|
import {
|
|
8
|
-
cohensD,
|
|
9
8
|
continuousAgreement,
|
|
10
9
|
pairedBootstrap,
|
|
10
|
+
pairedCohensDz,
|
|
11
11
|
pairedMde,
|
|
12
12
|
pairedTTest,
|
|
13
13
|
pearsonR,
|
|
14
|
-
|
|
14
|
+
requiredPairedSampleSize,
|
|
15
15
|
spearmanR
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-MHELPNRP.js";
|
|
17
17
|
import {
|
|
18
18
|
llmSpans
|
|
19
19
|
} from "./chunk-ZET2UAYW.js";
|
|
20
20
|
import {
|
|
21
|
-
|
|
22
|
-
} from "./chunk-
|
|
21
|
+
validateRunRecord
|
|
22
|
+
} from "./chunk-2JX3CFMB.js";
|
|
23
23
|
|
|
24
24
|
// src/contamination-guard.ts
|
|
25
25
|
function checkCanaries(output, scenarios) {
|
|
@@ -124,14 +124,15 @@ function excerpt(source, needle) {
|
|
|
124
124
|
|
|
125
125
|
// src/contract/analyze-runs.ts
|
|
126
126
|
function summarizeExecution(opts) {
|
|
127
|
+
const runs = opts.runs.map(validateRunRecord);
|
|
127
128
|
const bins = opts.histogramBins ?? 12;
|
|
128
129
|
return {
|
|
129
|
-
execution: computeExecutionInsight(
|
|
130
|
-
costProvenance: summarizeCostProvenance(
|
|
130
|
+
execution: computeExecutionInsight(runs, bins),
|
|
131
|
+
costProvenance: summarizeCostProvenance(runs)
|
|
131
132
|
};
|
|
132
133
|
}
|
|
133
134
|
async function analyzeRuns(opts) {
|
|
134
|
-
const runs = opts.runs;
|
|
135
|
+
const runs = opts.runs.map(validateRunRecord);
|
|
135
136
|
const bins = opts.histogramBins ?? 12;
|
|
136
137
|
const threshold = opts.decisionThreshold ?? 0.02;
|
|
137
138
|
const split = resolveSplit(runs, opts.split ?? "auto");
|
|
@@ -146,15 +147,15 @@ async function analyzeRuns(opts) {
|
|
|
146
147
|
runs,
|
|
147
148
|
histogramBins: bins
|
|
148
149
|
});
|
|
149
|
-
const knownCostRuns = runs.filter((run) =>
|
|
150
|
-
const costs = knownCostRuns.map((r) => r.costUsd).filter(
|
|
150
|
+
const knownCostRuns = runs.filter((run) => run.costProvenance.kind !== "uncaptured");
|
|
151
|
+
const costs = knownCostRuns.map((r) => r.costUsd).filter(isFiniteNumber);
|
|
151
152
|
const costDist = distributionOf(costs, bins);
|
|
152
153
|
const pareto = paretoChart(knownCostRuns, { split });
|
|
153
154
|
const degraded = {};
|
|
154
155
|
if (provenance.uncaptured.n > 0) {
|
|
155
156
|
degraded.cost = diagnoseCostCoverage(runs, provenance);
|
|
156
157
|
} else if (costs.length === 0 || costs.every((c) => c === 0)) {
|
|
157
|
-
degraded.cost =
|
|
158
|
+
degraded.cost = `all ${runs.length} explicitly observed or estimated USD values are $0`;
|
|
158
159
|
}
|
|
159
160
|
if (pareto.points.length < 2) {
|
|
160
161
|
degraded.pareto = pareto.points.length === 0 ? "no candidates \u2014 Pareto unavailable" : "single candidate \u2014 Pareto is a single point, not a frontier";
|
|
@@ -169,7 +170,7 @@ async function analyzeRuns(opts) {
|
|
|
169
170
|
const interRater = opts.raterScores ? computeInterRater(opts.raterScores) : void 0;
|
|
170
171
|
const lift = computeLift(runs, opts.baselineCandidateId, opts.candidateCandidateId, split);
|
|
171
172
|
const failureClusters = opts.analyst ? await computeFailureClusters(runs, opts.analyst, split) : void 0;
|
|
172
|
-
const
|
|
173
|
+
const failureClasses = computeFailureClasses(runs, split);
|
|
173
174
|
const contamination = opts.canaryScenarios ? computeContamination(runs, opts.canaryScenarios) : void 0;
|
|
174
175
|
const outcomeCorrelation = opts.outcomeSignal ? computeOutcomeCorrelation(runs, opts.outcomeSignal, split) : void 0;
|
|
175
176
|
const release = buildReleaseScorecard(composite, lift, contamination);
|
|
@@ -180,7 +181,7 @@ async function analyzeRuns(opts) {
|
|
|
180
181
|
interRater,
|
|
181
182
|
lift,
|
|
182
183
|
failureClusters,
|
|
183
|
-
|
|
184
|
+
failureClasses,
|
|
184
185
|
contamination,
|
|
185
186
|
outcomeCorrelation,
|
|
186
187
|
priorPeriodComparison,
|
|
@@ -199,7 +200,7 @@ async function analyzeRuns(opts) {
|
|
|
199
200
|
contamination,
|
|
200
201
|
outcomeCorrelation,
|
|
201
202
|
release,
|
|
202
|
-
...
|
|
203
|
+
...failureClasses ? { failureClasses } : {},
|
|
203
204
|
...priorPeriodComparison ? { priorPeriodComparison } : {},
|
|
204
205
|
recommendations
|
|
205
206
|
};
|
|
@@ -213,16 +214,34 @@ function computeExecutionInsight(runs, bins) {
|
|
|
213
214
|
(row) => row.costUsd !== void 0 ? [row.costUsd] : []
|
|
214
215
|
);
|
|
215
216
|
const modelCounts = /* @__PURE__ */ new Map();
|
|
216
|
-
let
|
|
217
|
-
let
|
|
217
|
+
let executionErrorRuns = 0;
|
|
218
|
+
let executionErrorEvents = 0;
|
|
218
219
|
let errorReportingRuns = 0;
|
|
220
|
+
let errorSpanEvents = 0;
|
|
221
|
+
let errorSpanReportingRuns = 0;
|
|
222
|
+
const terminalOutcomes = {
|
|
223
|
+
succeeded: 0,
|
|
224
|
+
failed: 0,
|
|
225
|
+
cancelled: 0,
|
|
226
|
+
incomplete: 0,
|
|
227
|
+
unknown: 0
|
|
228
|
+
};
|
|
229
|
+
const errorsByTerminalOutcome = {
|
|
230
|
+
succeeded: { withErrors: 0, withoutErrors: 0, unreported: 0 },
|
|
231
|
+
failed: { withErrors: 0, withoutErrors: 0, unreported: 0 },
|
|
232
|
+
cancelled: { withErrors: 0, withoutErrors: 0, unreported: 0 },
|
|
233
|
+
incomplete: { withErrors: 0, withoutErrors: 0, unreported: 0 },
|
|
234
|
+
unknown: { withErrors: 0, withoutErrors: 0, unreported: 0 }
|
|
235
|
+
};
|
|
219
236
|
let modelCallRuns = 0;
|
|
220
237
|
let modelCallEvents = 0;
|
|
221
238
|
let modelCallReportingRuns = 0;
|
|
222
239
|
for (const run of runs) {
|
|
223
240
|
modelCounts.set(run.model, (modelCounts.get(run.model) ?? 0) + 1);
|
|
224
|
-
const
|
|
225
|
-
|
|
241
|
+
const terminalOutcome = run.terminalOutcome;
|
|
242
|
+
terminalOutcomes[terminalOutcome] += 1;
|
|
243
|
+
const modelCalls = nonNegativeCountRaw(run, "llm_span_count");
|
|
244
|
+
if (modelCalls !== void 0) {
|
|
226
245
|
modelCallEvents += modelCalls;
|
|
227
246
|
modelCallReportingRuns += 1;
|
|
228
247
|
}
|
|
@@ -230,13 +249,19 @@ function computeExecutionInsight(runs, bins) {
|
|
|
230
249
|
if ((modelCalls ?? 0) > 0 || usage.input > 0 || usage.output > 0 || (usage.cached ?? 0) > 0 || (usage.cacheWrite ?? 0) > 0) {
|
|
231
250
|
modelCallRuns += 1;
|
|
232
251
|
}
|
|
233
|
-
const errorEvents = run
|
|
234
|
-
if (
|
|
235
|
-
|
|
252
|
+
const errorEvents = reportedExecutionErrorEvents(run);
|
|
253
|
+
if (errorEvents !== void 0) {
|
|
254
|
+
executionErrorEvents += errorEvents;
|
|
236
255
|
errorReportingRuns += 1;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
256
|
+
if (errorEvents > 0) {
|
|
257
|
+
executionErrorRuns += 1;
|
|
258
|
+
errorsByTerminalOutcome[terminalOutcome].withErrors += 1;
|
|
259
|
+
} else errorsByTerminalOutcome[terminalOutcome].withoutErrors += 1;
|
|
260
|
+
} else errorsByTerminalOutcome[terminalOutcome].unreported += 1;
|
|
261
|
+
const reportedErrorSpans = nonNegativeCountRaw(run, "error_span_count");
|
|
262
|
+
if (reportedErrorSpans !== void 0) {
|
|
263
|
+
errorSpanEvents += reportedErrorSpans;
|
|
264
|
+
errorSpanReportingRuns += 1;
|
|
240
265
|
}
|
|
241
266
|
}
|
|
242
267
|
return {
|
|
@@ -267,14 +292,28 @@ function computeExecutionInsight(runs, bins) {
|
|
|
267
292
|
events: modelCallEvents,
|
|
268
293
|
reportingRuns: modelCallReportingRuns
|
|
269
294
|
},
|
|
270
|
-
|
|
271
|
-
runs:
|
|
272
|
-
fraction:
|
|
273
|
-
|
|
274
|
-
reportingRuns: errorReportingRuns
|
|
275
|
-
|
|
295
|
+
executionErrors: {
|
|
296
|
+
runs: executionErrorRuns,
|
|
297
|
+
fraction: errorReportingRuns > 0 ? executionErrorRuns / errorReportingRuns : null,
|
|
298
|
+
events: executionErrorEvents,
|
|
299
|
+
reportingRuns: errorReportingRuns,
|
|
300
|
+
errorSpanEvents,
|
|
301
|
+
errorSpanReportingRuns,
|
|
302
|
+
byTerminalOutcome: errorsByTerminalOutcome
|
|
303
|
+
},
|
|
304
|
+
terminalOutcomes
|
|
276
305
|
};
|
|
277
306
|
}
|
|
307
|
+
function reportedExecutionErrorEvents(run) {
|
|
308
|
+
const canonical = nonNegativeCountRaw(run, "execution_error_count");
|
|
309
|
+
if (canonical !== void 0) return canonical;
|
|
310
|
+
const toolErrors = nonNegativeCountRaw(run, "tool_errors");
|
|
311
|
+
return toolErrors;
|
|
312
|
+
}
|
|
313
|
+
function nonNegativeCountRaw(run, key) {
|
|
314
|
+
const value = finiteRaw(run, key);
|
|
315
|
+
return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
316
|
+
}
|
|
278
317
|
function summarizeTokenUsage(usages, bins) {
|
|
279
318
|
const reasoning = usages.flatMap(
|
|
280
319
|
(usage) => usage.reasoning !== void 0 ? [usage.reasoning] : []
|
|
@@ -332,7 +371,7 @@ function summarizeCostProvenance(runs) {
|
|
|
332
371
|
knownFraction: 0
|
|
333
372
|
};
|
|
334
373
|
for (const run of runs) {
|
|
335
|
-
const cost =
|
|
374
|
+
const cost = run.costProvenance;
|
|
336
375
|
if (cost.kind === "uncaptured") {
|
|
337
376
|
summary.uncaptured.n += 1;
|
|
338
377
|
} else {
|
|
@@ -347,36 +386,25 @@ function summarizeCostProvenance(runs) {
|
|
|
347
386
|
function diagnoseCostCoverage(runs, provenance) {
|
|
348
387
|
const uncaptured = provenance.uncaptured.n;
|
|
349
388
|
const known = provenance.observed.n + provenance.estimated.n;
|
|
350
|
-
const explicitUncaptured = runs.some((run) => run.costProvenance?.kind === "uncaptured");
|
|
351
|
-
if (uncaptured === runs.length && !explicitUncaptured) return diagnoseZeroCost(runs);
|
|
352
389
|
if (uncaptured === runs.length) {
|
|
353
390
|
return `USD cost uncaptured for all ${runs.length} runs \u2014 no observed or estimated USD values; token and wall-time metrics remain available.`;
|
|
354
391
|
}
|
|
355
392
|
return `USD cost uncaptured for ${uncaptured}/${runs.length} runs; excluded those rows from cost statistics (${known}/${runs.length} retained: ${provenance.observed.n} observed, ${provenance.estimated.n} estimated).`;
|
|
356
393
|
}
|
|
357
|
-
function
|
|
358
|
-
const integrity = summarizeBackendIntegrity(runs);
|
|
359
|
-
const { totalRecords, stubRecords, uncostedRecords } = integrity;
|
|
360
|
-
if (totalRecords > 0 && stubRecords === totalRecords) {
|
|
361
|
-
return `no costUsd values recorded \u2014 all ${totalRecords} records are stub-mode (zero token usage). The backend never reported real LLM activity, so cost cannot be computed; verify the backend actually ran before trusting this corpus.`;
|
|
362
|
-
}
|
|
363
|
-
if (uncostedRecords > 0) {
|
|
364
|
-
return `no costUsd values recorded \u2014 ${uncostedRecords}/${totalRecords} records have token usage but $0 cost (unpriced model). Check isModelPriced(model) for the run's model id and add it to FAMILY_PRICING.`;
|
|
365
|
-
}
|
|
366
|
-
if (stubRecords > 0) {
|
|
367
|
-
return `no costUsd values recorded \u2014 ${stubRecords}/${totalRecords} records are stub-mode (zero token usage); the remainder reported neither tokens nor cost. Cost axis carries no signal.`;
|
|
368
|
-
}
|
|
369
|
-
return "no costUsd values recorded \u2014 cost axis carries no signal";
|
|
370
|
-
}
|
|
371
|
-
function computeFailureModes(runs) {
|
|
394
|
+
function computeFailureClasses(runs, split) {
|
|
372
395
|
const counts = /* @__PURE__ */ new Map();
|
|
373
396
|
for (const r of runs) {
|
|
374
|
-
|
|
375
|
-
|
|
397
|
+
if (!isTaskFailure(r, split)) continue;
|
|
398
|
+
const key = r.failureClass !== void 0 && r.failureClass !== "success" ? r.failureClass : "unknown";
|
|
399
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
376
400
|
}
|
|
377
401
|
if (counts.size === 0) return void 0;
|
|
378
402
|
const n = runs.length;
|
|
379
|
-
return [...counts.entries()].map(([
|
|
403
|
+
return [...counts.entries()].map(([failureClass, count]) => ({
|
|
404
|
+
failureClass,
|
|
405
|
+
count,
|
|
406
|
+
share: n > 0 ? count / n : 0
|
|
407
|
+
})).sort((a, b) => b.count - a.count || a.failureClass.localeCompare(b.failureClass));
|
|
380
408
|
}
|
|
381
409
|
function computePriorPeriodComparison(current, baseline, split, windowLabel) {
|
|
382
410
|
if (current.length === 0 || baseline.length === 0) return void 0;
|
|
@@ -434,7 +462,10 @@ function computePriorPeriodComparison(current, baseline, split, windowLabel) {
|
|
|
434
462
|
};
|
|
435
463
|
}
|
|
436
464
|
function knownCostValues(runs) {
|
|
437
|
-
return runs.filter((run) =>
|
|
465
|
+
return runs.filter((run) => run.costProvenance.kind !== "uncaptured").map((run) => run.costUsd).filter(isFiniteNumber);
|
|
466
|
+
}
|
|
467
|
+
function isFiniteNumber(value) {
|
|
468
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
438
469
|
}
|
|
439
470
|
function collectPerDimension(runs) {
|
|
440
471
|
const out = {};
|
|
@@ -465,15 +496,15 @@ function welchCompare(baseline, current) {
|
|
|
465
496
|
const pooledStddev = Math.sqrt(
|
|
466
497
|
((baselineN - 1) * baselineVar + (currentN - 1) * currentVar) / Math.max(1, baselineN + currentN - 2)
|
|
467
498
|
);
|
|
468
|
-
const
|
|
469
|
-
const significant = pValue < 0.05 && Math.abs(
|
|
499
|
+
const cohensD = pooledStddev > 0 ? delta / pooledStddev : 0;
|
|
500
|
+
const significant = pValue < 0.05 && Math.abs(cohensD) >= 0.2;
|
|
470
501
|
return {
|
|
471
502
|
current: currentMean,
|
|
472
503
|
baseline: baselineMean,
|
|
473
504
|
delta,
|
|
474
505
|
ci95,
|
|
475
506
|
pValue,
|
|
476
|
-
cohensD
|
|
507
|
+
cohensD,
|
|
477
508
|
baselineN,
|
|
478
509
|
currentN,
|
|
479
510
|
significant
|
|
@@ -504,21 +535,19 @@ function resolveSplit(runs, pref) {
|
|
|
504
535
|
return hasHoldout ? "holdout" : "search";
|
|
505
536
|
}
|
|
506
537
|
function compositeOf(run, split) {
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
const alt = split === "holdout" ? run.outcome.searchScore : run.outcome.holdoutScore;
|
|
510
|
-
return Number.isFinite(alt) ? alt : Number.NaN;
|
|
538
|
+
const score = split === "holdout" ? run.outcome.holdoutScore : run.outcome.searchScore;
|
|
539
|
+
return Number.isFinite(score) ? score : Number.NaN;
|
|
511
540
|
}
|
|
512
541
|
function distributionOf(values, bins, withIds) {
|
|
513
542
|
if (values.length === 0) {
|
|
514
543
|
return {
|
|
515
544
|
n: 0,
|
|
516
|
-
mean:
|
|
517
|
-
p50:
|
|
518
|
-
p95:
|
|
519
|
-
stddev:
|
|
520
|
-
min:
|
|
521
|
-
max:
|
|
545
|
+
mean: null,
|
|
546
|
+
p50: null,
|
|
547
|
+
p95: null,
|
|
548
|
+
stddev: null,
|
|
549
|
+
min: null,
|
|
550
|
+
max: null,
|
|
522
551
|
histogram: []
|
|
523
552
|
};
|
|
524
553
|
}
|
|
@@ -677,41 +706,28 @@ function computeLift(runs, baselineId, candidateId, split) {
|
|
|
677
706
|
const ids = [...new Set(runs.map((r) => r.candidateId))];
|
|
678
707
|
if (ids.length !== 2) return void 0;
|
|
679
708
|
const [idA, idB] = ids;
|
|
680
|
-
const
|
|
681
|
-
|
|
709
|
+
const scoresA = finiteCompositeScores(
|
|
710
|
+
runs.filter((run) => run.candidateId === idA),
|
|
711
|
+
split
|
|
712
|
+
);
|
|
713
|
+
const scoresB = finiteCompositeScores(
|
|
714
|
+
runs.filter((run) => run.candidateId === idB),
|
|
715
|
+
split
|
|
716
|
+
);
|
|
717
|
+
if (scoresA.length === 0 || scoresB.length === 0) return void 0;
|
|
718
|
+
const meanA = mean(scoresA);
|
|
719
|
+
const meanB = mean(scoresB);
|
|
682
720
|
bId = meanA <= meanB ? idA : idB;
|
|
683
721
|
cId = meanA <= meanB ? idB : idA;
|
|
684
722
|
}
|
|
685
723
|
const baseline = runs.filter((r) => r.candidateId === bId);
|
|
686
724
|
const candidate = runs.filter((r) => r.candidateId === cId);
|
|
687
725
|
if (baseline.length === 0 || candidate.length === 0) return void 0;
|
|
688
|
-
const
|
|
689
|
-
const
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const b = baselineByKey.get(pairingKey(cand));
|
|
694
|
-
if (b) {
|
|
695
|
-
const bC = compositeOf(b, split);
|
|
696
|
-
const cC = compositeOf(cand, split);
|
|
697
|
-
if (Number.isFinite(bC) && Number.isFinite(cC)) {
|
|
698
|
-
pairedBaseline.push(bC);
|
|
699
|
-
pairedCandidate.push(cC);
|
|
700
|
-
usedKeyPairing = true;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
if (!usedKeyPairing) {
|
|
705
|
-
const n = Math.min(baseline.length, candidate.length);
|
|
706
|
-
for (let i = 0; i < n; i++) {
|
|
707
|
-
const bC = compositeOf(baseline[i], split);
|
|
708
|
-
const cC = compositeOf(candidate[i], split);
|
|
709
|
-
if (Number.isFinite(bC) && Number.isFinite(cC)) {
|
|
710
|
-
pairedBaseline.push(bC);
|
|
711
|
-
pairedCandidate.push(cC);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
726
|
+
const scoredBaseline = baseline.filter((run) => Number.isFinite(compositeOf(run, split)));
|
|
727
|
+
const scoredCandidate = candidate.filter((run) => Number.isFinite(compositeOf(run, split)));
|
|
728
|
+
const pairing = pairRunRecords(scoredBaseline, scoredCandidate);
|
|
729
|
+
const pairedBaseline = pairing.pairs.map((pair) => compositeOf(pair.baseline, split));
|
|
730
|
+
const pairedCandidate = pairing.pairs.map((pair) => compositeOf(pair.treatment, split));
|
|
715
731
|
if (pairedBaseline.length === 0) return void 0;
|
|
716
732
|
const baselineMean = mean(pairedBaseline);
|
|
717
733
|
const candidateMean = mean(pairedCandidate);
|
|
@@ -722,10 +738,10 @@ function computeLift(runs, baselineId, candidateId, split) {
|
|
|
722
738
|
statistic: "mean"
|
|
723
739
|
});
|
|
724
740
|
const tTest = pairedTTest(pairedBaseline, pairedCandidate);
|
|
725
|
-
const d =
|
|
741
|
+
const d = pairedCohensDz(pairedBaseline, pairedCandidate);
|
|
726
742
|
const mde = pairedMde({ nPaired: pairedBaseline.length, power: 0.8, alpha: 0.05 });
|
|
727
|
-
const requiredN =
|
|
728
|
-
effect: Math.
|
|
743
|
+
const requiredN = d === null || d === 0 ? null : requiredPairedSampleSize({
|
|
744
|
+
effect: Math.abs(d),
|
|
729
745
|
power: 0.8,
|
|
730
746
|
alpha: 0.05
|
|
731
747
|
});
|
|
@@ -736,19 +752,18 @@ function computeLift(runs, baselineId, candidateId, split) {
|
|
|
736
752
|
ci95: [bootstrap.low, bootstrap.high],
|
|
737
753
|
pValue: tTest.p,
|
|
738
754
|
n: pairedBaseline.length,
|
|
755
|
+
unpairedBaseline: pairing.unpairedBaseline.length,
|
|
756
|
+
unpairedCandidate: pairing.unpairedTreatment.length,
|
|
739
757
|
cohensD: d,
|
|
740
758
|
mde,
|
|
741
759
|
requiredN
|
|
742
760
|
};
|
|
743
761
|
}
|
|
744
|
-
function pairingKey(r) {
|
|
745
|
-
return `${r.experimentId}::${r.seed}`;
|
|
746
|
-
}
|
|
747
762
|
function mean(arr) {
|
|
748
763
|
return arr.length === 0 ? 0 : arr.reduce((s, v) => s + v, 0) / arr.length;
|
|
749
764
|
}
|
|
750
765
|
async function computeFailureClusters(runs, analyst, split) {
|
|
751
|
-
const failed = runs.filter((
|
|
766
|
+
const failed = runs.filter((run) => isTaskFailure(run, split));
|
|
752
767
|
if (failed.length === 0) return { clusters: [], totalFailures: 0 };
|
|
753
768
|
const clusters = /* @__PURE__ */ new Map();
|
|
754
769
|
for (const run of failed) {
|
|
@@ -775,6 +790,14 @@ async function computeFailureClusters(runs, analyst, split) {
|
|
|
775
790
|
clusterList.sort((a, b) => b.share - a.share);
|
|
776
791
|
return { clusters: clusterList, totalFailures: failed.length };
|
|
777
792
|
}
|
|
793
|
+
function finiteCompositeScores(runs, split) {
|
|
794
|
+
return runs.map((run) => compositeOf(run, split)).filter(Number.isFinite);
|
|
795
|
+
}
|
|
796
|
+
function isTaskFailure(run, split) {
|
|
797
|
+
if (run.failureClass !== void 0 && run.failureClass !== "success") return true;
|
|
798
|
+
const score = compositeOf(run, split);
|
|
799
|
+
return Number.isFinite(score) && score < 0.5;
|
|
800
|
+
}
|
|
778
801
|
function computeContamination(runs, canaries) {
|
|
779
802
|
let leaks = 0;
|
|
780
803
|
const details = [];
|
|
@@ -832,24 +855,30 @@ function computeOutcomeCorrelation(runs, outcome, split) {
|
|
|
832
855
|
}
|
|
833
856
|
function buildReleaseScorecard(composite, lift, contamination) {
|
|
834
857
|
const axes = [];
|
|
835
|
-
const liftPass = lift === void 0
|
|
858
|
+
const liftPass = lift === void 0 ? "not_evaluated" : lift.ci95[0] > 0 ? "pass" : lift.delta > 0 ? "warn" : "fail";
|
|
836
859
|
axes.push({
|
|
837
860
|
name: "quality-lift",
|
|
838
861
|
status: liftPass,
|
|
839
862
|
detail: lift ? `delta=${lift.delta.toFixed(3)}, CI95=[${lift.ci95[0].toFixed(3)}, ${lift.ci95[1].toFixed(3)}], n=${lift.n}` : "no baseline/candidate pair available"
|
|
840
863
|
});
|
|
841
|
-
const contamPass = contamination === void 0
|
|
864
|
+
const contamPass = contamination === void 0 ? "not_evaluated" : contamination.leaks === 0 ? "pass" : "fail";
|
|
842
865
|
axes.push({
|
|
843
866
|
name: "contamination",
|
|
844
867
|
status: contamPass,
|
|
845
868
|
detail: contamination ? `${contamination.leaks} canary leak(s)` : "no canaries supplied"
|
|
846
869
|
});
|
|
847
|
-
axes.push(
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
870
|
+
axes.push(
|
|
871
|
+
composite.n === 0 ? {
|
|
872
|
+
name: "composite-distribution",
|
|
873
|
+
status: "not_evaluated",
|
|
874
|
+
detail: "no task-quality scores available"
|
|
875
|
+
} : {
|
|
876
|
+
name: "composite-distribution",
|
|
877
|
+
status: composite.mean !== null && composite.mean >= 0.5 ? "pass" : composite.mean !== null && composite.mean >= 0.3 ? "warn" : "fail",
|
|
878
|
+
detail: composite.mean === null || composite.p50 === null || composite.p95 === null ? "task-quality distribution is internally incomplete" : `mean=${composite.mean.toFixed(3)}, p50=${composite.p50.toFixed(3)}, p95=${composite.p95.toFixed(3)} over n=${composite.n}`
|
|
879
|
+
}
|
|
880
|
+
);
|
|
881
|
+
const status = axes.some((a) => a.status === "fail") ? "fail" : axes.some((a) => a.status === "warn" || a.status === "not_evaluated") ? "warn" : "pass";
|
|
853
882
|
return {
|
|
854
883
|
status,
|
|
855
884
|
axes,
|
|
@@ -884,7 +913,7 @@ function buildRecommendations(ctx) {
|
|
|
884
913
|
});
|
|
885
914
|
}
|
|
886
915
|
}
|
|
887
|
-
if (ctx.composite.n > 0) {
|
|
916
|
+
if (ctx.composite.n > 0 && ctx.composite.mean !== null && ctx.composite.p50 !== null && ctx.composite.p95 !== null) {
|
|
888
917
|
if (ctx.composite.mean < 0.3) {
|
|
889
918
|
const tail = ctx.composite.tailRuns ?? [];
|
|
890
919
|
const names = tail.slice(0, 5).map((t) => `${t.runId}=${t.score.toFixed(3)}`).join(", ");
|
|
@@ -907,15 +936,15 @@ function buildRecommendations(ctx) {
|
|
|
907
936
|
});
|
|
908
937
|
}
|
|
909
938
|
}
|
|
910
|
-
if (ctx.
|
|
911
|
-
const top = ctx.
|
|
939
|
+
if (ctx.failureClasses && ctx.failureClasses.length > 0) {
|
|
940
|
+
const top = ctx.failureClasses[0];
|
|
912
941
|
if (top.count >= 3 && top.share >= 0.15) {
|
|
913
942
|
out.push({
|
|
914
943
|
priority: top.share >= 0.25 ? "high" : "medium",
|
|
915
944
|
kind: "investigate",
|
|
916
|
-
title: `'${top.
|
|
917
|
-
detail: `The mean composite can look acceptable while one
|
|
918
|
-
evidencePath: "
|
|
945
|
+
title: `'${top.failureClass}' is the dominant failure class \u2014 ${top.count} runs (${(top.share * 100).toFixed(0)}% of the corpus)`,
|
|
946
|
+
detail: `The mean composite can look acceptable while one failure class dominates the lower tail. ${top.count} of ${ctx.composite.n} runs failed with '${top.failureClass}'${ctx.failureClasses.length > 1 ? ` (next: '${ctx.failureClasses[1].failureClass}' \xD7${ctx.failureClasses[1].count})` : ""}. Fix this cause first.`,
|
|
947
|
+
evidencePath: "failureClasses"
|
|
919
948
|
});
|
|
920
949
|
}
|
|
921
950
|
}
|
|
@@ -929,6 +958,8 @@ function buildRecommendations(ctx) {
|
|
|
929
958
|
});
|
|
930
959
|
}
|
|
931
960
|
if (ctx.lift) {
|
|
961
|
+
const pairedEffect = ctx.lift.cohensD === null ? "undefined (zero delta variance)" : ctx.lift.cohensD.toFixed(2);
|
|
962
|
+
const requiredRuns = ctx.lift.requiredN === null ? "not estimable" : `~${ctx.lift.requiredN} paired runs`;
|
|
932
963
|
const decisive = ctx.lift.ci95[0] > ctx.threshold;
|
|
933
964
|
const inconclusive = ctx.lift.ci95[0] <= ctx.threshold && ctx.lift.ci95[1] > ctx.threshold;
|
|
934
965
|
if (decisive) {
|
|
@@ -936,14 +967,14 @@ function buildRecommendations(ctx) {
|
|
|
936
967
|
priority: "critical",
|
|
937
968
|
kind: "ship",
|
|
938
969
|
title: `Ship \u2014 lift ${ctx.lift.delta.toFixed(3)} (95% CI ${ctx.lift.ci95[0].toFixed(3)}..${ctx.lift.ci95[1].toFixed(3)})`,
|
|
939
|
-
detail: `Holdout lift exceeds threshold ${ctx.threshold} with 95% bootstrap confidence (n=${ctx.lift.n}, p=${ctx.lift.pValue.toFixed(4)}, d=${
|
|
970
|
+
detail: `Holdout lift exceeds threshold ${ctx.threshold} with 95% bootstrap confidence (n=${ctx.lift.n}, p=${ctx.lift.pValue.toFixed(4)}, paired d=${pairedEffect}).`,
|
|
940
971
|
evidencePath: "lift"
|
|
941
972
|
});
|
|
942
973
|
} else if (inconclusive) {
|
|
943
974
|
out.push({
|
|
944
975
|
priority: "high",
|
|
945
976
|
kind: "expand-corpus",
|
|
946
|
-
title: `Inconclusive \u2014
|
|
977
|
+
title: `Inconclusive \u2014 required sample is ${requiredRuns} (have ${ctx.lift.n}) at current effect size`,
|
|
947
978
|
detail: `CI straddles threshold. Current MDE at 80% power is ${ctx.lift.mde.toFixed(3)}; observed delta is ${ctx.lift.delta.toFixed(3)}.`,
|
|
948
979
|
evidencePath: "lift"
|
|
949
980
|
});
|
|
@@ -1006,4 +1037,4 @@ export {
|
|
|
1006
1037
|
summarizeExecution,
|
|
1007
1038
|
analyzeRuns
|
|
1008
1039
|
};
|
|
1009
|
-
//# sourceMappingURL=chunk-
|
|
1040
|
+
//# sourceMappingURL=chunk-NACAGYSY.js.map
|