@tangle-network/agent-eval 0.145.18 → 0.145.20
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 +41 -0
- package/dist/analyst/index.d.ts +1 -1
- package/dist/analyst/index.js +1 -1
- package/dist/{benchmark-command-CyqmP3uA.js → benchmark-command-CkgLXHoN.js} +2 -2
- package/dist/{benchmark-command-CyqmP3uA.js.map → benchmark-command-CkgLXHoN.js.map} +1 -1
- package/dist/cli.js +1 -1
- package/dist/index-D_P7Ye43.d.ts +198 -0
- package/dist/index-D_P7Ye43.d.ts.map +1 -0
- package/dist/matrix/index.d.ts +3 -2
- package/dist/matrix/index.js +2 -2
- package/dist/{matrix-BzQnu2S6.js → matrix-pWinRMPl.js} +148 -6
- package/dist/matrix-pWinRMPl.js.map +1 -0
- package/dist/multishot/index.d.ts +111 -48
- package/dist/multishot/index.d.ts.map +1 -1
- package/dist/multishot/index.js +245 -94
- package/dist/multishot/index.js.map +1 -1
- package/dist/openapi.json +1 -1
- package/dist/run-record-D2lDdSAz.js.map +1 -1
- package/dist/run-record-DVV82Gwh.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/index-CvjYbU0D.d.ts +0 -102
- package/dist/index-CvjYbU0D.d.ts.map +0 -1
- package/dist/matrix-BzQnu2S6.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { o as runRolloutReleaseCli } from "./hf-dataset-XggBupCr.js";
|
|
3
|
-
import { n as runAnalystBenchmarkCommand } from "./benchmark-command-
|
|
3
|
+
import { n as runAnalystBenchmarkCommand } from "./benchmark-command-CkgLXHoN.js";
|
|
4
4
|
import { a as runRpcBatch, o as runRpcOnce, p as handleVersion, r as startServerAsync, s as buildOpenApi } from "./server-ulsOdrTI.js";
|
|
5
5
|
import { writeFileSync } from "node:fs";
|
|
6
6
|
//#region src/cli-config.ts
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { t as DefaultVerdict } from "./verdict-E4eRNf7-.js";
|
|
2
|
+
import { p as CostProvenance } from "./cost-ledger-DbQdN3nO.js";
|
|
3
|
+
//#region src/matrix/cell-spend.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Spend carrier for a cell that fails after it has already spent money.
|
|
6
|
+
*
|
|
7
|
+
* `runCell` reports what a cell cost by returning a `CellResult`. A throw
|
|
8
|
+
* carries no result, so the runner cannot know what the cell spent before it
|
|
9
|
+
* failed: the cumulative sum the cost ceiling reads, and `totalCostUsd`, both
|
|
10
|
+
* miss that money. A throw that carries a `CellSpend` closes the gap — the
|
|
11
|
+
* runner bills the failed cell for the spend the carrier declares, and records
|
|
12
|
+
* a cell that declared nothing as `uncaptured` instead of a zero that reads
|
|
13
|
+
* like "this cell spent nothing".
|
|
14
|
+
*
|
|
15
|
+
* The carrier is read structurally, never by `instanceof`, so a throw that
|
|
16
|
+
* crosses a package or realm boundary still bills.
|
|
17
|
+
*/
|
|
18
|
+
/** Money and wall time a cell consumed before it threw. */
|
|
19
|
+
interface CellSpend {
|
|
20
|
+
/** KNOWN subtotal in USD. When `kind` is `uncaptured` the cell spent this
|
|
21
|
+
* much AND an unknown amount more, so the value must not be read as the
|
|
22
|
+
* cell's total. */
|
|
23
|
+
costUsd: number;
|
|
24
|
+
/** Wall time the cell consumed before the throw, in milliseconds. */
|
|
25
|
+
durationMs: number;
|
|
26
|
+
/** `observed` = provider-reported amounts, `estimated` = computed from token
|
|
27
|
+
* prices, `uncaptured` = `costUsd` is a subtotal and part of the spend
|
|
28
|
+
* could not be measured. */
|
|
29
|
+
kind: 'observed' | 'estimated' | 'uncaptured';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Return `error` carrying `spend`, for a throw site inside `runCell`:
|
|
33
|
+
* `throw withCellSpend(err, { costUsd, durationMs, kind })`.
|
|
34
|
+
*
|
|
35
|
+
* A value that cannot hold the carrier — a thrown string or number, or a
|
|
36
|
+
* frozen, sealed, or otherwise non-extensible object — is wrapped in an
|
|
37
|
+
* `Error` that keeps the original as `cause`, so the spend is never dropped.
|
|
38
|
+
* A later call overwrites an earlier carrier — the outer frame knows more
|
|
39
|
+
* than the inner one.
|
|
40
|
+
*
|
|
41
|
+
* Throws `TypeError` on a non-finite or negative amount. A poisoned amount
|
|
42
|
+
* would reach `cumulativeCost` and disable the cost ceiling for the whole run,
|
|
43
|
+
* because `NaN >= ceiling` is false.
|
|
44
|
+
*/
|
|
45
|
+
declare function withCellSpend(error: unknown, spend: CellSpend): unknown;
|
|
46
|
+
/** Read the spend a thrown value carries. `undefined` when the value carries
|
|
47
|
+
* none, or carries one that does not satisfy `CellSpend` — the runner records
|
|
48
|
+
* both as `uncaptured`. */
|
|
49
|
+
declare function readCellSpend(error: unknown): CellSpend | undefined;
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/matrix/types.d.ts
|
|
52
|
+
/** One axis = one dimension to iterate. `V` is the value type — pass any
|
|
53
|
+
* substrate type (AgentProfile, Driver, Validator, rubric record). */
|
|
54
|
+
interface MatrixAxis<V> {
|
|
55
|
+
/** Axis name. Becomes the key in `MatrixResult.byAxis`. */
|
|
56
|
+
name: string;
|
|
57
|
+
/** Stable id per value. Used as the bucket key in aggregation. */
|
|
58
|
+
values: Array<{
|
|
59
|
+
id: string;
|
|
60
|
+
value: V;
|
|
61
|
+
}>;
|
|
62
|
+
/** Optional bucket label override. Receives the same `(value, id)` the
|
|
63
|
+
* runner stored on the cell; default label is `id`. */
|
|
64
|
+
label?: (value: V, id: string) => string;
|
|
65
|
+
}
|
|
66
|
+
/** A cell carries one picked value from each axis, keyed by axis name. */
|
|
67
|
+
interface MatrixCell {
|
|
68
|
+
axes: Record<string, {
|
|
69
|
+
id: string;
|
|
70
|
+
value: unknown;
|
|
71
|
+
}>;
|
|
72
|
+
/** 0-based replicate index within the same axis combination. */
|
|
73
|
+
rep: number;
|
|
74
|
+
/** Stable sort key — preserves cartesian order across concurrent execution. */
|
|
75
|
+
ordinal: number;
|
|
76
|
+
}
|
|
77
|
+
interface CellResult<Output> {
|
|
78
|
+
output: Output;
|
|
79
|
+
verdict: DefaultVerdict;
|
|
80
|
+
/** Known subtotal in USD. When `costProvenance.kind` is `uncaptured` the
|
|
81
|
+
* cell spent this much AND an unknown amount more, so the value must not be
|
|
82
|
+
* read as the cell's total. Counts toward `costCeiling` either way. */
|
|
83
|
+
costUsd: number;
|
|
84
|
+
durationMs: number;
|
|
85
|
+
runId?: string;
|
|
86
|
+
/** Origin of `costUsd`. Absent on a result `runCell` returned — that
|
|
87
|
+
* `costUsd` is measured by contract. The runner always sets it on a cell
|
|
88
|
+
* that threw, where a bare number cannot say whether `0` means "spent
|
|
89
|
+
* nothing" or "spend unknown".
|
|
90
|
+
*
|
|
91
|
+
* Converting to `RunRecord` needs one translation: a `RunRecord` rejects a
|
|
92
|
+
* numeric `costUsd` beside `uncaptured` provenance, so an uncaptured cell
|
|
93
|
+
* becomes `costUsd: null` there. A cell keeps the subtotal because a cost
|
|
94
|
+
* ceiling must charge the part it can see; a record drops it because a
|
|
95
|
+
* record must never read as a total. */
|
|
96
|
+
costProvenance?: CostProvenance;
|
|
97
|
+
/** Populated when `runCell` threw, or when it returned a cost the runner
|
|
98
|
+
* cannot bill. The cell contributes 0 to passRate AND meanScore regardless
|
|
99
|
+
* of `verdict`. */
|
|
100
|
+
error?: {
|
|
101
|
+
message: string;
|
|
102
|
+
kind: string;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
interface AxisSummary {
|
|
106
|
+
axisName: string;
|
|
107
|
+
axisValue: string;
|
|
108
|
+
cells: number;
|
|
109
|
+
passRate: number;
|
|
110
|
+
meanScore: number;
|
|
111
|
+
p50Score: number;
|
|
112
|
+
p90Score: number;
|
|
113
|
+
/** Sum of the known cost subtotals in this bucket. Under-counts real spend
|
|
114
|
+
* by an unknown amount when `costUncapturedCells` is above 0. */
|
|
115
|
+
totalCostUsd: number;
|
|
116
|
+
/** Cells in this bucket whose cost is a subtotal, not a total. */
|
|
117
|
+
costUncapturedCells: number;
|
|
118
|
+
meanDurationMs: number;
|
|
119
|
+
}
|
|
120
|
+
interface MatrixResult<Output> {
|
|
121
|
+
cells: Array<{
|
|
122
|
+
cell: MatrixCell;
|
|
123
|
+
runs: CellResult<Output>[];
|
|
124
|
+
}>;
|
|
125
|
+
/** `byAxis[axisName][axisValueId] = summary`. Populated only for axes
|
|
126
|
+
* named in `aggregateBy` (default = every axis in `axes`). */
|
|
127
|
+
byAxis: Record<string, Record<string, AxisSummary>>;
|
|
128
|
+
summary: {
|
|
129
|
+
totalCells: number;
|
|
130
|
+
runsExecuted: number;
|
|
131
|
+
/** Cells removed by `filter` plus cells unscheduled after the cost
|
|
132
|
+
* ceiling or abort signal tripped. */
|
|
133
|
+
cellsSkipped: number;
|
|
134
|
+
overallPassRate: number;
|
|
135
|
+
overallMeanScore: number;
|
|
136
|
+
/** Sum of every cell's known cost subtotal. Read it together with
|
|
137
|
+
* `costUncapturedCells`: above 0, this number is a floor on real spend,
|
|
138
|
+
* and the cost ceiling stopped the run later than true spend deserved. */
|
|
139
|
+
totalCostUsd: number;
|
|
140
|
+
/** Cells whose cost is a subtotal rather than a total — a failure that
|
|
141
|
+
* reported no spend, a failure that reported a partial amount, or a
|
|
142
|
+
* result the runner could not bill. */
|
|
143
|
+
costUncapturedCells: number;
|
|
144
|
+
/** What the cost ceiling actually read. Equals `totalCostUsd` unless
|
|
145
|
+
* `maxCellCostUsd` was set and a cell's cost was a subtotal, in which case
|
|
146
|
+
* the ceiling charged the bound and this figure is higher. */
|
|
147
|
+
ceilingChargedUsd: number;
|
|
148
|
+
durationMs: number;
|
|
149
|
+
};
|
|
150
|
+
/** Stable id-like string generated at the end of the run. */
|
|
151
|
+
matrixId: string;
|
|
152
|
+
}
|
|
153
|
+
interface RunAgentMatrixOptions<Output> {
|
|
154
|
+
axes: MatrixAxis<unknown>[];
|
|
155
|
+
/** User-supplied cell executor. May throw; the matrix captures throws as
|
|
156
|
+
* `CellResult.error` and continues. A cell that spends money before it
|
|
157
|
+
* throws must declare that spend — `throw withCellSpend(err, spend)` — or
|
|
158
|
+
* the money is missing from `totalCostUsd` and from the sum `costCeiling`
|
|
159
|
+
* reads. */
|
|
160
|
+
runCell: (cell: MatrixCell) => Promise<CellResult<Output>>;
|
|
161
|
+
/** Replicates per cell. Default 1. */
|
|
162
|
+
reps?: number;
|
|
163
|
+
/** Prune cells from the cartesian BEFORE rep expansion. */
|
|
164
|
+
filter?: (cell: Omit<MatrixCell, 'rep' | 'ordinal'>) => boolean;
|
|
165
|
+
/** Axes to aggregate into `byAxis`. Default: every axis in `axes`. */
|
|
166
|
+
aggregateBy?: string[];
|
|
167
|
+
/** Max concurrent in-flight `runCell` invocations. Default 4. */
|
|
168
|
+
maxConcurrency?: number;
|
|
169
|
+
/** Cumulative-cost abort threshold (USD). When the running sum of
|
|
170
|
+
* `result.costUsd` crosses this value, no new cells are scheduled.
|
|
171
|
+
* In-flight cells finish. Failed cells count for the spend they declare.
|
|
172
|
+
* Default `Infinity`. */
|
|
173
|
+
costCeiling?: number;
|
|
174
|
+
/** Upper bound on what ONE cell can spend (USD). A cell whose cost is a
|
|
175
|
+
* subtotal — a failure that declared nothing, a partial amount, a result
|
|
176
|
+
* the runner could not bill — is charged this bound against `costCeiling`
|
|
177
|
+
* instead of its known subtotal, so hidden spend cannot walk the run past
|
|
178
|
+
* its budget.
|
|
179
|
+
*
|
|
180
|
+
* It changes only what the ceiling reads. `CellResult.costUsd` and
|
|
181
|
+
* `summary.totalCostUsd` keep reporting known spend, never a bound that was
|
|
182
|
+
* charged but not spent; `summary.ceilingChargedUsd` reports the
|
|
183
|
+
* conservative figure. Omit it and the ceiling stays fail-open for exactly
|
|
184
|
+
* the cells that hid their spend, with `costUncapturedCells` as the only
|
|
185
|
+
* signal. */
|
|
186
|
+
maxCellCostUsd?: number;
|
|
187
|
+
/** Fires once per executed cell, after its promise settles. */
|
|
188
|
+
onCellComplete?: (cell: MatrixCell, result: CellResult<Output>) => void;
|
|
189
|
+
/** External cancellation. Aborts in-flight cells via a forwarded signal
|
|
190
|
+
* and suppresses scheduling of new ones. */
|
|
191
|
+
signal?: AbortSignal;
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/matrix/runner.d.ts
|
|
195
|
+
declare function runAgentMatrix<Output>(opts: RunAgentMatrixOptions<Output>): Promise<MatrixResult<Output>>;
|
|
196
|
+
//#endregion
|
|
197
|
+
export { MatrixCell as a, CellSpend as c, MatrixAxis as i, readCellSpend as l, AxisSummary as n, MatrixResult as o, CellResult as r, RunAgentMatrixOptions as s, runAgentMatrix as t, withCellSpend as u };
|
|
198
|
+
//# sourceMappingURL=index-D_P7Ye43.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-D_P7Ye43.d.ts","names":[],"sources":["../src/matrix/cell-spend.ts","../src/matrix/types.ts","../src/matrix/runner.ts"],"mappings":";;;;;;;;;;;;;;;;;;UAoBiB;;;;EAIf;;EAEA;;;;EAIA;;;;;;;;;;;;;;;;iBAiBc,cAAc,gBAAgB,OAAO;;;;iBA+CrC,cAAc,iBAAiB;;;;;UC5E9B,WAAW;;EAE1B;;EAEA,QAAQ;IAAQ;IAAY,OAAO;;;;EAGnC,SAAS,OAAO,GAAG;;;UAIJ;EACf,MAAM;IAAiB;IAAY;;;EAEnC;;EAEA;;UAGe,WAAW;EAC1B,QAAQ;EACR,SAAS;;;;EAIT;EACA;EACA;;;;;;;;;;;EAWA,iBAAiB;;;;EAIjB;IAAU;IAAiB;;;UAGZ;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;;EAGA;;EAEA;EACA;;UAGe,aAAa;EAC5B,OAAO;IAAQ,MAAM;IAAY,MAAM,WAAW;;;;EAGlD,QAAQ,eAAe,eAAe;EACtC;IACE;IACA;;;IAGA;IACA;IACA;;;;IAIA;;;;IAIA;;;;IAIA;IACA;;;EAGF;;UAGe,sBAAsB;EACrC,MAAM;;;;;;EAMN,UAAU,MAAM,eAAe,QAAQ,WAAW;;EAElD;;EAEA,UAAU,MAAM,KAAK;;EAErB;;EAEA;;;;;EAKA;;;;;;;;;;;;;EAaA;;EAEA,kBAAkB,MAAM,YAAY,QAAQ,WAAW;;;EAGvD,SAAS;;;;iBC1BW,eAAe,QACnC,MAAM,sBAAsB,UAC3B,QAAQ,aAAa"}
|
package/dist/matrix/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { t as DefaultVerdict } from "../verdict-E4eRNf7-.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { p as CostProvenance } from "../cost-ledger-DbQdN3nO.js";
|
|
3
|
+
import { a as MatrixCell, c as CellSpend, i as MatrixAxis, l as readCellSpend, n as AxisSummary, o as MatrixResult, r as CellResult, s as RunAgentMatrixOptions, t as runAgentMatrix, u as withCellSpend } from "../index-D_P7Ye43.js";
|
|
4
|
+
export { type AxisSummary, type CellResult, type CellSpend, type CostProvenance, type DefaultVerdict, type MatrixAxis, type MatrixCell, type MatrixResult, type RunAgentMatrixOptions, readCellSpend, runAgentMatrix, withCellSpend };
|
package/dist/matrix/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as runAgentMatrix } from "../matrix-
|
|
2
|
-
export { runAgentMatrix };
|
|
1
|
+
import { n as readCellSpend, r as withCellSpend, t as runAgentMatrix } from "../matrix-pWinRMPl.js";
|
|
2
|
+
export { readCellSpend, runAgentMatrix, withCellSpend };
|
|
@@ -1,3 +1,94 @@
|
|
|
1
|
+
//#region src/matrix/cell-spend.ts
|
|
2
|
+
/**
|
|
3
|
+
* Spend carrier for a cell that fails after it has already spent money.
|
|
4
|
+
*
|
|
5
|
+
* `runCell` reports what a cell cost by returning a `CellResult`. A throw
|
|
6
|
+
* carries no result, so the runner cannot know what the cell spent before it
|
|
7
|
+
* failed: the cumulative sum the cost ceiling reads, and `totalCostUsd`, both
|
|
8
|
+
* miss that money. A throw that carries a `CellSpend` closes the gap — the
|
|
9
|
+
* runner bills the failed cell for the spend the carrier declares, and records
|
|
10
|
+
* a cell that declared nothing as `uncaptured` instead of a zero that reads
|
|
11
|
+
* like "this cell spent nothing".
|
|
12
|
+
*
|
|
13
|
+
* The carrier is read structurally, never by `instanceof`, so a throw that
|
|
14
|
+
* crosses a package or realm boundary still bills.
|
|
15
|
+
*/
|
|
16
|
+
/** Property key the carrier occupies on a thrown object. Stable across
|
|
17
|
+
* versions — two copies of this package must agree on it. */
|
|
18
|
+
const CELL_SPEND_KEY = "__agentEvalCellSpend";
|
|
19
|
+
/**
|
|
20
|
+
* Return `error` carrying `spend`, for a throw site inside `runCell`:
|
|
21
|
+
* `throw withCellSpend(err, { costUsd, durationMs, kind })`.
|
|
22
|
+
*
|
|
23
|
+
* A value that cannot hold the carrier — a thrown string or number, or a
|
|
24
|
+
* frozen, sealed, or otherwise non-extensible object — is wrapped in an
|
|
25
|
+
* `Error` that keeps the original as `cause`, so the spend is never dropped.
|
|
26
|
+
* A later call overwrites an earlier carrier — the outer frame knows more
|
|
27
|
+
* than the inner one.
|
|
28
|
+
*
|
|
29
|
+
* Throws `TypeError` on a non-finite or negative amount. A poisoned amount
|
|
30
|
+
* would reach `cumulativeCost` and disable the cost ceiling for the whole run,
|
|
31
|
+
* because `NaN >= ceiling` is false.
|
|
32
|
+
*/
|
|
33
|
+
function withCellSpend(error, spend) {
|
|
34
|
+
assertAmount(spend.costUsd, "costUsd");
|
|
35
|
+
assertAmount(spend.durationMs, "durationMs");
|
|
36
|
+
if (spend.kind !== "observed" && spend.kind !== "estimated" && spend.kind !== "uncaptured") throw new TypeError(`withCellSpend: kind must be observed, estimated or uncaptured, received ${String(spend.kind)}`);
|
|
37
|
+
const carrier = {
|
|
38
|
+
costUsd: spend.costUsd,
|
|
39
|
+
durationMs: spend.durationMs,
|
|
40
|
+
kind: spend.kind
|
|
41
|
+
};
|
|
42
|
+
if (typeof error === "object" && error !== null && attach(error, carrier)) return error;
|
|
43
|
+
const wrapper = new Error(`cell failed: ${messageOf(error)}`, { cause: error });
|
|
44
|
+
attach(wrapper, carrier);
|
|
45
|
+
return wrapper;
|
|
46
|
+
}
|
|
47
|
+
/** True when the carrier landed. `defineProperty` throws on a non-extensible
|
|
48
|
+
* target, so the caller must be able to fall back. */
|
|
49
|
+
function attach(target, carrier) {
|
|
50
|
+
try {
|
|
51
|
+
Object.defineProperty(target, CELL_SPEND_KEY, {
|
|
52
|
+
value: carrier,
|
|
53
|
+
enumerable: false,
|
|
54
|
+
configurable: true,
|
|
55
|
+
writable: true
|
|
56
|
+
});
|
|
57
|
+
return true;
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function messageOf(error) {
|
|
63
|
+
if (error instanceof Error) return error.message;
|
|
64
|
+
return String(error);
|
|
65
|
+
}
|
|
66
|
+
/** Read the spend a thrown value carries. `undefined` when the value carries
|
|
67
|
+
* none, or carries one that does not satisfy `CellSpend` — the runner records
|
|
68
|
+
* both as `uncaptured`. */
|
|
69
|
+
function readCellSpend(error) {
|
|
70
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
71
|
+
const raw = error[CELL_SPEND_KEY];
|
|
72
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
73
|
+
const candidate = raw;
|
|
74
|
+
if (!isAmount(candidate.costUsd) || !isAmount(candidate.durationMs)) return void 0;
|
|
75
|
+
const kind = candidate.kind;
|
|
76
|
+
if (kind !== "observed" && kind !== "estimated" && kind !== "uncaptured") return void 0;
|
|
77
|
+
return {
|
|
78
|
+
costUsd: candidate.costUsd,
|
|
79
|
+
durationMs: candidate.durationMs,
|
|
80
|
+
kind
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** A number the matrix can add to a running cost or duration: finite and not
|
|
84
|
+
* negative. Anything else would poison a sum the cost ceiling reads. */
|
|
85
|
+
function isAmount(value) {
|
|
86
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
87
|
+
}
|
|
88
|
+
function assertAmount(value, field) {
|
|
89
|
+
if (!isAmount(value)) throw new TypeError(`withCellSpend: ${field} must be a finite number >= 0, received ${String(value)}`);
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
1
92
|
//#region src/matrix/aggregation.ts
|
|
2
93
|
function flattenRuns(cells) {
|
|
3
94
|
const rows = [];
|
|
@@ -27,12 +118,14 @@ function summariseRows(rows, axisName, axisValue) {
|
|
|
27
118
|
p50Score: 0,
|
|
28
119
|
p90Score: 0,
|
|
29
120
|
totalCostUsd: 0,
|
|
121
|
+
costUncapturedCells: 0,
|
|
30
122
|
meanDurationMs: 0
|
|
31
123
|
};
|
|
32
124
|
let pass = 0;
|
|
33
125
|
let scoreSum = 0;
|
|
34
126
|
let costSum = 0;
|
|
35
127
|
let durSum = 0;
|
|
128
|
+
let costUncapturedCells = 0;
|
|
36
129
|
const scores = [];
|
|
37
130
|
for (const { result } of rows) {
|
|
38
131
|
const errored = result.error !== void 0;
|
|
@@ -42,6 +135,7 @@ function summariseRows(rows, axisName, axisValue) {
|
|
|
42
135
|
scores.push(score);
|
|
43
136
|
costSum += result.costUsd;
|
|
44
137
|
durSum += result.durationMs;
|
|
138
|
+
if (result.costProvenance?.kind === "uncaptured") costUncapturedCells++;
|
|
45
139
|
}
|
|
46
140
|
scores.sort((a, b) => a - b);
|
|
47
141
|
return {
|
|
@@ -53,6 +147,7 @@ function summariseRows(rows, axisName, axisValue) {
|
|
|
53
147
|
p50Score: quantile(scores, .5),
|
|
54
148
|
p90Score: quantile(scores, .9),
|
|
55
149
|
totalCostUsd: costSum,
|
|
150
|
+
costUncapturedCells,
|
|
56
151
|
meanDurationMs: durSum / rows.length
|
|
57
152
|
};
|
|
58
153
|
}
|
|
@@ -139,28 +234,62 @@ function makeMatrixId() {
|
|
|
139
234
|
for (let i = 0; i < 8; i++) r += Math.floor(Math.random() * 16).toString(16);
|
|
140
235
|
return `mtx_${t}_${r}`;
|
|
141
236
|
}
|
|
237
|
+
/** Fresh per result — a shared object would let one consumer's mutation reach
|
|
238
|
+
* every other cell in the run. */
|
|
239
|
+
function uncaptured() {
|
|
240
|
+
return {
|
|
241
|
+
kind: "uncaptured",
|
|
242
|
+
usd: null
|
|
243
|
+
};
|
|
244
|
+
}
|
|
142
245
|
function makeErrorResult(err) {
|
|
143
246
|
const e = err;
|
|
247
|
+
const spend = readCellSpend(err);
|
|
144
248
|
return {
|
|
145
249
|
output: void 0,
|
|
146
250
|
verdict: {
|
|
147
251
|
valid: false,
|
|
148
252
|
score: 0
|
|
149
253
|
},
|
|
150
|
-
costUsd: 0,
|
|
151
|
-
durationMs: 0,
|
|
254
|
+
costUsd: spend?.costUsd ?? 0,
|
|
255
|
+
durationMs: spend?.durationMs ?? 0,
|
|
256
|
+
costProvenance: spend === void 0 || spend.kind === "uncaptured" ? uncaptured() : {
|
|
257
|
+
kind: spend.kind,
|
|
258
|
+
usd: spend.costUsd
|
|
259
|
+
},
|
|
152
260
|
error: {
|
|
153
261
|
message: typeof e?.message === "string" ? e.message : String(err),
|
|
154
262
|
kind: typeof e?.name === "string" ? e.name : "Error"
|
|
155
263
|
}
|
|
156
264
|
};
|
|
157
265
|
}
|
|
266
|
+
/** Make a result safe to sum.
|
|
267
|
+
*
|
|
268
|
+
* A `costUsd` that cannot be summed fails the cell: `NaN` in `cumulativeCost`
|
|
269
|
+
* makes `>= costCeiling` false forever, so one bad cell would disable the
|
|
270
|
+
* ceiling for every cell after it.
|
|
271
|
+
*
|
|
272
|
+
* A bad `durationMs` reaches only `meanDurationMs`, never the ceiling, so it
|
|
273
|
+
* cannot justify throwing away a real verdict and a real cost. It is reported
|
|
274
|
+
* as 0 and warned about, and the rest of the result stands. A wall clock that
|
|
275
|
+
* steps back mid-cell makes `Date.now() - startedAt` negative, so this is a
|
|
276
|
+
* reachable state, not only a caller bug. */
|
|
277
|
+
function billableResult(result, onBadDuration) {
|
|
278
|
+
if (!isAmount(result.costUsd)) return makeErrorResult(/* @__PURE__ */ new RangeError(`runCell reported costUsd=${String(result.costUsd)}; a cell cost must be a finite number >= 0 so the cost ceiling stays enforceable`));
|
|
279
|
+
if (isAmount(result.durationMs)) return result;
|
|
280
|
+
onBadDuration(result.durationMs);
|
|
281
|
+
return {
|
|
282
|
+
...result,
|
|
283
|
+
durationMs: 0
|
|
284
|
+
};
|
|
285
|
+
}
|
|
158
286
|
async function runAgentMatrix(opts) {
|
|
159
287
|
const startedAt = Date.now();
|
|
160
288
|
const reps = Math.max(1, opts.reps ?? 1);
|
|
161
289
|
const maxConcurrency = Math.max(1, opts.maxConcurrency ?? 4);
|
|
162
290
|
const costCeiling = opts.costCeiling ?? Number.POSITIVE_INFINITY;
|
|
163
291
|
const aggregateBy = opts.aggregateBy ?? opts.axes.map((a) => a.name);
|
|
292
|
+
if (opts.maxCellCostUsd !== void 0 && !isAmount(opts.maxCellCostUsd)) throw new RangeError(`runAgentMatrix: maxCellCostUsd must be a finite number >= 0, received ${String(opts.maxCellCostUsd)}`);
|
|
164
293
|
const base = cartesian(opts.axes);
|
|
165
294
|
const filtered = opts.filter ? base.filter((c) => opts.filter(c)) : base;
|
|
166
295
|
const filteredOut = base.length - filtered.length;
|
|
@@ -175,6 +304,8 @@ async function runAgentMatrix(opts) {
|
|
|
175
304
|
let costCeilingReached = false;
|
|
176
305
|
let runsExecuted = 0;
|
|
177
306
|
let cellsUnscheduled = 0;
|
|
307
|
+
let costUncapturedCells = 0;
|
|
308
|
+
let badDurationCells = 0;
|
|
178
309
|
const aborted = () => opts.signal?.aborted === true;
|
|
179
310
|
let inFlight = 0;
|
|
180
311
|
let cursor = 0;
|
|
@@ -203,10 +334,19 @@ async function runAgentMatrix(opts) {
|
|
|
203
334
|
} catch (err) {
|
|
204
335
|
return makeErrorResult(err);
|
|
205
336
|
}
|
|
206
|
-
})().then((
|
|
337
|
+
})().then((settled) => {
|
|
338
|
+
const result = billableResult(settled, (reported) => {
|
|
339
|
+
badDurationCells++;
|
|
340
|
+
if (badDurationCells === 1) console.warn(`[matrix] a cell reported durationMs=${String(reported)}; recorded as 0 — meanDurationMs under-reports this run`);
|
|
341
|
+
});
|
|
207
342
|
record.runs.push(result);
|
|
208
343
|
runsExecuted++;
|
|
209
|
-
|
|
344
|
+
const uncapturedCost = result.costProvenance?.kind === "uncaptured";
|
|
345
|
+
cumulativeCost += uncapturedCost && opts.maxCellCostUsd !== void 0 ? Math.max(result.costUsd, opts.maxCellCostUsd) : result.costUsd;
|
|
346
|
+
if (uncapturedCost) {
|
|
347
|
+
costUncapturedCells++;
|
|
348
|
+
if (costUncapturedCells === 1) console.warn("[matrix] a cell's cost is a subtotal, not a total — totalCostUsd and the cost ceiling under-count this run");
|
|
349
|
+
}
|
|
210
350
|
if (cumulativeCost >= costCeiling && !costCeilingReached) {
|
|
211
351
|
costCeilingReached = true;
|
|
212
352
|
console.warn("[matrix] cost ceiling reached");
|
|
@@ -259,12 +399,14 @@ async function runAgentMatrix(opts) {
|
|
|
259
399
|
overallPassRate: runCount === 0 ? 0 : pass / runCount,
|
|
260
400
|
overallMeanScore: runCount === 0 ? 0 : scoreSum / runCount,
|
|
261
401
|
totalCostUsd: totalCost,
|
|
402
|
+
costUncapturedCells,
|
|
403
|
+
ceilingChargedUsd: cumulativeCost,
|
|
262
404
|
durationMs: Date.now() - startedAt
|
|
263
405
|
},
|
|
264
406
|
matrixId: makeMatrixId()
|
|
265
407
|
};
|
|
266
408
|
}
|
|
267
409
|
//#endregion
|
|
268
|
-
export { runAgentMatrix as t };
|
|
410
|
+
export { readCellSpend as n, withCellSpend as r, runAgentMatrix as t };
|
|
269
411
|
|
|
270
|
-
//# sourceMappingURL=matrix-
|
|
412
|
+
//# sourceMappingURL=matrix-pWinRMPl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrix-pWinRMPl.js","names":[],"sources":["../src/matrix/cell-spend.ts","../src/matrix/aggregation.ts","../src/matrix/runner.ts"],"sourcesContent":["/**\n * Spend carrier for a cell that fails after it has already spent money.\n *\n * `runCell` reports what a cell cost by returning a `CellResult`. A throw\n * carries no result, so the runner cannot know what the cell spent before it\n * failed: the cumulative sum the cost ceiling reads, and `totalCostUsd`, both\n * miss that money. A throw that carries a `CellSpend` closes the gap — the\n * runner bills the failed cell for the spend the carrier declares, and records\n * a cell that declared nothing as `uncaptured` instead of a zero that reads\n * like \"this cell spent nothing\".\n *\n * The carrier is read structurally, never by `instanceof`, so a throw that\n * crosses a package or realm boundary still bills.\n */\n\n/** Property key the carrier occupies on a thrown object. Stable across\n * versions — two copies of this package must agree on it. */\nconst CELL_SPEND_KEY = '__agentEvalCellSpend'\n\n/** Money and wall time a cell consumed before it threw. */\nexport interface CellSpend {\n /** KNOWN subtotal in USD. When `kind` is `uncaptured` the cell spent this\n * much AND an unknown amount more, so the value must not be read as the\n * cell's total. */\n costUsd: number\n /** Wall time the cell consumed before the throw, in milliseconds. */\n durationMs: number\n /** `observed` = provider-reported amounts, `estimated` = computed from token\n * prices, `uncaptured` = `costUsd` is a subtotal and part of the spend\n * could not be measured. */\n kind: 'observed' | 'estimated' | 'uncaptured'\n}\n\n/**\n * Return `error` carrying `spend`, for a throw site inside `runCell`:\n * `throw withCellSpend(err, { costUsd, durationMs, kind })`.\n *\n * A value that cannot hold the carrier — a thrown string or number, or a\n * frozen, sealed, or otherwise non-extensible object — is wrapped in an\n * `Error` that keeps the original as `cause`, so the spend is never dropped.\n * A later call overwrites an earlier carrier — the outer frame knows more\n * than the inner one.\n *\n * Throws `TypeError` on a non-finite or negative amount. A poisoned amount\n * would reach `cumulativeCost` and disable the cost ceiling for the whole run,\n * because `NaN >= ceiling` is false.\n */\nexport function withCellSpend(error: unknown, spend: CellSpend): unknown {\n assertAmount(spend.costUsd, 'costUsd')\n assertAmount(spend.durationMs, 'durationMs')\n if (spend.kind !== 'observed' && spend.kind !== 'estimated' && spend.kind !== 'uncaptured') {\n throw new TypeError(\n `withCellSpend: kind must be observed, estimated or uncaptured, received ${String(spend.kind)}`,\n )\n }\n const carrier: CellSpend = {\n costUsd: spend.costUsd,\n durationMs: spend.durationMs,\n kind: spend.kind,\n }\n if (typeof error === 'object' && error !== null && attach(error, carrier)) return error\n // A primitive cannot hold a property, and a frozen or sealed object refuses\n // one. Carry the spend on a fresh Error that keeps the original as `cause`\n // rather than lose it — a dropped carrier bills the cell as uncaptured,\n // which is the under-billing this module exists to prevent.\n const wrapper = new Error(`cell failed: ${messageOf(error)}`, { cause: error })\n attach(wrapper, carrier)\n return wrapper\n}\n\n/** True when the carrier landed. `defineProperty` throws on a non-extensible\n * target, so the caller must be able to fall back. */\nfunction attach(target: object, carrier: CellSpend): boolean {\n try {\n Object.defineProperty(target, CELL_SPEND_KEY, {\n value: carrier,\n enumerable: false,\n configurable: true,\n writable: true,\n })\n return true\n } catch {\n return false\n }\n}\n\nfunction messageOf(error: unknown): string {\n if (error instanceof Error) return error.message\n return String(error)\n}\n\n/** Read the spend a thrown value carries. `undefined` when the value carries\n * none, or carries one that does not satisfy `CellSpend` — the runner records\n * both as `uncaptured`. */\nexport function readCellSpend(error: unknown): CellSpend | undefined {\n if (typeof error !== 'object' || error === null) return undefined\n const raw = (error as Record<string, unknown>)[CELL_SPEND_KEY]\n if (typeof raw !== 'object' || raw === null) return undefined\n const candidate = raw as Record<string, unknown>\n if (!isAmount(candidate.costUsd) || !isAmount(candidate.durationMs)) return undefined\n const kind = candidate.kind\n if (kind !== 'observed' && kind !== 'estimated' && kind !== 'uncaptured') return undefined\n return { costUsd: candidate.costUsd, durationMs: candidate.durationMs, kind }\n}\n\n/** A number the matrix can add to a running cost or duration: finite and not\n * negative. Anything else would poison a sum the cost ceiling reads. */\nexport function isAmount(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0\n}\n\nfunction assertAmount(value: number, field: string): void {\n if (!isAmount(value)) {\n throw new TypeError(\n `withCellSpend: ${field} must be a finite number >= 0, received ${String(value)}`,\n )\n }\n}\n","/**\n * Per-axis aggregation of cell runs into `AxisSummary` rows.\n *\n * Pure: consumes the final `cells: [{cell, runs}]` array and returns the\n * `byAxis` table. Error runs contribute 0 to passRate and meanScore. Cost and\n * duration always count — the budget was spent regardless. A run whose cost is\n * a subtotal rather than a total raises `costUncapturedCells` for its bucket,\n * so a per-axis cost row says when it under-counts.\n */\n\nimport type { AxisSummary, CellResult, MatrixAxis, MatrixCell, MatrixResult } from './types'\n\ninterface Row<Output> {\n cell: MatrixCell\n result: CellResult<Output>\n}\n\nfunction flattenRuns<Output>(cells: MatrixResult<Output>['cells']): Row<Output>[] {\n const rows: Row<Output>[] = []\n for (const { cell, runs } of cells) {\n for (const result of runs) rows.push({ cell, result })\n }\n return rows\n}\n\nfunction quantile(sorted: number[], q: number): number {\n if (sorted.length === 0) return 0\n if (sorted.length === 1) return sorted[0] as number\n const pos = (sorted.length - 1) * q\n const lo = Math.floor(pos)\n const hi = Math.ceil(pos)\n if (lo === hi) return sorted[lo] as number\n const frac = pos - lo\n return (sorted[lo] as number) * (1 - frac) + (sorted[hi] as number) * frac\n}\n\nexport function summariseRows<Output>(\n rows: Row<Output>[],\n axisName: string,\n axisValue: string,\n): AxisSummary {\n if (rows.length === 0) {\n return {\n axisName,\n axisValue,\n cells: 0,\n passRate: 0,\n meanScore: 0,\n p50Score: 0,\n p90Score: 0,\n totalCostUsd: 0,\n costUncapturedCells: 0,\n meanDurationMs: 0,\n }\n }\n let pass = 0\n let scoreSum = 0\n let costSum = 0\n let durSum = 0\n let costUncapturedCells = 0\n const scores: number[] = []\n for (const { result } of rows) {\n const errored = result.error !== undefined\n const score = errored ? 0 : result.verdict.score\n const valid = !errored && result.verdict.valid\n if (valid) pass++\n scoreSum += score\n scores.push(score)\n costSum += result.costUsd\n durSum += result.durationMs\n if (result.costProvenance?.kind === 'uncaptured') costUncapturedCells++\n }\n scores.sort((a, b) => a - b)\n return {\n axisName,\n axisValue,\n cells: rows.length,\n passRate: pass / rows.length,\n meanScore: scoreSum / rows.length,\n p50Score: quantile(scores, 0.5),\n p90Score: quantile(scores, 0.9),\n totalCostUsd: costSum,\n costUncapturedCells,\n meanDurationMs: durSum / rows.length,\n }\n}\n\nfunction bucketBy<Output>(\n rows: Row<Output>[],\n axisName: string,\n labelFor: (id: string) => string,\n): Record<string, AxisSummary> {\n const buckets = new Map<string, Row<Output>[]>()\n for (const row of rows) {\n const slot = row.cell.axes[axisName]\n if (!slot) continue\n const id = slot.id\n let arr = buckets.get(id)\n if (!arr) {\n arr = []\n buckets.set(id, arr)\n }\n arr.push(row)\n }\n const out: Record<string, AxisSummary> = {}\n // Sorted keys for deterministic JSON serialisation.\n for (const id of [...buckets.keys()].sort()) {\n out[id] = summariseRows(buckets.get(id) as Row<Output>[], axisName, labelFor(id))\n }\n return out\n}\n\nexport function buildByAxis<Output>(\n cells: MatrixResult<Output>['cells'],\n axes: MatrixAxis<unknown>[],\n aggregateBy: string[],\n): Record<string, Record<string, AxisSummary>> {\n const rows = flattenRuns(cells)\n const byName = new Map(axes.map((a) => [a.name, a]))\n const byAxis: Record<string, Record<string, AxisSummary>> = {}\n for (const name of aggregateBy) {\n const axis = byName.get(name)\n const labelFor = (id: string): string => {\n if (!axis?.label) return id\n const found = axis.values.find((v) => v.id === id)\n if (!found) return id\n return axis.label(found.value, id)\n }\n byAxis[name] = bucketBy(rows, name, labelFor)\n }\n return byAxis\n}\n","/**\n * N-axis cartesian runner.\n *\n * Expansion order: cartesian over `axes` in declared order, then `reps` as the\n * inner-most dim → `ordinal = (cartIdx * reps) + rep`. The returned\n * `cells[]` is sorted by `ordinal` so concurrent execution does not reorder\n * the output.\n *\n * Scheduling is a sliding window of in-flight promises capped at\n * `maxConcurrency`. The window stops admitting new cells when the cost\n * ceiling trips or the abort signal fires; in-flight cells finish.\n */\n\nimport { buildByAxis } from './aggregation'\nimport { isAmount, readCellSpend } from './cell-spend'\nimport type {\n CellResult,\n CostProvenance,\n MatrixAxis,\n MatrixCell,\n MatrixResult,\n RunAgentMatrixOptions,\n} from './types'\n\ninterface BaseCell {\n axes: Record<string, { id: string; value: unknown }>\n}\n\nfunction cartesian(axes: MatrixAxis<unknown>[]): BaseCell[] {\n // Empty axes (`values=[]`) collapse the whole product to zero cells. An\n // empty `axes` array yields a single empty-axes cell — degenerate but\n // valid (caller is iterating only reps).\n if (axes.length === 0) return [{ axes: {} }]\n for (const a of axes) if (a.values.length === 0) return []\n const out: BaseCell[] = []\n const idx = new Array(axes.length).fill(0)\n while (true) {\n const slot: Record<string, { id: string; value: unknown }> = {}\n for (let i = 0; i < axes.length; i++) {\n const axis = axes[i] as MatrixAxis<unknown>\n const v = axis.values[idx[i] as number] as { id: string; value: unknown }\n slot[axis.name] = { id: v.id, value: v.value }\n }\n out.push({ axes: slot })\n // Increment like an odometer, left-most axis is fastest.\n let i = 0\n while (i < axes.length) {\n const next = (idx[i] as number) + 1\n const axis = axes[i] as MatrixAxis<unknown>\n if (next < axis.values.length) {\n idx[i] = next\n break\n }\n idx[i] = 0\n i++\n }\n if (i === axes.length) break\n }\n return out\n}\n\nfunction makeMatrixId(): string {\n // Stable id-like string: time + 8 random hex chars. Avoids node:crypto\n // import to keep the matrix dep-free.\n const t = Date.now().toString(36)\n let r = ''\n for (let i = 0; i < 8; i++) r += Math.floor(Math.random() * 16).toString(16)\n return `mtx_${t}_${r}`\n}\n\n/** Fresh per result — a shared object would let one consumer's mutation reach\n * every other cell in the run. */\nfunction uncaptured(): CostProvenance {\n return { kind: 'uncaptured', usd: null }\n}\n\nfunction makeErrorResult<Output>(err: unknown): CellResult<Output> {\n const e = err as { message?: string; name?: string }\n const spend = readCellSpend(err)\n return {\n output: undefined as unknown as Output,\n verdict: { valid: false, score: 0 },\n costUsd: spend?.costUsd ?? 0,\n durationMs: spend?.durationMs ?? 0,\n costProvenance:\n spend === undefined || spend.kind === 'uncaptured'\n ? uncaptured()\n : { kind: spend.kind, usd: spend.costUsd },\n error: {\n message: typeof e?.message === 'string' ? e.message : String(err),\n kind: typeof e?.name === 'string' ? e.name : 'Error',\n },\n }\n}\n\n/** Make a result safe to sum.\n *\n * A `costUsd` that cannot be summed fails the cell: `NaN` in `cumulativeCost`\n * makes `>= costCeiling` false forever, so one bad cell would disable the\n * ceiling for every cell after it.\n *\n * A bad `durationMs` reaches only `meanDurationMs`, never the ceiling, so it\n * cannot justify throwing away a real verdict and a real cost. It is reported\n * as 0 and warned about, and the rest of the result stands. A wall clock that\n * steps back mid-cell makes `Date.now() - startedAt` negative, so this is a\n * reachable state, not only a caller bug. */\nfunction billableResult<Output>(\n result: CellResult<Output>,\n onBadDuration: (reported: unknown) => void,\n): CellResult<Output> {\n if (!isAmount(result.costUsd)) {\n return makeErrorResult<Output>(\n new RangeError(\n `runCell reported costUsd=${String(result.costUsd)}; a cell cost must be a finite number >= 0 so the cost ceiling stays enforceable`,\n ),\n )\n }\n if (isAmount(result.durationMs)) return result\n onBadDuration(result.durationMs)\n return { ...result, durationMs: 0 }\n}\n\nexport async function runAgentMatrix<Output>(\n opts: RunAgentMatrixOptions<Output>,\n): Promise<MatrixResult<Output>> {\n const startedAt = Date.now()\n const reps = Math.max(1, opts.reps ?? 1)\n const maxConcurrency = Math.max(1, opts.maxConcurrency ?? 4)\n const costCeiling = opts.costCeiling ?? Number.POSITIVE_INFINITY\n const aggregateBy = opts.aggregateBy ?? opts.axes.map((a) => a.name)\n if (opts.maxCellCostUsd !== undefined && !isAmount(opts.maxCellCostUsd)) {\n throw new RangeError(\n `runAgentMatrix: maxCellCostUsd must be a finite number >= 0, received ${String(opts.maxCellCostUsd)}`,\n )\n }\n\n const base = cartesian(opts.axes)\n const filtered = opts.filter\n ? base.filter((c) => (opts.filter as (b: BaseCell) => boolean)(c))\n : base\n const filteredOut = base.length - filtered.length\n\n const planned: MatrixCell[] = []\n for (let i = 0; i < filtered.length; i++) {\n for (let r = 0; r < reps; r++) {\n planned.push({\n axes: (filtered[i] as BaseCell).axes,\n rep: r,\n ordinal: i * reps + r,\n })\n }\n }\n\n const cellRecords: Array<{ cell: MatrixCell; runs: CellResult<Output>[] }> = []\n // What the ceiling reads. It runs ahead of the reported total whenever a\n // cell's cost is a subtotal and `maxCellCostUsd` bounds what it could have\n // been — a budget must assume the worst about spend it cannot see.\n let cumulativeCost = 0\n let costCeilingReached = false\n let runsExecuted = 0\n let cellsUnscheduled = 0\n let costUncapturedCells = 0\n let badDurationCells = 0\n\n const aborted = (): boolean => opts.signal?.aborted === true\n\n // Per-run abort controller forwards the external signal so cell executors\n // see cancellation. We don't expose it on `MatrixCell` — the signature on\n // `runCell` per the public API is `(cell) => Promise<...>`. Executors that\n // need cancellation use the external signal directly via closure.\n\n let inFlight = 0\n let cursor = 0\n let resolveAll: (() => void) | undefined\n const done = new Promise<void>((res) => {\n resolveAll = res\n })\n\n const pump = (): void => {\n while (inFlight < maxConcurrency && cursor < planned.length) {\n if (aborted() || costCeilingReached) {\n // Drain remaining as unscheduled.\n const left = planned.length - cursor\n cellsUnscheduled += left\n cursor = planned.length\n break\n }\n const cell = planned[cursor++] as MatrixCell\n inFlight++\n // Lazily allocate the record so cells appear in `cells[]` in any\n // arrival order; we sort by ordinal at the end.\n const record = { cell, runs: [] as CellResult<Output>[] }\n cellRecords.push(record)\n const promise: Promise<CellResult<Output>> = (async () => {\n try {\n return await opts.runCell(cell)\n } catch (err) {\n return makeErrorResult<Output>(err)\n }\n })()\n promise.then((settled) => {\n const result = billableResult(settled, (reported) => {\n badDurationCells++\n if (badDurationCells === 1) {\n // eslint-disable-next-line no-console\n console.warn(\n `[matrix] a cell reported durationMs=${String(reported)}; recorded as 0 — meanDurationMs under-reports this run`,\n )\n }\n })\n record.runs.push(result)\n runsExecuted++\n const uncapturedCost = result.costProvenance?.kind === 'uncaptured'\n cumulativeCost +=\n uncapturedCost && opts.maxCellCostUsd !== undefined\n ? Math.max(result.costUsd, opts.maxCellCostUsd)\n : result.costUsd\n if (uncapturedCost) {\n costUncapturedCells++\n if (costUncapturedCells === 1) {\n // eslint-disable-next-line no-console\n console.warn(\n \"[matrix] a cell's cost is a subtotal, not a total — totalCostUsd and the cost ceiling under-count this run\",\n )\n }\n }\n if (cumulativeCost >= costCeiling && !costCeilingReached) {\n costCeilingReached = true\n // eslint-disable-next-line no-console\n console.warn('[matrix] cost ceiling reached')\n }\n try {\n opts.onCellComplete?.(cell, result)\n } catch {\n // onCellComplete is observational — swallow throws so a noisy\n // callback can't tank the run.\n }\n inFlight--\n if (cursor < planned.length) {\n pump()\n } else if (inFlight === 0) {\n resolveAll?.()\n }\n })\n }\n if (cursor >= planned.length && inFlight === 0) resolveAll?.()\n }\n\n const onAbort = (): void => {\n // External abort: stop scheduling. In-flight cells finish; their\n // executors observe `opts.signal.aborted` directly via closure.\n if (cursor < planned.length) {\n cellsUnscheduled += planned.length - cursor\n cursor = planned.length\n }\n if (inFlight === 0) resolveAll?.()\n }\n if (opts.signal) {\n if (opts.signal.aborted) {\n cellsUnscheduled = planned.length\n cursor = planned.length\n resolveAll?.()\n } else {\n opts.signal.addEventListener('abort', onAbort, { once: true })\n }\n }\n\n if (planned.length === 0) {\n resolveAll?.()\n } else {\n pump()\n }\n\n await done\n if (opts.signal) opts.signal.removeEventListener('abort', onAbort)\n\n cellRecords.sort((a, b) => a.cell.ordinal - b.cell.ordinal)\n\n let pass = 0\n let scoreSum = 0\n let totalCost = 0\n let runCount = 0\n for (const { runs } of cellRecords) {\n for (const r of runs) {\n runCount++\n const errored = r.error !== undefined\n if (!errored && r.verdict.valid) pass++\n scoreSum += errored ? 0 : r.verdict.score\n totalCost += r.costUsd\n }\n }\n\n const byAxis = buildByAxis(cellRecords, opts.axes, aggregateBy)\n\n return {\n cells: cellRecords,\n byAxis,\n summary: {\n totalCells: planned.length,\n runsExecuted,\n cellsSkipped: cellsUnscheduled + filteredOut * reps,\n overallPassRate: runCount === 0 ? 0 : pass / runCount,\n overallMeanScore: runCount === 0 ? 0 : scoreSum / runCount,\n totalCostUsd: totalCost,\n costUncapturedCells,\n ceilingChargedUsd: cumulativeCost,\n durationMs: Date.now() - startedAt,\n },\n matrixId: makeMatrixId(),\n }\n}\n"],"mappings":"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"}
|