@tangle-network/agent-eval 0.119.0 → 0.120.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 +25 -3
- package/README.md +0 -26
- package/dist/analyst/index.d.ts +30 -39
- package/dist/analyst/index.js +9 -5
- package/dist/analyst/index.js.map +1 -1
- package/dist/benchmarks/index.d.ts +11 -5
- package/dist/benchmarks/index.js +7 -7
- package/dist/campaign/index.d.ts +121 -66
- package/dist/campaign/index.js +26 -10
- package/dist/{chunk-RLCJQ6VZ.js → chunk-32BZXMSO.js} +385 -120
- package/dist/chunk-32BZXMSO.js.map +1 -0
- package/dist/{chunk-XJ7JVCHB.js → chunk-3A246TSA.js} +2 -2
- package/dist/{chunk-LMJ2TGWJ.js → chunk-JM2SKQMS.js} +2 -2
- package/dist/{chunk-F6YUH3L4.js → chunk-JN2FCO5W.js} +14 -168
- package/dist/chunk-JN2FCO5W.js.map +1 -0
- package/dist/{chunk-D7AEXSM5.js → chunk-PICTDURQ.js} +2 -2
- package/dist/{chunk-6QIM2EAP.js → chunk-PXD6ZFNY.js} +99 -1
- package/dist/chunk-PXD6ZFNY.js.map +1 -0
- package/dist/{chunk-GERDAIAL.js → chunk-QWMPPZ3X.js} +3 -3
- package/dist/{chunk-7A4LIMMY.js → chunk-S5TT5R3L.js} +229 -97
- package/dist/chunk-S5TT5R3L.js.map +1 -0
- package/dist/{chunk-ZYHJNKI3.js → chunk-ULOKLHIQ.js} +42 -5
- package/dist/chunk-ULOKLHIQ.js.map +1 -0
- package/dist/{chunk-FIUFRXP3.js → chunk-WW2A73HW.js} +49 -91
- package/dist/chunk-WW2A73HW.js.map +1 -0
- package/dist/{chunk-PAHNGS65.js → chunk-XDIRG3TO.js} +175 -11
- package/dist/chunk-XDIRG3TO.js.map +1 -0
- package/dist/contract/index.d.ts +76 -43
- package/dist/contract/index.js +596 -32
- package/dist/contract/index.js.map +1 -1
- package/dist/index.d.ts +92 -75
- package/dist/index.js +38 -35
- package/dist/index.js.map +1 -1
- package/dist/openapi.json +1 -1
- package/dist/rl.d.ts +11 -5
- package/dist/{run-campaign-OBXSN5WK.js → run-campaign-HNFPJET4.js} +2 -2
- package/dist/traces.d.ts +39 -25
- package/dist/traces.js +3 -5
- package/docs/trace-analysis.md +6 -2
- package/package.json +2 -7
- package/dist/chunk-6QIM2EAP.js.map +0 -1
- package/dist/chunk-7A4LIMMY.js.map +0 -1
- package/dist/chunk-F6YUH3L4.js.map +0 -1
- package/dist/chunk-FIUFRXP3.js.map +0 -1
- package/dist/chunk-PAHNGS65.js.map +0 -1
- package/dist/chunk-RLCJQ6VZ.js.map +0 -1
- package/dist/chunk-ZYHJNKI3.js.map +0 -1
- package/dist/primeintellect/index.d.ts +0 -311
- package/dist/primeintellect/index.js +0 -348
- package/dist/primeintellect/index.js.map +0 -1
- /package/dist/{chunk-XJ7JVCHB.js.map → chunk-3A246TSA.js.map} +0 -0
- /package/dist/{chunk-LMJ2TGWJ.js.map → chunk-JM2SKQMS.js.map} +0 -0
- /package/dist/{chunk-D7AEXSM5.js.map → chunk-PICTDURQ.js.map} +0 -0
- /package/dist/{chunk-GERDAIAL.js.map → chunk-QWMPPZ3X.js.map} +0 -0
- /package/dist/{run-campaign-OBXSN5WK.js.map → run-campaign-HNFPJET4.js.map} +0 -0
package/dist/contract/index.js
CHANGED
|
@@ -13,12 +13,15 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
analyzeRuns,
|
|
15
15
|
summarizeExecution
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-3A246TSA.js";
|
|
17
17
|
import {
|
|
18
18
|
REFERENCE_EQUIVALENCE_INPUT_LIMITS,
|
|
19
19
|
REFERENCE_EQUIVALENCE_JUDGE_VERSION,
|
|
20
20
|
buildEvidenceVector,
|
|
21
|
+
buildLoopProvenanceRecord,
|
|
22
|
+
campaignBreakdown,
|
|
21
23
|
campaignMeanComposite,
|
|
24
|
+
canonicalDigest,
|
|
22
25
|
composeGate,
|
|
23
26
|
createReferenceEquivalenceJudge,
|
|
24
27
|
defaultProductionGate,
|
|
@@ -26,28 +29,36 @@ import {
|
|
|
26
29
|
evolutionaryProposer,
|
|
27
30
|
gepaProposer,
|
|
28
31
|
heldOutGate,
|
|
32
|
+
loopProvenanceArgsFromResult,
|
|
33
|
+
pairArms,
|
|
29
34
|
paretoPolicy,
|
|
30
35
|
paretoSignificanceGate,
|
|
31
36
|
powerPreflight,
|
|
37
|
+
renderSurfaceDiff,
|
|
32
38
|
runEval,
|
|
33
39
|
runImprovementLoop,
|
|
34
40
|
runReferenceEquivalenceJudge,
|
|
35
41
|
surfaceContentHash,
|
|
36
|
-
surfaceHash
|
|
37
|
-
|
|
42
|
+
surfaceHash,
|
|
43
|
+
verifyLoopProvenanceRecord
|
|
44
|
+
} from "../chunk-32BZXMSO.js";
|
|
38
45
|
import {
|
|
46
|
+
assertCampaignSplitIdentity,
|
|
47
|
+
assertRealAgentReceipts,
|
|
48
|
+
campaignCoverage,
|
|
49
|
+
campaignSplitDigest,
|
|
39
50
|
createRunCostLedger,
|
|
40
51
|
fsCampaignStorage,
|
|
41
52
|
inMemoryCampaignStorage,
|
|
42
53
|
resolveRunDir,
|
|
43
54
|
runCampaign
|
|
44
|
-
} from "../chunk-
|
|
55
|
+
} from "../chunk-XDIRG3TO.js";
|
|
45
56
|
import {
|
|
46
57
|
buildDefaultAnalystRegistry,
|
|
47
58
|
createChatClient
|
|
48
|
-
} from "../chunk-
|
|
59
|
+
} from "../chunk-QWMPPZ3X.js";
|
|
49
60
|
import "../chunk-HHWE3POT.js";
|
|
50
|
-
import "../chunk-
|
|
61
|
+
import "../chunk-S5TT5R3L.js";
|
|
51
62
|
import {
|
|
52
63
|
FileSystemOutcomeStore,
|
|
53
64
|
InMemoryOutcomeStore
|
|
@@ -55,8 +66,12 @@ import {
|
|
|
55
66
|
import "../chunk-ARU2PZFM.js";
|
|
56
67
|
import "../chunk-NJC7U437.js";
|
|
57
68
|
import "../chunk-DPZAEKA6.js";
|
|
58
|
-
import
|
|
59
|
-
|
|
69
|
+
import {
|
|
70
|
+
pairedBootstrap
|
|
71
|
+
} from "../chunk-PJQFMIOX.js";
|
|
72
|
+
import {
|
|
73
|
+
CostLedger
|
|
74
|
+
} from "../chunk-JHOJHHU7.js";
|
|
60
75
|
import "../chunk-VI2UW6B6.js";
|
|
61
76
|
import {
|
|
62
77
|
recordAggregateMeasurements,
|
|
@@ -65,7 +80,7 @@ import {
|
|
|
65
80
|
import "../chunk-PXE2VKMX.js";
|
|
66
81
|
import "../chunk-ZET2UAYW.js";
|
|
67
82
|
import "../chunk-GGE4NNQT.js";
|
|
68
|
-
import "../chunk-
|
|
83
|
+
import "../chunk-PXD6ZFNY.js";
|
|
69
84
|
import "../chunk-PC4UYEBM.js";
|
|
70
85
|
import {
|
|
71
86
|
parseRunRecordSafe
|
|
@@ -249,29 +264,17 @@ async function runSelfImprove(opts, costLedger, startedAt, runDir, storage) {
|
|
|
249
264
|
candidateCandidateId: "winner"
|
|
250
265
|
});
|
|
251
266
|
const durationMs = Date.now() - startedAt;
|
|
252
|
-
const workerRecords = opts.collectWorkerRecords?.() ?? cellsToRunRecords(result.winnerOnHoldout.cells, "winner", runDir, result.winnerSurface);
|
|
253
267
|
const { record: provenance } = await emitLoopProvenance({
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
generationIndex: g.record.generationIndex,
|
|
265
|
-
candidates: g.record.candidates,
|
|
266
|
-
promoted: g.record.promoted,
|
|
267
|
-
surfaces: g.surfaces.map((s) => ({ surfaceHash: s.surfaceHash, surface: s.surface }))
|
|
268
|
-
})),
|
|
269
|
-
gate: result.gateResult,
|
|
270
|
-
baselineOnHoldout: result.baselineOnHoldout,
|
|
271
|
-
winnerOnHoldout: result.winnerOnHoldout,
|
|
272
|
-
workerRecords,
|
|
273
|
-
totalCostUsd: totalCost,
|
|
274
|
-
totalDurationMs: durationMs,
|
|
268
|
+
...loopProvenanceArgsFromResult({
|
|
269
|
+
runId: `${runDir}#${startedAt}`,
|
|
270
|
+
runDir,
|
|
271
|
+
timestamp: new Date(startedAt).toISOString(),
|
|
272
|
+
baselineSurface: opts.baselineSurface,
|
|
273
|
+
result,
|
|
274
|
+
costReceipts: costLedger.list(),
|
|
275
|
+
totalCostUsd: totalCost,
|
|
276
|
+
totalDurationMs: durationMs
|
|
277
|
+
}),
|
|
275
278
|
storage,
|
|
276
279
|
hostedClient: opts.hostedTenant ? createHostedClient(opts.hostedTenant) : void 0
|
|
277
280
|
});
|
|
@@ -419,7 +422,13 @@ function cellsToRunRecords(cells, candidateId, runId, surface) {
|
|
|
419
422
|
commitSha: "cell",
|
|
420
423
|
wallMs: cell.durationMs,
|
|
421
424
|
costUsd: cell.costUsd,
|
|
422
|
-
tokenUsage: {
|
|
425
|
+
tokenUsage: {
|
|
426
|
+
input: cell.tokenUsage.input,
|
|
427
|
+
output: cell.tokenUsage.output,
|
|
428
|
+
...cell.tokenUsage.reasoning === void 0 ? {} : { reasoning: cell.tokenUsage.reasoning },
|
|
429
|
+
...cell.tokenUsage.cached === void 0 ? {} : { cached: cell.tokenUsage.cached },
|
|
430
|
+
...cell.tokenUsage.cacheWrite === void 0 ? {} : { cacheWrite: cell.tokenUsage.cacheWrite }
|
|
431
|
+
},
|
|
423
432
|
outcome: {
|
|
424
433
|
holdoutScore: composite,
|
|
425
434
|
raw: {},
|
|
@@ -536,6 +545,559 @@ function requirePositiveInteger(value, field) {
|
|
|
536
545
|
return value;
|
|
537
546
|
}
|
|
538
547
|
|
|
548
|
+
// src/contract/measured-comparison.ts
|
|
549
|
+
import {
|
|
550
|
+
agentImprovementMeasuredComparisonSchema
|
|
551
|
+
} from "@tangle-network/agent-interface";
|
|
552
|
+
function measuredComparisonFromSelfImproveResult(options) {
|
|
553
|
+
const { result } = options;
|
|
554
|
+
verifyLoopProvenanceRecord(result.provenance);
|
|
555
|
+
const power = result.power;
|
|
556
|
+
if (!power) throw new Error("agent improvement comparison requires heldout power analysis");
|
|
557
|
+
if (result.provenance.gate.reasons.length === 0) {
|
|
558
|
+
throw new Error("agent improvement comparison requires measured decision reasons");
|
|
559
|
+
}
|
|
560
|
+
const receiptLedger = new CostLedger({ receipts: result.receipts });
|
|
561
|
+
const receiptCost = receiptLedger.summary();
|
|
562
|
+
const receipts = receiptLedger.list();
|
|
563
|
+
assertRealAgentReceipts(receipts, { allowMixed: false });
|
|
564
|
+
const receiptsById = new Map(receipts.map((receipt) => [receipt.callId, receipt]));
|
|
565
|
+
const receiptIdsByCell = indexReceiptIdsByCell(receipts);
|
|
566
|
+
assertCompleteMeasuredCampaign(
|
|
567
|
+
result.raw.baselineOnHoldout,
|
|
568
|
+
"heldout baseline",
|
|
569
|
+
receiptsById,
|
|
570
|
+
receiptIdsByCell
|
|
571
|
+
);
|
|
572
|
+
assertCompleteMeasuredCampaign(
|
|
573
|
+
result.raw.winnerOnHoldout,
|
|
574
|
+
"heldout candidate",
|
|
575
|
+
receiptsById,
|
|
576
|
+
receiptIdsByCell
|
|
577
|
+
);
|
|
578
|
+
const pairs = pairMeasuredCells(
|
|
579
|
+
result.raw.baselineOnHoldout.cells,
|
|
580
|
+
result.raw.winnerOnHoldout.cells
|
|
581
|
+
);
|
|
582
|
+
const composite = measuredObjective(
|
|
583
|
+
{
|
|
584
|
+
kind: "objective",
|
|
585
|
+
name: "composite",
|
|
586
|
+
direction: "higher-is-better",
|
|
587
|
+
unit: "score"
|
|
588
|
+
},
|
|
589
|
+
pairs,
|
|
590
|
+
measuredComposite
|
|
591
|
+
);
|
|
592
|
+
assertMeasuredNumber(result.lift, composite.delta, "heldout lift");
|
|
593
|
+
assertMeasuredNumber(result.baseline.compositeMean, composite.baseline, "heldout baseline");
|
|
594
|
+
assertMeasuredNumber(result.winner.compositeMean, composite.candidate, "heldout candidate");
|
|
595
|
+
const baselineContentHash = surfaceContentHash(options.baselineSurface);
|
|
596
|
+
const candidateContentHash = surfaceContentHash(result.winner.surface);
|
|
597
|
+
const canonicalDiff = baselineContentHash === candidateContentHash ? "" : renderSurfaceDiff(result.raw.winnerSurface, options.baselineSurface);
|
|
598
|
+
assertMeasuredIdentity(result.gateDecision, result.raw.gateResult.decision, "raw decision");
|
|
599
|
+
assertMeasuredIdentity(
|
|
600
|
+
candidateContentHash,
|
|
601
|
+
surfaceContentHash(result.raw.winnerSurface),
|
|
602
|
+
"raw winner surface"
|
|
603
|
+
);
|
|
604
|
+
assertMeasuredIdentity(result.diff, canonicalDiff, "surface diff");
|
|
605
|
+
assertMeasuredIdentity(result.raw.promotedDiff, canonicalDiff, "raw surface diff");
|
|
606
|
+
if (result.gateDecision === "ship" && (baselineContentHash === candidateContentHash || result.diff.trim().length === 0)) {
|
|
607
|
+
throw new Error("a shipped improvement requires a changed surface and non-empty diff");
|
|
608
|
+
}
|
|
609
|
+
assertMeasuredIdentity(
|
|
610
|
+
canonicalDigest(power),
|
|
611
|
+
canonicalDigest(
|
|
612
|
+
powerPreflight({
|
|
613
|
+
baselineComposites: pairs.map(([cell]) => measuredComposite(cell)),
|
|
614
|
+
sharedScorerChannel: true
|
|
615
|
+
})
|
|
616
|
+
),
|
|
617
|
+
"power analysis"
|
|
618
|
+
);
|
|
619
|
+
assertMeasuredNumber(power.n, composite.n, "power sample size");
|
|
620
|
+
assertMeasuredNumber(power.confidence, 0.95, "power confidence");
|
|
621
|
+
assertCompleteMeasuredCampaign(
|
|
622
|
+
result.raw.baselineCampaign,
|
|
623
|
+
"search baseline",
|
|
624
|
+
receiptsById,
|
|
625
|
+
receiptIdsByCell
|
|
626
|
+
);
|
|
627
|
+
assertGenerationMeasurements(result.raw.generations, receiptsById, receiptIdsByCell);
|
|
628
|
+
if (result.raw.neutralizedOnHoldout === void 0 !== (result.raw.neutralizedSurface === void 0)) {
|
|
629
|
+
throw new Error("neutralized surface and campaign must be supplied together");
|
|
630
|
+
}
|
|
631
|
+
if (result.raw.neutralizedOnHoldout) {
|
|
632
|
+
assertCompleteMeasuredCampaign(
|
|
633
|
+
result.raw.neutralizedOnHoldout,
|
|
634
|
+
"heldout neutralized",
|
|
635
|
+
receiptsById,
|
|
636
|
+
receiptIdsByCell
|
|
637
|
+
);
|
|
638
|
+
pairMeasuredCells(result.raw.baselineOnHoldout.cells, result.raw.neutralizedOnHoldout.cells);
|
|
639
|
+
}
|
|
640
|
+
const rebuiltProvenance = buildLoopProvenanceRecord(
|
|
641
|
+
loopProvenanceArgsFromResult({
|
|
642
|
+
runId: result.provenance.runId,
|
|
643
|
+
runDir: result.provenance.runDir,
|
|
644
|
+
timestamp: result.provenance.timestamp,
|
|
645
|
+
baselineSurface: options.baselineSurface,
|
|
646
|
+
result: result.raw,
|
|
647
|
+
costReceipts: receipts,
|
|
648
|
+
totalCostUsd: result.totalCostUsd,
|
|
649
|
+
totalDurationMs: result.durationMs
|
|
650
|
+
})
|
|
651
|
+
);
|
|
652
|
+
assertMeasuredIdentity(
|
|
653
|
+
result.provenance.recordDigest,
|
|
654
|
+
rebuiltProvenance.recordDigest,
|
|
655
|
+
"provenance record"
|
|
656
|
+
);
|
|
657
|
+
assertMeasuredIdentity(
|
|
658
|
+
options.benchmark.splitDigest,
|
|
659
|
+
rebuiltProvenance.evidence.holdout.splitDigest,
|
|
660
|
+
"benchmark heldout split"
|
|
661
|
+
);
|
|
662
|
+
const generationsExplored = result.raw.generations.length;
|
|
663
|
+
assertMeasuredNumber(result.generationsExplored, generationsExplored, "generation count");
|
|
664
|
+
assertMeasuredNumber(result.totalCostUsd, result.cost.totalCostUsd, "cost summary");
|
|
665
|
+
assertMeasuredIdentity(
|
|
666
|
+
canonicalDigest(result.cost),
|
|
667
|
+
canonicalDigest(result.raw.cost),
|
|
668
|
+
"raw cost summary"
|
|
669
|
+
);
|
|
670
|
+
if (!result.cost.accountingComplete) {
|
|
671
|
+
throw new Error(
|
|
672
|
+
`cost accounting is incomplete: ${result.cost.incompleteReasons.join("; ") || "unknown reason"}`
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
if (!receiptCost.accountingComplete) {
|
|
676
|
+
throw new Error(
|
|
677
|
+
`cost accounting is incomplete: ${receiptCost.incompleteReasons.join("; ") || "unknown reason"}`
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
assertMeasuredIdentity(
|
|
681
|
+
canonicalDigest(result.cost),
|
|
682
|
+
canonicalDigest(receiptCost),
|
|
683
|
+
"cost receipt summary"
|
|
684
|
+
);
|
|
685
|
+
assertMeasuredNumber(result.totalCostUsd, receiptCost.totalCostUsd, "cost receipts");
|
|
686
|
+
assertMeasuredOptional(result.winner.label, result.raw.winnerLabel, "raw winner label");
|
|
687
|
+
assertMeasuredOptional(
|
|
688
|
+
result.winner.rationale,
|
|
689
|
+
result.raw.winnerRationale,
|
|
690
|
+
"raw winner rationale"
|
|
691
|
+
);
|
|
692
|
+
return agentImprovementMeasuredComparisonSchema.parse({
|
|
693
|
+
schemaVersion: 1,
|
|
694
|
+
kind: "agent-improvement-measured-comparison",
|
|
695
|
+
benchmark: options.benchmark,
|
|
696
|
+
baselineProfileDigest: options.baselineProfileDigest,
|
|
697
|
+
candidateBundleDigest: options.candidateBundleDigest,
|
|
698
|
+
overall: {
|
|
699
|
+
name: "composite",
|
|
700
|
+
baseline: composite.baseline,
|
|
701
|
+
candidate: composite.candidate,
|
|
702
|
+
delta: composite.delta,
|
|
703
|
+
confidenceInterval: composite.confidenceInterval,
|
|
704
|
+
n: composite.n,
|
|
705
|
+
direction: "higher-is-better",
|
|
706
|
+
unit: "score"
|
|
707
|
+
},
|
|
708
|
+
objectives: measuredObjectives(pairs),
|
|
709
|
+
...result.winner.label || result.winner.rationale ? {
|
|
710
|
+
candidate: {
|
|
711
|
+
...result.winner.label ? { label: result.winner.label } : {},
|
|
712
|
+
...result.winner.rationale ? { rationale: result.winner.rationale } : {}
|
|
713
|
+
}
|
|
714
|
+
} : {},
|
|
715
|
+
decision: {
|
|
716
|
+
outcome: result.gateDecision,
|
|
717
|
+
reasons: rebuiltProvenance.gate.reasons,
|
|
718
|
+
contributingChecks: rebuiltProvenance.gate.contributingGates.map((check) => ({
|
|
719
|
+
name: check.name,
|
|
720
|
+
passed: check.passed
|
|
721
|
+
}))
|
|
722
|
+
},
|
|
723
|
+
power: {
|
|
724
|
+
sufficient: power.scaleAssumed && !power.underpowered,
|
|
725
|
+
n: power.n,
|
|
726
|
+
minimumDetectableDelta: power.mde,
|
|
727
|
+
confidenceLevel: power.confidence,
|
|
728
|
+
scaleAssumed: power.scaleAssumed,
|
|
729
|
+
sharedScorerChannel: power.sharedChannelCaveat !== void 0,
|
|
730
|
+
reason: power.recommendation
|
|
731
|
+
},
|
|
732
|
+
provenance: {
|
|
733
|
+
kind: "agent-eval-loop",
|
|
734
|
+
schema: rebuiltProvenance.schema,
|
|
735
|
+
runId: rebuiltProvenance.runId,
|
|
736
|
+
recordDigest: rebuiltProvenance.recordDigest,
|
|
737
|
+
baselineContentHash,
|
|
738
|
+
candidateContentHash
|
|
739
|
+
},
|
|
740
|
+
diff: canonicalDiff,
|
|
741
|
+
evaluation: {
|
|
742
|
+
generationsExplored,
|
|
743
|
+
durationMs: result.durationMs,
|
|
744
|
+
totalCostUsd: result.totalCostUsd
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
function assertGenerationMeasurements(generations, receiptsById, receiptIdsByCell) {
|
|
749
|
+
for (const generation of generations) {
|
|
750
|
+
for (const measured of generation.surfaces) {
|
|
751
|
+
const candidate = generation.record.candidates.find(
|
|
752
|
+
(entry) => entry.surfaceHash === measured.surfaceHash
|
|
753
|
+
);
|
|
754
|
+
if (!candidate) {
|
|
755
|
+
throw new Error(
|
|
756
|
+
`generation ${generation.record.generationIndex} is missing candidate ${measured.surfaceHash}`
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
const composite = campaignMeanComposite(measured.campaign);
|
|
760
|
+
const breakdown = campaignBreakdown(measured.campaign);
|
|
761
|
+
const coverage = campaignCoverage(
|
|
762
|
+
measured.campaign.cells,
|
|
763
|
+
measured.campaign.scenarios,
|
|
764
|
+
measured.campaign.reps,
|
|
765
|
+
true
|
|
766
|
+
);
|
|
767
|
+
assertScorableMeasuredCells(
|
|
768
|
+
measured.campaign,
|
|
769
|
+
coverage.scorableCellIds,
|
|
770
|
+
receiptsById,
|
|
771
|
+
receiptIdsByCell
|
|
772
|
+
);
|
|
773
|
+
assertMeasuredNumber(
|
|
774
|
+
candidate.composite,
|
|
775
|
+
composite,
|
|
776
|
+
`candidate ${measured.surfaceHash} composite`
|
|
777
|
+
);
|
|
778
|
+
if (!candidate.coverage) {
|
|
779
|
+
throw new Error(`candidate ${measured.surfaceHash} does not report campaign coverage`);
|
|
780
|
+
}
|
|
781
|
+
assertMeasuredIdentity(
|
|
782
|
+
canonicalDigest(candidate.coverage),
|
|
783
|
+
canonicalDigest({
|
|
784
|
+
expectedCells: coverage.expectedCellIds.length,
|
|
785
|
+
scorableCells: coverage.scorableCellIds.length,
|
|
786
|
+
unscorableCells: coverage.unscorableCells
|
|
787
|
+
}),
|
|
788
|
+
`candidate ${measured.surfaceHash} coverage`
|
|
789
|
+
);
|
|
790
|
+
assertMeasuredIdentity(
|
|
791
|
+
String(candidate.eligibleForPromotion),
|
|
792
|
+
String(coverage.complete),
|
|
793
|
+
`candidate ${measured.surfaceHash} eligibility`
|
|
794
|
+
);
|
|
795
|
+
assertMeasuredIdentity(
|
|
796
|
+
canonicalDigest(candidate.dimensions),
|
|
797
|
+
canonicalDigest(breakdown.dimensions),
|
|
798
|
+
`candidate ${measured.surfaceHash} dimensions`
|
|
799
|
+
);
|
|
800
|
+
assertMeasuredIdentity(
|
|
801
|
+
canonicalDigest(candidate.scenarios),
|
|
802
|
+
canonicalDigest(breakdown.scenarios),
|
|
803
|
+
`candidate ${measured.surfaceHash} scenarios`
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function assertCompleteMeasuredCampaign(campaign, name, receiptsById, receiptIdsByCell) {
|
|
809
|
+
assertCampaignSplitIdentity(campaign.scenarios, campaign.reps, campaign.splitDigest);
|
|
810
|
+
const coverage = campaignCoverage(campaign.cells, campaign.scenarios, campaign.reps, true);
|
|
811
|
+
if (!coverage.complete) {
|
|
812
|
+
throw new Error(
|
|
813
|
+
`${name} is incomplete (${coverage.scorableCellIds.length}/${coverage.expectedCellIds.length} designed cells scorable)`
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
assertScorableMeasuredCells(campaign, coverage.scorableCellIds, receiptsById, receiptIdsByCell);
|
|
817
|
+
}
|
|
818
|
+
function assertScorableMeasuredCells(campaign, scorableCellIds, receiptsById, receiptIdsByCell) {
|
|
819
|
+
const cellsById = new Map(campaign.cells.map((cell) => [cell.cellId, cell]));
|
|
820
|
+
for (const cellId of scorableCellIds) {
|
|
821
|
+
const cell = cellsById.get(cellId);
|
|
822
|
+
if (!cell) throw new Error(`measured campaign is missing scorable cell ${cellId}`);
|
|
823
|
+
assertMeasuredCell(cell, receiptsById, receiptIdsByCell, campaign.runDir);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
function measuredObjectives(pairs) {
|
|
827
|
+
const qualityColumns = /* @__PURE__ */ new Map();
|
|
828
|
+
for (const [baseline, candidate] of pairs) {
|
|
829
|
+
for (const cell of [baseline, candidate]) {
|
|
830
|
+
for (const [objective, score] of Object.entries(cell.judgeScores)) {
|
|
831
|
+
if (score.failed) continue;
|
|
832
|
+
qualityColumns.set(`objective:${objective}`, {
|
|
833
|
+
kind: "objective",
|
|
834
|
+
name: objective,
|
|
835
|
+
direction: "higher-is-better",
|
|
836
|
+
unit: "score"
|
|
837
|
+
});
|
|
838
|
+
for (const name of Object.keys(score.dimensions)) {
|
|
839
|
+
qualityColumns.set(`dimension:${objective}:${name}`, {
|
|
840
|
+
kind: "dimension",
|
|
841
|
+
objective,
|
|
842
|
+
name,
|
|
843
|
+
direction: "higher-is-better",
|
|
844
|
+
unit: "score"
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return [
|
|
851
|
+
...[...qualityColumns.entries()].sort(([left], [right]) => left.localeCompare(right)).map(
|
|
852
|
+
([, column]) => measuredObjective(column, pairs, (cell) => measuredQuality(cell, column))
|
|
853
|
+
),
|
|
854
|
+
measuredCostObjective(pairs),
|
|
855
|
+
measuredObjective(
|
|
856
|
+
{
|
|
857
|
+
kind: "latency",
|
|
858
|
+
name: "latency",
|
|
859
|
+
direction: "lower-is-better",
|
|
860
|
+
unit: "milliseconds"
|
|
861
|
+
},
|
|
862
|
+
pairs,
|
|
863
|
+
(cell) => cell.durationMs
|
|
864
|
+
)
|
|
865
|
+
];
|
|
866
|
+
}
|
|
867
|
+
function measuredCostObjective(pairs) {
|
|
868
|
+
const cells = pairs.flat();
|
|
869
|
+
for (const cell of cells) finiteMeasuredValue(cell.costUsd, "cost:cost");
|
|
870
|
+
return measuredObjective(
|
|
871
|
+
{
|
|
872
|
+
kind: "cost",
|
|
873
|
+
name: "cost",
|
|
874
|
+
direction: "lower-is-better",
|
|
875
|
+
unit: "usd"
|
|
876
|
+
},
|
|
877
|
+
pairs,
|
|
878
|
+
(cell) => cell.costUsd
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
function measuredComposite(cell) {
|
|
882
|
+
const values = Object.values(cell.judgeScores).filter((score) => !score.failed).map((score) => score.composite).filter(Number.isFinite);
|
|
883
|
+
if (values.length === 0) {
|
|
884
|
+
throw new Error(`heldout cell ${measuredCellKey(cell)} has no successful composite score`);
|
|
885
|
+
}
|
|
886
|
+
return measuredMean(values);
|
|
887
|
+
}
|
|
888
|
+
function pairMeasuredCells(baselineCells, candidateCells) {
|
|
889
|
+
const cells = [...baselineCells, ...candidateCells];
|
|
890
|
+
const errors = cells.filter((cell) => cell.error);
|
|
891
|
+
if (errors.length > 0) {
|
|
892
|
+
throw new Error(
|
|
893
|
+
`measured objectives cannot publish ${errors.length} errored heldout cells: ${errors.map((cell) => measuredCellKey(cell)).join(", ")}`
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
const rows = [
|
|
897
|
+
...baselineCells.map((cell) => ({
|
|
898
|
+
pairKey: cell.scenarioId,
|
|
899
|
+
repKey: String(cell.rep),
|
|
900
|
+
arm: "baseline",
|
|
901
|
+
cell
|
|
902
|
+
})),
|
|
903
|
+
...candidateCells.map((cell) => ({
|
|
904
|
+
pairKey: cell.scenarioId,
|
|
905
|
+
repKey: String(cell.rep),
|
|
906
|
+
arm: "candidate",
|
|
907
|
+
cell
|
|
908
|
+
}))
|
|
909
|
+
];
|
|
910
|
+
const paired = pairArms(rows, { baselineArm: "baseline", treatmentArm: "candidate" });
|
|
911
|
+
if (paired.pairs.length === 0 || paired.unpairedBaseline.length > 0 || paired.unpairedTreatment.length > 0) {
|
|
912
|
+
throw new Error("measured objectives require the same non-empty paired heldout cells");
|
|
913
|
+
}
|
|
914
|
+
return paired.pairs.map((pair) => {
|
|
915
|
+
const baseline = pair.baseline.cell;
|
|
916
|
+
const candidate = pair.treatment.cell;
|
|
917
|
+
if (baseline.seed !== candidate.seed) {
|
|
918
|
+
throw new Error(`heldout cell ${baseline.cellId} does not share one paired seed`);
|
|
919
|
+
}
|
|
920
|
+
return [baseline, candidate];
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
function assertMeasuredCell(cell, receiptsById, receiptIdsByCell, runDir) {
|
|
924
|
+
if (!cell.scenarioId.trim() || !Number.isSafeInteger(cell.rep) || cell.rep < 0 || !Number.isSafeInteger(cell.seed) || cell.cellId !== measuredCellKey(cell)) {
|
|
925
|
+
throw new Error(`heldout cell '${cell.cellId}' has an invalid scenario/rep identity`);
|
|
926
|
+
}
|
|
927
|
+
nonnegativeMeasuredValue(cell.costUsd, `heldout cell ${cell.cellId} cost`);
|
|
928
|
+
nonnegativeMeasuredValue(cell.durationMs, `heldout cell ${cell.cellId} latency`);
|
|
929
|
+
if (!cell.tokenUsage) {
|
|
930
|
+
throw new Error(`heldout cell ${cell.cellId} does not report token usage`);
|
|
931
|
+
}
|
|
932
|
+
nonnegativeMeasuredInteger(cell.tokenUsage.input, `heldout cell ${cell.cellId} input tokens`);
|
|
933
|
+
nonnegativeMeasuredInteger(cell.tokenUsage.output, `heldout cell ${cell.cellId} output tokens`);
|
|
934
|
+
if (cell.tokenUsage.cached !== void 0) {
|
|
935
|
+
nonnegativeMeasuredInteger(cell.tokenUsage.cached, `heldout cell ${cell.cellId} cached tokens`);
|
|
936
|
+
}
|
|
937
|
+
if (!Array.isArray(cell.costCallIds)) {
|
|
938
|
+
throw new Error(`heldout cell ${cell.cellId} does not identify its cost receipts`);
|
|
939
|
+
}
|
|
940
|
+
if (new Set(cell.costCallIds).size !== cell.costCallIds.length) {
|
|
941
|
+
throw new Error(`heldout cell ${cell.cellId} repeats a cost receipt`);
|
|
942
|
+
}
|
|
943
|
+
const linkedReceipts = cell.costCallIds.map((callId) => {
|
|
944
|
+
const receipt = receiptsById.get(callId);
|
|
945
|
+
if (!receipt)
|
|
946
|
+
throw new Error(`heldout cell ${cell.cellId} references missing cost receipt ${callId}`);
|
|
947
|
+
if (receipt.tags?.runDir !== runDir || receipt.tags.cellId !== cell.cellId || receipt.tags.scenarioId !== cell.scenarioId || receipt.tags.rep !== String(cell.rep)) {
|
|
948
|
+
throw new Error(`heldout cell ${cell.cellId} references a cost receipt from another cell`);
|
|
949
|
+
}
|
|
950
|
+
return receipt;
|
|
951
|
+
});
|
|
952
|
+
assertMeasuredIdentity(
|
|
953
|
+
canonicalDigest([...cell.costCallIds].sort()),
|
|
954
|
+
canonicalDigest(
|
|
955
|
+
[
|
|
956
|
+
...receiptIdsByCell.get(receiptCellKey(runDir, cell.cellId, cell.scenarioId, cell.rep)) ?? []
|
|
957
|
+
].sort()
|
|
958
|
+
),
|
|
959
|
+
`heldout cell ${cell.cellId} cost receipt IDs`
|
|
960
|
+
);
|
|
961
|
+
const agentReceipts = linkedReceipts.filter((receipt) => receipt.channel === "agent");
|
|
962
|
+
if (agentReceipts.some((receipt) => receipt.error)) {
|
|
963
|
+
throw new Error(`measured cell ${cell.cellId} links a failed agent receipt`);
|
|
964
|
+
}
|
|
965
|
+
assertRealAgentReceipts(agentReceipts, { allowMixed: false });
|
|
966
|
+
assertMeasuredNumber(
|
|
967
|
+
cell.costUsd,
|
|
968
|
+
agentReceipts.reduce((total, receipt) => total + receipt.costUsd, 0),
|
|
969
|
+
`heldout cell ${cell.cellId} cost receipts`
|
|
970
|
+
);
|
|
971
|
+
assertMeasuredNumber(
|
|
972
|
+
cell.tokenUsage.input,
|
|
973
|
+
agentReceipts.reduce((total, receipt) => total + receipt.inputTokens, 0),
|
|
974
|
+
`heldout cell ${cell.cellId} input receipts`
|
|
975
|
+
);
|
|
976
|
+
assertMeasuredNumber(
|
|
977
|
+
cell.tokenUsage.output,
|
|
978
|
+
agentReceipts.reduce((total, receipt) => total + receipt.outputTokens, 0),
|
|
979
|
+
`heldout cell ${cell.cellId} output receipts`
|
|
980
|
+
);
|
|
981
|
+
assertMeasuredNumber(
|
|
982
|
+
cell.tokenUsage.cached ?? 0,
|
|
983
|
+
agentReceipts.reduce((total, receipt) => total + (receipt.cachedTokens ?? 0), 0),
|
|
984
|
+
`heldout cell ${cell.cellId} cached receipts`
|
|
985
|
+
);
|
|
986
|
+
for (const [judge, score] of Object.entries(cell.judgeScores)) {
|
|
987
|
+
if (score.failed) {
|
|
988
|
+
throw new Error(`heldout cell ${cell.cellId} contains failed judge '${judge}'`);
|
|
989
|
+
}
|
|
990
|
+
finiteMeasuredValue(score.composite, `objective:${judge}`);
|
|
991
|
+
for (const [dimension, value] of Object.entries(score.dimensions)) {
|
|
992
|
+
finiteMeasuredValue(value, `dimension:${judge}:${dimension}`);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
measuredComposite(cell);
|
|
996
|
+
}
|
|
997
|
+
function measuredObjective(identity, pairs, value) {
|
|
998
|
+
const key = measuredObjectiveKey(identity);
|
|
999
|
+
const baseline = pairs.map(([cell]) => finiteMeasuredValue(value(cell), key));
|
|
1000
|
+
const candidate = pairs.map(([, cell]) => finiteMeasuredValue(value(cell), key));
|
|
1001
|
+
const interval = pairedBootstrap(baseline, candidate, {
|
|
1002
|
+
confidence: 0.95,
|
|
1003
|
+
resamples: 2e3,
|
|
1004
|
+
statistic: "mean",
|
|
1005
|
+
seed: measuredSeed(key)
|
|
1006
|
+
});
|
|
1007
|
+
const baselineMean = measuredMean(baseline);
|
|
1008
|
+
const candidateMean = measuredMean(candidate);
|
|
1009
|
+
return {
|
|
1010
|
+
...identity,
|
|
1011
|
+
availability: "measured",
|
|
1012
|
+
baseline: baselineMean,
|
|
1013
|
+
candidate: candidateMean,
|
|
1014
|
+
delta: candidateMean - baselineMean,
|
|
1015
|
+
confidenceInterval: {
|
|
1016
|
+
level: interval.confidence,
|
|
1017
|
+
lower: interval.low,
|
|
1018
|
+
upper: interval.high,
|
|
1019
|
+
method: "paired-bootstrap",
|
|
1020
|
+
statistic: "mean",
|
|
1021
|
+
resamples: interval.resamples
|
|
1022
|
+
},
|
|
1023
|
+
n: interval.n
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
function measuredQuality(cell, column) {
|
|
1027
|
+
const objective = column.kind === "objective" ? column.name : column.objective;
|
|
1028
|
+
const score = cell.judgeScores[objective];
|
|
1029
|
+
if (!score || score.failed) {
|
|
1030
|
+
throw new Error(
|
|
1031
|
+
`heldout cell ${measuredCellKey(cell)} is missing measured objective '${objective}'`
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
const value = column.kind === "objective" ? score.composite : score.dimensions[column.name];
|
|
1035
|
+
if (value === void 0) {
|
|
1036
|
+
throw new Error(
|
|
1037
|
+
`heldout cell ${measuredCellKey(cell)} is missing '${objective}' dimension '${column.name}'`
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
return finiteMeasuredValue(value, measuredObjectiveKey(column));
|
|
1041
|
+
}
|
|
1042
|
+
function measuredObjectiveKey(identity) {
|
|
1043
|
+
return identity.kind === "dimension" ? `${identity.kind}:${identity.objective}:${identity.name}` : `${identity.kind}:${identity.name}`;
|
|
1044
|
+
}
|
|
1045
|
+
function measuredCellKey(cell) {
|
|
1046
|
+
return `${cell.scenarioId}:${cell.rep}`;
|
|
1047
|
+
}
|
|
1048
|
+
function finiteMeasuredValue(value, name) {
|
|
1049
|
+
if (!Number.isFinite(value)) throw new Error(`measured objective '${name}' is not finite`);
|
|
1050
|
+
return value;
|
|
1051
|
+
}
|
|
1052
|
+
function nonnegativeMeasuredValue(value, name) {
|
|
1053
|
+
if (!Number.isFinite(value) || value < 0) throw new Error(`${name} must be non-negative`);
|
|
1054
|
+
return value;
|
|
1055
|
+
}
|
|
1056
|
+
function nonnegativeMeasuredInteger(value, name) {
|
|
1057
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
1058
|
+
throw new Error(`${name} must be a non-negative safe integer`);
|
|
1059
|
+
}
|
|
1060
|
+
return value;
|
|
1061
|
+
}
|
|
1062
|
+
function measuredMean(values) {
|
|
1063
|
+
if (values.length === 0) throw new Error("measured objective has no paired values");
|
|
1064
|
+
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
1065
|
+
}
|
|
1066
|
+
function measuredSeed(value) {
|
|
1067
|
+
let seed = 2166136261;
|
|
1068
|
+
for (const byte of Buffer.from(value, "utf8")) {
|
|
1069
|
+
seed = Math.imul(seed ^ byte, 16777619) >>> 0;
|
|
1070
|
+
}
|
|
1071
|
+
return seed;
|
|
1072
|
+
}
|
|
1073
|
+
function assertMeasuredNumber(actual, expected, name) {
|
|
1074
|
+
const tolerance = Number.EPSILON * Math.max(1, Math.abs(actual), Math.abs(expected)) * 8;
|
|
1075
|
+
if (!Number.isFinite(actual) || !Number.isFinite(expected) || Math.abs(actual - expected) > tolerance) {
|
|
1076
|
+
throw new Error(`${name} does not agree across the measured comparison`);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function assertMeasuredIdentity(actual, expected, name) {
|
|
1080
|
+
if (actual !== expected) throw new Error(`${name} does not agree across the measured comparison`);
|
|
1081
|
+
}
|
|
1082
|
+
function assertMeasuredOptional(actual, expected, name) {
|
|
1083
|
+
if (actual !== expected) throw new Error(`${name} does not agree across the measured comparison`);
|
|
1084
|
+
}
|
|
1085
|
+
function indexReceiptIdsByCell(receipts) {
|
|
1086
|
+
const idsByCell = /* @__PURE__ */ new Map();
|
|
1087
|
+
for (const receipt of receipts) {
|
|
1088
|
+
const tags = receipt.tags;
|
|
1089
|
+
if (!tags?.runDir || !tags.cellId || !tags.scenarioId || tags.rep === void 0) continue;
|
|
1090
|
+
const key = receiptCellKey(tags.runDir, tags.cellId, tags.scenarioId, tags.rep);
|
|
1091
|
+
const ids = idsByCell.get(key) ?? [];
|
|
1092
|
+
ids.push(receipt.callId);
|
|
1093
|
+
idsByCell.set(key, ids);
|
|
1094
|
+
}
|
|
1095
|
+
return idsByCell;
|
|
1096
|
+
}
|
|
1097
|
+
function receiptCellKey(runDir, cellId, scenarioId, rep) {
|
|
1098
|
+
return JSON.stringify([runDir, cellId, scenarioId, String(rep)]);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
539
1101
|
// src/contract/eval-reporting-suite.ts
|
|
540
1102
|
import { mkdir, writeFile } from "fs/promises";
|
|
541
1103
|
import { dirname, join as join2 } from "path";
|
|
@@ -1051,6 +1613,7 @@ export {
|
|
|
1051
1613
|
analyzeRuns,
|
|
1052
1614
|
buildDefaultAnalystRegistry,
|
|
1053
1615
|
buildEvidenceVector,
|
|
1616
|
+
campaignSplitDigest,
|
|
1054
1617
|
composeGate,
|
|
1055
1618
|
createChatClient,
|
|
1056
1619
|
createReferenceEquivalenceJudge,
|
|
@@ -1074,6 +1637,7 @@ export {
|
|
|
1074
1637
|
gepaProposer,
|
|
1075
1638
|
heldOutGate,
|
|
1076
1639
|
inMemoryCampaignStorage,
|
|
1640
|
+
measuredComparisonFromSelfImproveResult,
|
|
1077
1641
|
paretoPolicy,
|
|
1078
1642
|
paretoSignificanceGate,
|
|
1079
1643
|
parseAgentTrace,
|