@gaunt-sloth/batch 2.0.0-alpha.19 → 2.0.0-alpha.21
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/README.md +140 -0
- package/dist/bin.d.ts +13 -0
- package/dist/bin.js +28 -0
- package/dist/bin.js.map +1 -0
- package/dist/deterministicChecks.d.ts +39 -7
- package/dist/deterministicChecks.js +120 -11
- package/dist/deterministicChecks.js.map +1 -1
- package/dist/evalOutput.d.ts +17 -4
- package/dist/evalOutput.js +40 -5
- package/dist/evalOutput.js.map +1 -1
- package/dist/evalRunner.d.ts +68 -15
- package/dist/evalRunner.js +309 -55
- package/dist/evalRunner.js.map +1 -1
- package/dist/evalSuite.d.ts +32 -11
- package/dist/evalSuite.js +487 -42
- package/dist/evalSuite.js.map +1 -1
- package/dist/evalTypes.d.ts +187 -15
- package/dist/index.d.ts +5 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/pipelineCli.d.ts +102 -0
- package/dist/pipelineCli.js +348 -0
- package/dist/pipelineCli.js.map +1 -0
- package/dist/reporters/drive.d.ts +14 -0
- package/dist/reporters/drive.js +49 -0
- package/dist/reporters/drive.js.map +1 -0
- package/dist/reporters/registry.d.ts +11 -0
- package/dist/reporters/registry.js +26 -0
- package/dist/reporters/registry.js.map +1 -0
- package/dist/reporters/reporterTypes.d.ts +36 -0
- package/dist/reporters/reporterTypes.js +2 -0
- package/dist/reporters/reporterTypes.js.map +1 -0
- package/dist/reporters/textReporter.d.ts +10 -0
- package/dist/reporters/textReporter.js +50 -0
- package/dist/reporters/textReporter.js.map +1 -0
- package/dist/toolChecks.d.ts +21 -0
- package/dist/toolChecks.js +38 -0
- package/dist/toolChecks.js.map +1 -0
- package/package.json +6 -3
package/dist/evalRunner.js
CHANGED
|
@@ -1,53 +1,225 @@
|
|
|
1
1
|
import { runBatchMatrix } from '#src/BatchRunner.js';
|
|
2
2
|
import { runDeterministicChecks } from '#src/deterministicChecks.js';
|
|
3
|
+
import { runToolCallChecks } from '#src/toolChecks.js';
|
|
4
|
+
/** A block applies to an identity when it names no identities (unscoped → all) or explicitly names
|
|
5
|
+
* this one. For the single-run pseudo-identity (`undefined`) only unscoped blocks apply — and the
|
|
6
|
+
* parser guarantees a no-identities suite has only unscoped blocks. */
|
|
7
|
+
function expectationAppliesTo(expectation, identity) {
|
|
8
|
+
if (!expectation.identities || expectation.identities.length === 0)
|
|
9
|
+
return true;
|
|
10
|
+
return identity !== undefined && expectation.identities.includes(identity);
|
|
11
|
+
}
|
|
3
12
|
/**
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
* single-row/single-model matrix per case for no benefit (no `{{field}}` interpolation applies to
|
|
8
|
-
* an eval case's literal `prompt`).
|
|
9
|
-
*
|
|
10
|
-
* `cell.id` is the case's own `id` (not a `cell-<m>-<r>` index) so per-case JSON output filenames
|
|
11
|
-
* read naturally (`<case-id>.json` — see `#src/evalOutput.js`) and results are trivially traceable
|
|
12
|
-
* back to the authored case. `parseEvalSuite` already rejects duplicate case ids, so this can't
|
|
13
|
-
* collide.
|
|
13
|
+
* Reduce the suite to its (case × identity) units. A suite with no `identities` reduces to a single
|
|
14
|
+
* pseudo-identity (`undefined`) so the runner has ONE code path — the whole point of the BATCH-12
|
|
15
|
+
* design: flat cases are just the one-identity, one-unscoped-block special case of the matrix.
|
|
14
16
|
*/
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
function buildUnits(suite) {
|
|
18
|
+
const identities = suite.identities && suite.identities.length > 0 ? suite.identities : [undefined];
|
|
19
|
+
const units = [];
|
|
20
|
+
for (const evalCase of suite.cases) {
|
|
21
|
+
for (const identity of identities) {
|
|
22
|
+
units.push({
|
|
23
|
+
cellId: identity === undefined ? evalCase.id : `${evalCase.id}__${identity}`,
|
|
24
|
+
evalCase,
|
|
25
|
+
identity,
|
|
26
|
+
// BATCH-12 Task 2: filter EACH turn's blocks for this identity (length 1 = single-turn).
|
|
27
|
+
applicablePerTurn: evalCase.turns.map((turn) => turn.expectations.filter((e) => expectationAppliesTo(e, identity))),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return units;
|
|
22
32
|
}
|
|
23
33
|
/**
|
|
24
|
-
* Run every case
|
|
25
|
-
* BATCH-1's `runBatchMatrix`), then grade each answer with
|
|
26
|
-
* declares a rubric) the judge. A
|
|
27
|
-
*
|
|
28
|
-
*
|
|
34
|
+
* Run every (case × identity) cell of the suite through the SUT ({@link RunCellFn}, pooled via
|
|
35
|
+
* BATCH-1's `runBatchMatrix`), then grade each answer with the applicable expectation blocks'
|
|
36
|
+
* deterministic checks, tool-trace checks, and (when a block declares a rubric) the judge. A cell
|
|
37
|
+
* PASSES iff EVERY applicable block passes — ported from the field user's proven harness semantics
|
|
38
|
+
* (`docs/batch-eval-user-requirements.md` Appendix A), generalized to per-identity blocks.
|
|
39
|
+
*
|
|
40
|
+
* ONE concurrency pool: flat and matrix suites both normalize to {@link EvalUnit}s and ride the same
|
|
41
|
+
* `runBatchMatrix` pool; the only divergence is which `RunCellFn` a unit's identity resolves to.
|
|
42
|
+
*
|
|
43
|
+
* BATCH-12 Task 2 — a unit whose case has `turns.length > 1` is a MULTI-TURN conversation: it runs
|
|
44
|
+
* through the injected {@link RunConversationFn} seam (agent/tools built once, messages accumulated
|
|
45
|
+
* across turns) instead of {@link RunCellFn}, still inside the SAME pool, and is graded turn-by-turn
|
|
46
|
+
* by {@link gradeConversationUnit} — the cell PASSES iff EVERY turn's applicable blocks pass. A
|
|
47
|
+
* single-turn unit keeps the proven `runCell` + {@link gradeUnit} path byte-for-byte.
|
|
29
48
|
*/
|
|
30
49
|
export async function runEvalSuite(suite, options) {
|
|
31
|
-
const
|
|
50
|
+
const units = buildUnits(suite);
|
|
51
|
+
// I1 — dispatch AND grading are keyed by the guaranteed-unique `inputIndex` (a unit's position in
|
|
52
|
+
// this array, threaded onto its `MatrixCell`/`CellResult`), NEVER by the composite `cellId`
|
|
53
|
+
// (`<caseId>__<identity>`). Both case ids and identity names permit `__`, so distinct cells can
|
|
54
|
+
// collapse to the same `cellId` (e.g. case `x` + identity `y__z` and case `x__y` + identity `z`
|
|
55
|
+
// both → `x__y__z`). A `cellId`-keyed Map would silently run/grade one cell under the WRONG
|
|
56
|
+
// identity and drop the other — the worst failure for an authorization matrix. `cellId` is kept
|
|
57
|
+
// for display and per-cell output filenames only (`evalOutput.ts` guards the filename collision).
|
|
58
|
+
const unitByInputIndex = new Map(units.map((unit, index) => [index, unit]));
|
|
59
|
+
// Resolve the RunCellFn for a unit's identity: the single `runCell` for the no-identities path,
|
|
60
|
+
// else the per-identity runCell the command built. A missing runCell is a wiring bug — surface it
|
|
61
|
+
// as a thrown error (caught by `runBatchMatrix` as a failed cell) rather than a silent skip.
|
|
62
|
+
const runCellFor = (identity) => {
|
|
63
|
+
if (identity === undefined) {
|
|
64
|
+
if (!options.runCell) {
|
|
65
|
+
throw new Error('runEvalSuite: no `runCell` provided for the single-run (no-identities) path.');
|
|
66
|
+
}
|
|
67
|
+
return options.runCell;
|
|
68
|
+
}
|
|
69
|
+
const identityRunCell = options.runCellByIdentity?.get(identity);
|
|
70
|
+
if (!identityRunCell) {
|
|
71
|
+
throw new Error(`runEvalSuite: no runCell provided for identity "${identity}".`);
|
|
72
|
+
}
|
|
73
|
+
return identityRunCell;
|
|
74
|
+
};
|
|
75
|
+
// BATCH-12 Task 2 — the MULTI-TURN counterpart to `runCellFor`: resolve the conversational seam
|
|
76
|
+
// for a unit's identity. Same missing-seam-is-a-wiring-bug discipline (throw → caught by
|
|
77
|
+
// `runBatchMatrix` as a failed cell → the whole conversation FAILs) rather than a silent skip.
|
|
78
|
+
const runConversationFor = (identity) => {
|
|
79
|
+
if (identity === undefined) {
|
|
80
|
+
if (!options.runConversation) {
|
|
81
|
+
throw new Error('runEvalSuite: no `runConversation` provided for the multi-turn (no-identities) path.');
|
|
82
|
+
}
|
|
83
|
+
return options.runConversation;
|
|
84
|
+
}
|
|
85
|
+
const identityRunConversation = options.runConversationByIdentity?.get(identity);
|
|
86
|
+
if (!identityRunConversation) {
|
|
87
|
+
throw new Error(`runEvalSuite: no runConversation provided for identity "${identity}".`);
|
|
88
|
+
}
|
|
89
|
+
return identityRunConversation;
|
|
90
|
+
};
|
|
91
|
+
const cells = units.map((unit, index) => ({
|
|
92
|
+
id: unit.cellId,
|
|
93
|
+
modelIndex: 0,
|
|
94
|
+
inputIndex: index,
|
|
95
|
+
// Single-turn dispatch reads `content`; the multi-turn branch reads the unit's turns directly
|
|
96
|
+
// (a MatrixCell has one `content`, so it can't carry a conversation) — turns[0].user is a
|
|
97
|
+
// harmless placeholder there.
|
|
98
|
+
content: unit.evalCase.turns[0].user,
|
|
99
|
+
}));
|
|
100
|
+
// BATCH-12 Task 2 — per-conversation per-turn outcomes, keyed by the unique `inputIndex` (a
|
|
101
|
+
// side-channel so a whole conversation still rides ONE `runBatchMatrix` pool with the single-turn
|
|
102
|
+
// cells — `CellRunOutcome` carries one answer, a conversation carries N). Only multi-turn cells
|
|
103
|
+
// populate this; single-turn cells grade straight from their `CellResult` as before.
|
|
104
|
+
const conversationOutcomes = new Map();
|
|
105
|
+
const dispatchRunCell = async (cell) => {
|
|
106
|
+
const unit = unitByInputIndex.get(cell.inputIndex);
|
|
107
|
+
// Single-turn: the proven `runSingleShot`-backed path, byte-for-byte (unchanged dispatch).
|
|
108
|
+
if (unit.evalCase.turns.length <= 1) {
|
|
109
|
+
return runCellFor(unit.identity)(cell);
|
|
110
|
+
}
|
|
111
|
+
// Multi-turn: run the whole conversation ONCE (agent/tools built once), stash the per-turn
|
|
112
|
+
// outcomes for grading, and report a synthetic pool outcome (`ok` iff every turn ran) so the
|
|
113
|
+
// pool's own bookkeeping is coherent — grading reads the stash, not this outcome's answer.
|
|
114
|
+
const outcomes = await runConversationFor(unit.identity)(unit.evalCase.turns.map((turn) => turn.user));
|
|
115
|
+
conversationOutcomes.set(cell.inputIndex, outcomes);
|
|
116
|
+
return { ok: outcomes.length > 0 && outcomes.every((o) => o.ok) };
|
|
117
|
+
};
|
|
32
118
|
const cellResults = await runBatchMatrix(cells, {
|
|
33
|
-
runCell:
|
|
119
|
+
runCell: dispatchRunCell,
|
|
34
120
|
concurrency: options.concurrency,
|
|
35
121
|
});
|
|
36
|
-
const casesById = new Map(suite.cases.map((evalCase) => [evalCase.id, evalCase]));
|
|
37
122
|
const results = [];
|
|
38
123
|
for (const cellResult of cellResults) {
|
|
39
|
-
const
|
|
40
|
-
/* istanbul ignore next --
|
|
41
|
-
|
|
124
|
+
const unit = unitByInputIndex.get(cellResult.inputIndex);
|
|
125
|
+
/* istanbul ignore next -- inputIndex is derived 1:1 from units (each cell's inputIndex is its
|
|
126
|
+
unit's array position, preserved through runBatchMatrix), so every result maps to one unit */
|
|
127
|
+
if (!unit)
|
|
42
128
|
continue;
|
|
43
|
-
|
|
129
|
+
if (unit.evalCase.turns.length <= 1) {
|
|
130
|
+
results.push(await gradeUnit(unit, cellResult, options.judge));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
results.push(await gradeConversationUnit(unit, conversationOutcomes.get(cellResult.inputIndex), cellResult, options.judge));
|
|
134
|
+
}
|
|
44
135
|
}
|
|
45
136
|
const passed = results.filter((result) => result.verdict === 'PASS').length;
|
|
46
137
|
return { total: results.length, passed, failed: results.length - passed, cases: results };
|
|
47
138
|
}
|
|
48
|
-
|
|
139
|
+
/**
|
|
140
|
+
* BATCH-11 (#405 his #6) — classify a completed suite's {@link EvalSuiteSummary} into a distinct
|
|
141
|
+
* exit code so CI can tell a product regression from a broken harness. BATCH-12 counts matrix
|
|
142
|
+
* CELLS (one per case × identity), not cases:
|
|
143
|
+
*
|
|
144
|
+
* - `0` — every cell passed (unchanged contract).
|
|
145
|
+
* - `1` — the suite **ran** but ≥1 cell FAILED (assertion/judge below threshold). A *product*
|
|
146
|
+
* signal: real, gradeable results, some below the bar.
|
|
147
|
+
* - `2` — **harness error**: no gradeable results at all — an empty suite, or **every** cell's SUT
|
|
148
|
+
* run failed (`sutOk === false`). (The other harness errors that never reach a summary — suite
|
|
149
|
+
* load/parse error, config error, an unresolved identity precondition — are mapped to `2` by the
|
|
150
|
+
* caller (`evalCommand.ts`) in a try/catch.)
|
|
151
|
+
*
|
|
152
|
+
* Classification is anchored on `sutOk`, not the verdict: a cell that ran (`sutOk === true`) but
|
|
153
|
+
* whose judge errored (or whose answer failed a check) is a real result → exit `1`, never `2`. A
|
|
154
|
+
* *mix* of `sutOk:false` and `sutOk:true` cells therefore yields `1`.
|
|
155
|
+
*/
|
|
156
|
+
export function classifyEvalExit(summary) {
|
|
157
|
+
// No gradeable results at all → harness/environment error, not a product signal.
|
|
158
|
+
if (summary.total === 0 || summary.cases.every((result) => !result.sutOk)) {
|
|
159
|
+
return 2;
|
|
160
|
+
}
|
|
161
|
+
// Ran and produced gradeable results, but at least one cell failed → product regression.
|
|
162
|
+
if (summary.failed > 0) {
|
|
163
|
+
return 1;
|
|
164
|
+
}
|
|
165
|
+
// Every cell passed.
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Grade ONE answer (+ its tool trace) against a set of APPLICABLE expectation blocks — the shared
|
|
170
|
+
* inner loop of both the single-turn {@link gradeUnit} and the multi-turn {@link gradeConversationUnit}.
|
|
171
|
+
* Runs each block's deterministic checks, tool-trace checks, and judge (when it declares a rubric),
|
|
172
|
+
* accumulating failure `reasons` (a passing block/judge appends nothing). `judgeOutcome` is the FIRST
|
|
173
|
+
* block that declared a judge, so a single-block case's `judge` field is exactly that block's outcome
|
|
174
|
+
* (back-compat). For a single applicable block this reproduces the pre-BATCH-12 grading byte-for-byte.
|
|
175
|
+
*/
|
|
176
|
+
async function gradeApplicableBlocks(answer, tools, applicable, passThreshold, judge) {
|
|
177
|
+
const deterministicFailures = [];
|
|
178
|
+
const reasons = [];
|
|
179
|
+
let judgeOutcome;
|
|
180
|
+
for (const block of applicable) {
|
|
181
|
+
const checks = runDeterministicChecks(answer, block);
|
|
182
|
+
// Tool-trace assertions read the captured tool names, not the answer, so they are graded
|
|
183
|
+
// separately (see #src/toolChecks.js) and merged into `reasons` alongside the answer checks.
|
|
184
|
+
const toolFailures = runToolCallChecks(tools, block);
|
|
185
|
+
deterministicFailures.push(...checks.failures);
|
|
186
|
+
reasons.push(...checks.failures, ...toolFailures);
|
|
187
|
+
if (block.judgeRubric) {
|
|
188
|
+
let outcome;
|
|
189
|
+
if (!judge) {
|
|
190
|
+
outcome = { attempted: false, ok: false, error: 'No judge configured for this suite.' };
|
|
191
|
+
reasons.push('judge: not configured');
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
outcome = await judge(answer, block.judgeRubric);
|
|
195
|
+
if (!outcome.ok || !outcome.verdict) {
|
|
196
|
+
reasons.push(`judge error: ${outcome.error ?? 'unknown error'}`);
|
|
197
|
+
}
|
|
198
|
+
else if (outcome.verdict.rate < passThreshold) {
|
|
199
|
+
reasons.push(`judge rate ${outcome.verdict.rate}/10 below threshold ${passThreshold}` +
|
|
200
|
+
(outcome.verdict.reason ? `: ${outcome.verdict.reason}` : ''));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (judgeOutcome === undefined)
|
|
204
|
+
judgeOutcome = outcome;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return { reasons, deterministicFailures, judgeOutcome };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Grade one SINGLE-TURN (case × identity) cell: run its (single turn's) APPLICABLE expectation
|
|
211
|
+
* blocks' deterministic checks, tool-trace checks, and judge; the cell PASSES iff every applicable
|
|
212
|
+
* block passes. For a flat case (exactly one unscoped applicable block) this produces byte-for-byte
|
|
213
|
+
* the same `reasons`/`checks`/`judge`/`verdict` as the pre-BATCH-12 single-block grader.
|
|
214
|
+
*/
|
|
215
|
+
async function gradeUnit(unit, cellResult, judge) {
|
|
216
|
+
const { evalCase, identity, applicablePerTurn } = unit;
|
|
217
|
+
const applicable = applicablePerTurn[0];
|
|
49
218
|
const base = {
|
|
50
219
|
id: evalCase.id,
|
|
220
|
+
// Omit `identity` entirely (rather than set it to `undefined`) for the no-identities path, so
|
|
221
|
+
// its `<id>.json` output stays byte-for-byte identical to before BATCH-12.
|
|
222
|
+
...(identity !== undefined ? { identity } : {}),
|
|
51
223
|
passThreshold: evalCase.passThreshold,
|
|
52
224
|
answer: cellResult.answer,
|
|
53
225
|
tokensInput: cellResult.tokensInput,
|
|
@@ -63,38 +235,120 @@ async function gradeCase(evalCase, cellResult, judge) {
|
|
|
63
235
|
reasons: [cellResult.error ? `SUT run failed: ${cellResult.error}` : 'SUT run failed.'],
|
|
64
236
|
};
|
|
65
237
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
judgeOutcome = await judge(answer, evalCase.judgeRubric);
|
|
79
|
-
if (!judgeOutcome.ok || !judgeOutcome.verdict) {
|
|
80
|
-
judgePassed = false;
|
|
81
|
-
reasons.push(`judge error: ${judgeOutcome.error ?? 'unknown error'}`);
|
|
82
|
-
}
|
|
83
|
-
else if (judgeOutcome.verdict.rate < evalCase.passThreshold) {
|
|
84
|
-
judgePassed = false;
|
|
85
|
-
reasons.push(`judge rate ${judgeOutcome.verdict.rate}/10 below threshold ${evalCase.passThreshold}` +
|
|
86
|
-
(judgeOutcome.verdict.reason ? `: ${judgeOutcome.verdict.reason}` : ''));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
238
|
+
// NO-SILENT-PASS backstop (M2): a cell with zero applicable blocks is a suite-AUTHORING error, not
|
|
239
|
+
// a product regression — nothing would grade this (case × identity). The parser rejects this
|
|
240
|
+
// statically when the suite declares identities, so this branch is only reachable if that guard is
|
|
241
|
+
// bypassed; when it is, THROW so the eval command's catch classifies it as a harness error
|
|
242
|
+
// (exit 2), never a product FAIL (exit 1) or, worse, a silent trivial pass. Throwing aborts the
|
|
243
|
+
// whole suite, which is acceptable precisely because this is unreachable under the parse-time guard.
|
|
244
|
+
if (applicable.length === 0) {
|
|
245
|
+
throw new Error(`no applicable expectation block for cell "${unit.cellId}" (identity ` +
|
|
246
|
+
`"${identity ?? '(default)'}") — nothing would grade this (case × identity), which is a ` +
|
|
247
|
+
'suite-authoring error.');
|
|
89
248
|
}
|
|
90
|
-
const
|
|
249
|
+
const answer = cellResult.answer ?? '';
|
|
250
|
+
const tools = cellResult.tools ?? [];
|
|
251
|
+
const { reasons, deterministicFailures, judgeOutcome } = await gradeApplicableBlocks(answer, tools, applicable, evalCase.passThreshold, judge);
|
|
252
|
+
// A cell PASSES iff nothing was recorded against it: every applicable block's deterministic
|
|
253
|
+
// checks, tool-trace checks, and judge all passed (a passing judge appends no reason). For a
|
|
254
|
+
// single applicable block this is exactly the pre-BATCH-12 verdict.
|
|
255
|
+
const verdict = reasons.length === 0 ? 'PASS' : 'FAIL';
|
|
91
256
|
return {
|
|
92
257
|
...base,
|
|
93
258
|
verdict,
|
|
94
259
|
sutOk: true,
|
|
95
|
-
checks,
|
|
260
|
+
checks: { passed: deterministicFailures.length === 0, failures: deterministicFailures },
|
|
96
261
|
judge: judgeOutcome,
|
|
97
262
|
reasons,
|
|
98
263
|
};
|
|
99
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* Grade one MULTI-TURN (case × identity) cell (BATCH-12 Task 2): grade EACH turn's answer + per-turn
|
|
267
|
+
* tool trace against THAT turn's applicable blocks, then roll them up. The cell PASSES iff EVERY turn
|
|
268
|
+
* PASSes; its top-level `reasons` are every turn's reasons each prefixed with the failing turn
|
|
269
|
+
* (`turn N: …`) so the summary/output pinpoints which turn broke, and the full per-turn breakdown is
|
|
270
|
+
* in `turns`.
|
|
271
|
+
*
|
|
272
|
+
* `outcomes` is what the conversational runner returned (one per turn it attempted). A SHORT array
|
|
273
|
+
* (the conversation aborted mid-way) or a MISSING one (`undefined` — the runner threw, so
|
|
274
|
+
* `runBatchMatrix` recorded a failed cell with no stash) fails the un-run turns with a clear reason.
|
|
275
|
+
* `sutOk` is TRUE iff at least one turn actually ran — so a totally-failed conversation is
|
|
276
|
+
* `sutOk:false` (exit-2-eligible, like a single-shot SUT failure) while a turn-1-ran/turn-2-failed
|
|
277
|
+
* cell is `sutOk:true`, a real product signal → exit 1.
|
|
278
|
+
*/
|
|
279
|
+
async function gradeConversationUnit(unit, outcomes, cellResult, judge) {
|
|
280
|
+
const { evalCase, identity, applicablePerTurn } = unit;
|
|
281
|
+
const turnOutcomes = outcomes ?? [];
|
|
282
|
+
const turnResults = [];
|
|
283
|
+
const cellReasons = [];
|
|
284
|
+
let anyTurnRan = false;
|
|
285
|
+
for (let i = 0; i < evalCase.turns.length; i++) {
|
|
286
|
+
const turn = evalCase.turns[i];
|
|
287
|
+
const applicable = applicablePerTurn[i];
|
|
288
|
+
// NO-SILENT-PASS backstop (per turn × identity), mirroring gradeUnit's: unreachable under the
|
|
289
|
+
// parse-time guard, but if bypassed THROW so it's a harness error (exit 2), never a silent pass.
|
|
290
|
+
if (applicable.length === 0) {
|
|
291
|
+
throw new Error(`no applicable expectation block for cell "${unit.cellId}" (identity ` +
|
|
292
|
+
`"${identity ?? '(default)'}") turn ${i} — nothing would grade this (turn × identity), ` +
|
|
293
|
+
'which is a suite-authoring error.');
|
|
294
|
+
}
|
|
295
|
+
const outcome = turnOutcomes[i];
|
|
296
|
+
const label = `turn ${i + 1}`;
|
|
297
|
+
if (!outcome || !outcome.ok) {
|
|
298
|
+
// This turn produced no answer: the conversation errored on it, or aborted before reaching it.
|
|
299
|
+
let detail;
|
|
300
|
+
if (outcome?.error)
|
|
301
|
+
detail = `SUT run failed: ${outcome.error}`;
|
|
302
|
+
else if (outcome)
|
|
303
|
+
detail = 'SUT run failed.';
|
|
304
|
+
else if (outcomes === undefined)
|
|
305
|
+
detail = `SUT run failed: ${cellResult.error ?? 'the conversation could not run.'}`;
|
|
306
|
+
else
|
|
307
|
+
detail = 'SUT run failed: the conversation ended before this turn.';
|
|
308
|
+
turnResults.push({
|
|
309
|
+
user: turn.user,
|
|
310
|
+
answer: outcome?.answer,
|
|
311
|
+
tokensInput: outcome?.tokensInput,
|
|
312
|
+
tokensOutput: outcome?.tokensOutput,
|
|
313
|
+
tools: outcome?.tools,
|
|
314
|
+
ok: false,
|
|
315
|
+
verdict: 'FAIL',
|
|
316
|
+
reasons: [detail],
|
|
317
|
+
});
|
|
318
|
+
cellReasons.push(`${label}: ${detail}`);
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
anyTurnRan = true;
|
|
322
|
+
const answer = outcome.answer ?? '';
|
|
323
|
+
const tools = outcome.tools ?? [];
|
|
324
|
+
const { reasons, deterministicFailures, judgeOutcome } = await gradeApplicableBlocks(answer, tools, applicable, evalCase.passThreshold, judge);
|
|
325
|
+
const turnVerdict = reasons.length === 0 ? 'PASS' : 'FAIL';
|
|
326
|
+
turnResults.push({
|
|
327
|
+
user: turn.user,
|
|
328
|
+
answer: outcome.answer,
|
|
329
|
+
tokensInput: outcome.tokensInput,
|
|
330
|
+
tokensOutput: outcome.tokensOutput,
|
|
331
|
+
tools: outcome.tools,
|
|
332
|
+
ok: true,
|
|
333
|
+
verdict: turnVerdict,
|
|
334
|
+
checks: { passed: deterministicFailures.length === 0, failures: deterministicFailures },
|
|
335
|
+
judge: judgeOutcome,
|
|
336
|
+
reasons,
|
|
337
|
+
});
|
|
338
|
+
for (const reason of reasons)
|
|
339
|
+
cellReasons.push(`${label}: ${reason}`);
|
|
340
|
+
}
|
|
341
|
+
const verdict = cellReasons.length === 0 ? 'PASS' : 'FAIL';
|
|
342
|
+
return {
|
|
343
|
+
id: evalCase.id,
|
|
344
|
+
...(identity !== undefined ? { identity } : {}),
|
|
345
|
+
passThreshold: evalCase.passThreshold,
|
|
346
|
+
// A multi-turn cell has no single answer/tools/checks/judge — read the per-turn breakdown below.
|
|
347
|
+
durationMs: cellResult.durationMs,
|
|
348
|
+
verdict,
|
|
349
|
+
sutOk: anyTurnRan,
|
|
350
|
+
reasons: cellReasons,
|
|
351
|
+
turns: turnResults,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
100
354
|
//# sourceMappingURL=evalRunner.js.map
|
package/dist/evalRunner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evalRunner.js","sourceRoot":"","sources":["../src/evalRunner.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"evalRunner.js","sourceRoot":"","sources":["../src/evalRunner.ts"],"names":[],"mappings":"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"}
|
package/dist/evalSuite.d.ts
CHANGED
|
@@ -2,21 +2,42 @@ import type { EvalSuite } from '#src/evalTypes.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Parse and validate an eval suite YAML document into a normalized {@link EvalSuite}.
|
|
4
4
|
*
|
|
5
|
+
* Every case is normalized to ONE shape — `turns: [{ user, expectations: EvalExpectation[] }]` (Task
|
|
6
|
+
* 1: exactly one turn). A flat case is sugar for one unscoped expectation (applies to every
|
|
7
|
+
* identity); a matrix case's `expect:` array becomes the expectation list directly.
|
|
8
|
+
*
|
|
5
9
|
* Rejects, with a clear message, at parse time (never silently no-ops or defers to run time):
|
|
6
10
|
* - Malformed YAML.
|
|
7
11
|
* - A suite shape that doesn't match {@link RawSuiteSchema} (missing/wrong-typed fields).
|
|
8
|
-
* - `target.type` other than `"gth-agent"` — pluggable CLI/HTTP
|
|
9
|
-
*
|
|
12
|
+
* - `target.type` other than `"gth-agent"`, `"adk-agent"`, or `"ag-ui"` — other pluggable CLI/HTTP
|
|
13
|
+
* targets are out of scope.
|
|
10
14
|
* - `target.profile` set to anything other than `"default"`/absent — a single suite-wide profile
|
|
11
|
-
* switch is the
|
|
12
|
-
*
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* switch is the `--identities` direction, replaced by the suite-level `identities` list.
|
|
16
|
+
* - A `"adk-agent"` (BATCH-14) target missing its `url`; an `adk-agent` suite that ALSO uses the
|
|
17
|
+
* `identities` matrix (per-identity gth configs are meaningless for an external agent); or an
|
|
18
|
+
* `adk-agent` suite that uses `must_call`/`must_not_call` (A2A does not expose the agent's tool
|
|
19
|
+
* trace, so grading a tool-call assertion is impossible — rejected rather than silently passed).
|
|
20
|
+
* - An `"ag-ui"` (BATCH-15) target missing its `url` or its `agent_id`; an `ag-ui` target carrying a
|
|
21
|
+
* `profile`; or an `ag-ui` suite that ALSO uses the `identities` matrix (per-identity gth configs
|
|
22
|
+
* are meaningless for an external agent). NOTE `must_call`/`must_not_call` ARE supported for an
|
|
23
|
+
* `ag-ui` target — the AG-UI wire streams `TOOL_CALL_START`, so the tool trace is captured and
|
|
24
|
+
* graded (this is the key difference from `adk-agent`).
|
|
25
|
+
* - A case `id`, or a suite `identities` name, containing anything other than alphanumerics,
|
|
26
|
+
* dashes, underscores, or dots (both double as output filenames — path traversal is rejected).
|
|
27
|
+
* - A duplicate case `id`, or a duplicate `identities` entry.
|
|
28
|
+
* - A case declaring BOTH `prompt` and `turns:`, or NEITHER (a case is single- or multi-turn).
|
|
29
|
+
* - A single-turn case with a missing/blank `prompt`; a multi-turn turn with a missing/blank `user`;
|
|
30
|
+
* an empty `turns:` array.
|
|
31
|
+
* - A multi-turn case that ALSO declares case-level assertions or an `expect:` array (those live on
|
|
32
|
+
* each turn for a multi-turn case).
|
|
33
|
+
* - A case (or turn) declaring BOTH flat assertions AND an `expect:` array (one way per case/turn).
|
|
34
|
+
* - An `expect:` block referencing an identity the suite does not declare.
|
|
35
|
+
* - A (turn × identity) with no applicable expectation block (statically determinable → rejected
|
|
36
|
+
* here; the runner also guards it as a per-cell FAIL as a backstop).
|
|
37
|
+
* - A flat case or `expect:` block with no checks of any kind AND no `judge` rubric.
|
|
38
|
+
* - An invalid `must_match`/`must_not_match` regex, or a `json_path` entry not setting exactly one
|
|
39
|
+
* of `equals`/`contains`.
|
|
40
|
+
* - A `judge_profile` containing a path separator or `..`.
|
|
20
41
|
*
|
|
21
42
|
* @param yamlText Raw suite file content.
|
|
22
43
|
* @param sourcePath Optional path, only used to make error messages more actionable.
|