@anvia/core 1.0.7 → 1.0.9
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 +168 -3
- package/dist/agent/index.d.ts +12 -12
- package/dist/agent/index.js +5 -5
- package/dist/agent/interactions/index.d.ts +1 -1
- package/dist/{agent-B-3jCRmI.d.ts → agent-DTmzs1Qr.d.ts} +16 -13
- package/dist/chunk-4UQALF5Y.js +117 -0
- package/dist/chunk-4UQALF5Y.js.map +1 -0
- package/dist/{chunk-HNRNTFMQ.js → chunk-GAOZ263Y.js} +15 -4
- package/dist/chunk-GAOZ263Y.js.map +1 -0
- package/dist/{chunk-XMVOBX43.js → chunk-JLJMVRRE.js} +2 -2
- package/dist/{chunk-DHJL44R2.js → chunk-JTJU56ZV.js} +2 -2
- package/dist/{chunk-TB5EKZM7.js → chunk-OI3LSMJG.js} +30 -1
- package/dist/chunk-OI3LSMJG.js.map +1 -0
- package/dist/{chunk-34EDX4ZL.js → chunk-Q5BCNYED.js} +2 -2
- package/dist/completion/index.d.ts +3 -3
- package/dist/completion/index.js +10 -2
- package/dist/{dynamic-tools-aIZlg5x1.d.ts → dynamic-tools-CNHSPXr1.d.ts} +3 -3
- package/dist/evals/index.d.ts +195 -49
- package/dist/evals/index.js +617 -126
- package/dist/evals/index.js.map +1 -1
- package/dist/extractor/index.d.ts +1 -1
- package/dist/extractor/index.js +3 -3
- package/dist/guardrails/index.d.ts +4 -4
- package/dist/image-generation/index.d.ts +1 -1
- package/dist/index.d.ts +14 -14
- package/dist/index.js +10 -5
- package/dist/internal/agent.d.ts +11 -11
- package/dist/internal/agent.js +6 -5
- package/dist/internal/agent.js.map +1 -1
- package/dist/mcp/index.d.ts +4 -4
- package/dist/memory/index.d.ts +3 -3
- package/dist/memory/index.js +2 -2
- package/dist/{middleware-kcF8AusP.d.ts → middleware-mY_lpjDp.d.ts} +1 -1
- package/dist/observability/index.d.ts +6 -6
- package/dist/observability/index.js +3 -3
- package/dist/pipeline/index.d.ts +12 -12
- package/dist/pipeline/index.js +3 -3
- package/dist/pipeline/index.js.map +1 -1
- package/dist/{provider-output-error-BEwDgVv3.d.ts → provider-output-error-OQ4fjw5W.d.ts} +10 -2
- package/dist/skills/index.d.ts +4 -4
- package/dist/speech-generation/index.d.ts +1 -1
- package/dist/{text-ZLECpQE7.d.ts → text-CmjXxakS.d.ts} +1 -1
- package/dist/{think-tool-BwCsJ365.d.ts → think-tool-YWsV98I5.d.ts} +1 -1
- package/dist/tool/index.d.ts +6 -6
- package/dist/{tool-BpqpoRSE.d.ts → tool-DQMZpZSS.d.ts} +1 -1
- package/dist/transcription/index.d.ts +1 -1
- package/dist/{types-DC1U1XwW.d.ts → types-C1xYKXF2.d.ts} +1 -1
- package/dist/{types-CXNE592e.d.ts → types-CODGMHnp.d.ts} +1 -1
- package/dist/{types-DL7wX6hJ.d.ts → types-CRda8x7p.d.ts} +8 -7
- package/dist/{types-DmzRCbU5.d.ts → types-CafU6Ux5.d.ts} +1 -1
- package/dist/{types-DgvozfPc.d.ts → types-CiHjqE6J.d.ts} +19 -3
- package/dist/{types-DjqRHeAi.d.ts → types-DfRPfehE.d.ts} +2 -2
- package/dist/{types-DhkodEft.d.ts → types-h0EL6Xn0.d.ts} +1 -1
- package/dist/vector-store/index.d.ts +4 -4
- package/package.json +1 -1
- package/dist/chunk-D4PACGPA.js +0 -37
- package/dist/chunk-D4PACGPA.js.map +0 -1
- package/dist/chunk-HNRNTFMQ.js.map +0 -1
- package/dist/chunk-TB5EKZM7.js.map +0 -1
- /package/dist/{chunk-XMVOBX43.js.map → chunk-JLJMVRRE.js.map} +0 -0
- /package/dist/{chunk-DHJL44R2.js.map → chunk-JTJU56ZV.js.map} +0 -0
- /package/dist/{chunk-34EDX4ZL.js.map → chunk-Q5BCNYED.js.map} +0 -0
package/dist/evals/index.js
CHANGED
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
} from "../chunk-IZNOP6JG.js";
|
|
11
11
|
import {
|
|
12
12
|
extract
|
|
13
|
-
} from "../chunk-
|
|
14
|
-
import "../chunk-
|
|
13
|
+
} from "../chunk-JTJU56ZV.js";
|
|
14
|
+
import "../chunk-4UQALF5Y.js";
|
|
15
15
|
import {
|
|
16
16
|
Usage
|
|
17
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-OI3LSMJG.js";
|
|
18
18
|
import "../chunk-7JLAIN6E.js";
|
|
19
19
|
import "../chunk-QGX73TSQ.js";
|
|
20
20
|
import {
|
|
@@ -32,18 +32,15 @@ function defaultOutputValue(output) {
|
|
|
32
32
|
}
|
|
33
33
|
return output;
|
|
34
34
|
}
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
return value;
|
|
38
|
-
}
|
|
39
|
-
return JSON.stringify(value);
|
|
35
|
+
function evalValuesEqual(left, right) {
|
|
36
|
+
return deepEqual(left, right, /* @__PURE__ */ new WeakMap());
|
|
40
37
|
}
|
|
41
38
|
function formatValue(value) {
|
|
42
39
|
if (typeof value === "string") {
|
|
43
40
|
return value;
|
|
44
41
|
}
|
|
45
42
|
try {
|
|
46
|
-
return JSON.stringify(value);
|
|
43
|
+
return JSON.stringify(value) ?? String(value);
|
|
47
44
|
} catch {
|
|
48
45
|
return String(value);
|
|
49
46
|
}
|
|
@@ -51,6 +48,34 @@ function formatValue(value) {
|
|
|
51
48
|
function errorMessage(error) {
|
|
52
49
|
return error instanceof Error ? error.message : String(error);
|
|
53
50
|
}
|
|
51
|
+
function deepEqual(left, right, seen) {
|
|
52
|
+
if (Object.is(left, right)) return true;
|
|
53
|
+
if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (Object.getPrototypeOf(left) !== Object.getPrototypeOf(right)) return false;
|
|
57
|
+
if (left instanceof Date && right instanceof Date) return left.getTime() === right.getTime();
|
|
58
|
+
if (left instanceof RegExp && right instanceof RegExp) return String(left) === String(right);
|
|
59
|
+
if (!Array.isArray(left) && Object.getPrototypeOf(left) !== Object.prototype && Object.getPrototypeOf(left) !== null) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (seen.get(left) === right) return true;
|
|
63
|
+
seen.set(left, right);
|
|
64
|
+
const leftKeys = Reflect.ownKeys(left);
|
|
65
|
+
const rightKeys = Reflect.ownKeys(right);
|
|
66
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
67
|
+
for (const key of leftKeys) {
|
|
68
|
+
if (!Object.hasOwn(right, key)) return false;
|
|
69
|
+
if (!deepEqual(
|
|
70
|
+
left[key],
|
|
71
|
+
right[key],
|
|
72
|
+
seen
|
|
73
|
+
)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
54
79
|
|
|
55
80
|
// src/evals/judge.ts
|
|
56
81
|
async function runJudge(args) {
|
|
@@ -134,16 +159,16 @@ var EvalOutcome = {
|
|
|
134
159
|
if (options.score !== void 0) {
|
|
135
160
|
outcome.score = options.score;
|
|
136
161
|
}
|
|
137
|
-
if (options.comment !== void 0)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (options.
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (options.usage !== void 0) {
|
|
144
|
-
outcome.usage = options.usage;
|
|
145
|
-
}
|
|
162
|
+
if (options.comment !== void 0) outcome.comment = options.comment;
|
|
163
|
+
if (options.metadata !== void 0) outcome.metadata = options.metadata;
|
|
164
|
+
if (options.usage !== void 0) outcome.usage = options.usage;
|
|
165
|
+
if (options.kind !== void 0) outcome.kind = options.kind;
|
|
166
|
+
if (options.error !== void 0) outcome.error = options.error;
|
|
146
167
|
return outcome;
|
|
168
|
+
},
|
|
169
|
+
fromError(error, kind = "metric") {
|
|
170
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
171
|
+
return EvalOutcome.invalid(reason, { kind, error });
|
|
147
172
|
}
|
|
148
173
|
};
|
|
149
174
|
|
|
@@ -154,12 +179,22 @@ function selectPromptOutput(args) {
|
|
|
154
179
|
}
|
|
155
180
|
return args.output.output;
|
|
156
181
|
}
|
|
182
|
+
function selectEvalCaseIds(result, outcomes = ["fail", "invalid"]) {
|
|
183
|
+
const selected = new Set(outcomes);
|
|
184
|
+
return result.results.filter((caseResult) => selected.has(caseResult.outcome)).map((caseResult) => caseResult.case.id);
|
|
185
|
+
}
|
|
157
186
|
async function resolveActual(selector, args) {
|
|
158
187
|
return selector === void 0 ? defaultOutputValue(args.output) : selector(args);
|
|
159
188
|
}
|
|
160
189
|
async function resolveActualText(selector, args) {
|
|
161
190
|
const value = selector === void 0 ? defaultOutputValue(args.output) : await selector(args);
|
|
162
|
-
|
|
191
|
+
if (typeof value === "string") return value;
|
|
192
|
+
try {
|
|
193
|
+
const serialized = JSON.stringify(value);
|
|
194
|
+
if (serialized !== void 0) return serialized;
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
throw new TypeError("Text metric actual value must be a string or JSON-serializable value.");
|
|
163
198
|
}
|
|
164
199
|
async function resolveExpected(selectorOrValue, args) {
|
|
165
200
|
if (selectorOrValue === void 0) {
|
|
@@ -260,7 +295,7 @@ ${actual}`,
|
|
|
260
295
|
usage
|
|
261
296
|
});
|
|
262
297
|
} catch (error) {
|
|
263
|
-
return EvalOutcome.
|
|
298
|
+
return EvalOutcome.fromError(error);
|
|
264
299
|
}
|
|
265
300
|
});
|
|
266
301
|
}
|
|
@@ -301,7 +336,7 @@ function promptAlignment(options) {
|
|
|
301
336
|
usage
|
|
302
337
|
});
|
|
303
338
|
} catch (error) {
|
|
304
|
-
return EvalOutcome.
|
|
339
|
+
return EvalOutcome.fromError(error);
|
|
305
340
|
}
|
|
306
341
|
});
|
|
307
342
|
}
|
|
@@ -363,7 +398,7 @@ function jsonCorrectness(options) {
|
|
|
363
398
|
usage
|
|
364
399
|
});
|
|
365
400
|
} catch (error) {
|
|
366
|
-
return EvalOutcome.
|
|
401
|
+
return EvalOutcome.fromError(error);
|
|
367
402
|
}
|
|
368
403
|
}
|
|
369
404
|
);
|
|
@@ -402,7 +437,7 @@ function hallucination(options) {
|
|
|
402
437
|
usage
|
|
403
438
|
});
|
|
404
439
|
} catch (error) {
|
|
405
|
-
return EvalOutcome.
|
|
440
|
+
return EvalOutcome.fromError(error);
|
|
406
441
|
}
|
|
407
442
|
});
|
|
408
443
|
}
|
|
@@ -478,7 +513,7 @@ ${actual}`,
|
|
|
478
513
|
usage
|
|
479
514
|
});
|
|
480
515
|
} catch (error) {
|
|
481
|
-
return EvalOutcome.
|
|
516
|
+
return EvalOutcome.fromError(error);
|
|
482
517
|
}
|
|
483
518
|
});
|
|
484
519
|
}
|
|
@@ -527,7 +562,7 @@ function abstention(options) {
|
|
|
527
562
|
};
|
|
528
563
|
return category === "correct_abstention" || category === "correct_grounded_answer" ? EvalOutcome.pass(category, outcomeOptions) : EvalOutcome.fail(category, outcomeOptions);
|
|
529
564
|
} catch (error) {
|
|
530
|
-
return EvalOutcome.
|
|
565
|
+
return EvalOutcome.fromError(error);
|
|
531
566
|
}
|
|
532
567
|
}
|
|
533
568
|
};
|
|
@@ -677,7 +712,7 @@ ${actual}`,
|
|
|
677
712
|
usage
|
|
678
713
|
});
|
|
679
714
|
} catch (error) {
|
|
680
|
-
return EvalOutcome.
|
|
715
|
+
return EvalOutcome.fromError(error);
|
|
681
716
|
}
|
|
682
717
|
});
|
|
683
718
|
}
|
|
@@ -761,7 +796,7 @@ function gEval(options) {
|
|
|
761
796
|
usage
|
|
762
797
|
});
|
|
763
798
|
} catch (error) {
|
|
764
|
-
return EvalOutcome.
|
|
799
|
+
return EvalOutcome.fromError(error);
|
|
765
800
|
}
|
|
766
801
|
});
|
|
767
802
|
}
|
|
@@ -810,7 +845,7 @@ function turnRelevancy(options) {
|
|
|
810
845
|
usage
|
|
811
846
|
});
|
|
812
847
|
} catch (error) {
|
|
813
|
-
return EvalOutcome.
|
|
848
|
+
return EvalOutcome.fromError(error);
|
|
814
849
|
}
|
|
815
850
|
});
|
|
816
851
|
}
|
|
@@ -882,7 +917,7 @@ function knowledgeRetention(options) {
|
|
|
882
917
|
usage
|
|
883
918
|
});
|
|
884
919
|
} catch (error) {
|
|
885
|
-
return EvalOutcome.
|
|
920
|
+
return EvalOutcome.fromError(error);
|
|
886
921
|
}
|
|
887
922
|
});
|
|
888
923
|
}
|
|
@@ -1204,6 +1239,72 @@ function agentRunSettings(request) {
|
|
|
1204
1239
|
return settings;
|
|
1205
1240
|
}
|
|
1206
1241
|
|
|
1242
|
+
// src/evals/execution.ts
|
|
1243
|
+
var EvalTimeoutError = class extends Error {
|
|
1244
|
+
constructor(timeoutMs) {
|
|
1245
|
+
super(`Evaluation case exceeded its timeout of ${timeoutMs}ms.`);
|
|
1246
|
+
this.timeoutMs = timeoutMs;
|
|
1247
|
+
this.name = "EvalTimeoutError";
|
|
1248
|
+
}
|
|
1249
|
+
timeoutMs;
|
|
1250
|
+
};
|
|
1251
|
+
var EvalAbortError = class extends Error {
|
|
1252
|
+
constructor(message = "Evaluation run was aborted.") {
|
|
1253
|
+
super(message);
|
|
1254
|
+
this.name = "EvalAbortError";
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
function createEvalCaseSignal(parent, timeoutMs) {
|
|
1258
|
+
const controller = new AbortController();
|
|
1259
|
+
const abortFromParent = () => controller.abort(parent?.reason ?? new EvalAbortError());
|
|
1260
|
+
if (parent?.aborted === true) abortFromParent();
|
|
1261
|
+
else parent?.addEventListener("abort", abortFromParent, { once: true });
|
|
1262
|
+
const timeout = timeoutMs === void 0 ? void 0 : setTimeout(() => controller.abort(new EvalTimeoutError(timeoutMs)), timeoutMs);
|
|
1263
|
+
return {
|
|
1264
|
+
signal: controller.signal,
|
|
1265
|
+
dispose() {
|
|
1266
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
1267
|
+
parent?.removeEventListener("abort", abortFromParent);
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
function abortable(signal, operation) {
|
|
1272
|
+
if (signal.aborted) return Promise.reject(abortReason(signal));
|
|
1273
|
+
return new Promise((resolve, reject) => {
|
|
1274
|
+
const onAbort = () => reject(abortReason(signal));
|
|
1275
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1276
|
+
operation.then(
|
|
1277
|
+
(value) => {
|
|
1278
|
+
signal.removeEventListener("abort", onAbort);
|
|
1279
|
+
resolve(value);
|
|
1280
|
+
},
|
|
1281
|
+
(error) => {
|
|
1282
|
+
signal.removeEventListener("abort", onAbort);
|
|
1283
|
+
reject(error);
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
function createConcurrencyLimiter(concurrency) {
|
|
1289
|
+
let active = 0;
|
|
1290
|
+
const waiting = [];
|
|
1291
|
+
return async (operation) => {
|
|
1292
|
+
if (active >= concurrency) {
|
|
1293
|
+
await new Promise((resolve) => waiting.push(resolve));
|
|
1294
|
+
}
|
|
1295
|
+
active += 1;
|
|
1296
|
+
try {
|
|
1297
|
+
return await operation();
|
|
1298
|
+
} finally {
|
|
1299
|
+
active -= 1;
|
|
1300
|
+
waiting.shift()?.();
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
function abortReason(signal) {
|
|
1305
|
+
return signal.reason ?? new EvalAbortError();
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1207
1308
|
// src/evals/reporting.ts
|
|
1208
1309
|
function projectEvalOutcome(outcome, dataType, projectScore) {
|
|
1209
1310
|
const value = projectScoreValue(outcome, dataType, projectScore);
|
|
@@ -1296,15 +1397,30 @@ var EvalReporterDispatchError = class extends AggregateError {
|
|
|
1296
1397
|
this.phase = phase;
|
|
1297
1398
|
}
|
|
1298
1399
|
};
|
|
1400
|
+
var EvalFailFastError = class extends Error {
|
|
1401
|
+
constructor(caseId, outcome) {
|
|
1402
|
+
super(`Evaluation stopped after case ${caseId} produced a required ${outcome} outcome.`);
|
|
1403
|
+
this.caseId = caseId;
|
|
1404
|
+
this.outcome = outcome;
|
|
1405
|
+
this.name = "EvalFailFastError";
|
|
1406
|
+
}
|
|
1407
|
+
caseId;
|
|
1408
|
+
outcome;
|
|
1409
|
+
};
|
|
1299
1410
|
async function runEvalSuite(options) {
|
|
1300
1411
|
validateSuiteOptions(options);
|
|
1412
|
+
const selectedCases = selectCases(options);
|
|
1413
|
+
const selectedOptions = {
|
|
1414
|
+
...options,
|
|
1415
|
+
cases: selectedCases
|
|
1416
|
+
};
|
|
1301
1417
|
const startedAtMs = Date.now();
|
|
1302
|
-
const run = resolveRun(
|
|
1418
|
+
const run = resolveRun(selectedOptions, startedAtMs);
|
|
1303
1419
|
const reporters = options.reporters ?? [];
|
|
1304
1420
|
const lifecycle = {
|
|
1305
1421
|
run,
|
|
1306
1422
|
suiteName: options.name,
|
|
1307
|
-
caseCount:
|
|
1423
|
+
caseCount: selectedCases.length,
|
|
1308
1424
|
metricNames: options.metrics.map((metric) => metric.name)
|
|
1309
1425
|
};
|
|
1310
1426
|
let reporterErrors;
|
|
@@ -1327,8 +1443,8 @@ async function runEvalSuite(options) {
|
|
|
1327
1443
|
let results;
|
|
1328
1444
|
let aggregates;
|
|
1329
1445
|
try {
|
|
1330
|
-
results = await runEvalCases(
|
|
1331
|
-
aggregates = await aggregateResult(
|
|
1446
|
+
results = await runEvalCases(selectedOptions, run);
|
|
1447
|
+
aggregates = await aggregateResult(selectedOptions, results);
|
|
1332
1448
|
} catch (error) {
|
|
1333
1449
|
await notifyRunEnd(reporters, {
|
|
1334
1450
|
...lifecycle,
|
|
@@ -1350,6 +1466,11 @@ async function runEvalSuite(options) {
|
|
|
1350
1466
|
durationMs: Date.now() - startedAtMs,
|
|
1351
1467
|
reporterErrors
|
|
1352
1468
|
};
|
|
1469
|
+
result.reporterErrors.push(
|
|
1470
|
+
...results.flatMap(
|
|
1471
|
+
(caseResult) => caseResult.metrics.flatMap((metricResult) => metricResult.reporterErrors)
|
|
1472
|
+
)
|
|
1473
|
+
);
|
|
1353
1474
|
if (aggregates.cost !== void 0) {
|
|
1354
1475
|
result.cost = aggregates.cost;
|
|
1355
1476
|
}
|
|
@@ -1371,102 +1492,214 @@ async function runEvalSuite(options) {
|
|
|
1371
1492
|
return result;
|
|
1372
1493
|
}
|
|
1373
1494
|
async function runEvalCases(options, run) {
|
|
1374
|
-
const
|
|
1495
|
+
const targetConcurrency = options.targetConcurrency ?? options.concurrency ?? 1;
|
|
1496
|
+
const metricConcurrency = options.metricConcurrency ?? options.concurrency ?? 1;
|
|
1497
|
+
const workerConcurrency = Math.max(targetConcurrency, metricConcurrency);
|
|
1498
|
+
const targetLimit = createConcurrencyLimiter(targetConcurrency);
|
|
1499
|
+
const metricLimit = createConcurrencyLimiter(metricConcurrency);
|
|
1375
1500
|
const results = Array(options.cases.length);
|
|
1376
1501
|
let nextIndex = 0;
|
|
1502
|
+
let completedCases = 0;
|
|
1377
1503
|
let failure;
|
|
1378
1504
|
async function worker() {
|
|
1379
1505
|
while (failure === void 0 && nextIndex < options.cases.length) {
|
|
1506
|
+
if (isAborted(options.signal)) {
|
|
1507
|
+
failure ??= { error: suiteAbortReason(options.signal) };
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1380
1510
|
const index = nextIndex;
|
|
1381
1511
|
nextIndex += 1;
|
|
1382
1512
|
try {
|
|
1383
1513
|
results[index] = await runEvalCase(
|
|
1384
1514
|
options,
|
|
1385
1515
|
options.cases[index],
|
|
1386
|
-
run
|
|
1516
|
+
run,
|
|
1517
|
+
targetLimit,
|
|
1518
|
+
metricLimit,
|
|
1519
|
+
() => completedCases
|
|
1387
1520
|
);
|
|
1521
|
+
completedCases += 1;
|
|
1522
|
+
if (isAborted(options.signal)) {
|
|
1523
|
+
failure ??= { error: suiteAbortReason(options.signal) };
|
|
1524
|
+
}
|
|
1525
|
+
if (options.failFast === true && results[index]?.outcome !== "pass") {
|
|
1526
|
+
const result = results[index];
|
|
1527
|
+
if (result !== void 0 && result.outcome !== "pass") {
|
|
1528
|
+
failure ??= { error: new EvalFailFastError(result.case.id, result.outcome) };
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1388
1531
|
} catch (error) {
|
|
1389
1532
|
failure ??= { error };
|
|
1390
1533
|
}
|
|
1391
1534
|
}
|
|
1392
1535
|
}
|
|
1393
1536
|
await Promise.all(
|
|
1394
|
-
Array.from({ length: Math.min(
|
|
1537
|
+
Array.from({ length: Math.min(workerConcurrency, options.cases.length) }, () => worker())
|
|
1395
1538
|
);
|
|
1396
1539
|
if (failure !== void 0) throw failure.error;
|
|
1397
1540
|
return results;
|
|
1398
1541
|
}
|
|
1399
|
-
async function runEvalCase(options, testCase, run) {
|
|
1400
|
-
|
|
1401
|
-
|
|
1542
|
+
async function runEvalCase(options, testCase, run, targetLimit, metricLimit, completedCases) {
|
|
1543
|
+
const caseStartedAt = performance.now();
|
|
1544
|
+
const caseSignal = createEvalCaseSignal(options.signal, options.caseTimeoutMs);
|
|
1402
1545
|
try {
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1546
|
+
const progress = (event) => notifyProgress(options, event);
|
|
1547
|
+
await progress({
|
|
1548
|
+
type: "case-start",
|
|
1549
|
+
suiteName: options.name,
|
|
1550
|
+
case: testCase,
|
|
1551
|
+
completedCases: completedCases(),
|
|
1552
|
+
totalCases: options.cases.length
|
|
1553
|
+
});
|
|
1554
|
+
let output;
|
|
1555
|
+
let targetError;
|
|
1556
|
+
let targetStatus = "succeeded";
|
|
1557
|
+
let targetDurationMs = 0;
|
|
1558
|
+
try {
|
|
1559
|
+
output = await targetLimit(async () => {
|
|
1560
|
+
const targetStartedAt = performance.now();
|
|
1561
|
+
try {
|
|
1562
|
+
return await (caseSignal.signal.aborted ? Promise.reject(caseSignal.signal.reason) : abortable(
|
|
1563
|
+
caseSignal.signal,
|
|
1564
|
+
Promise.resolve(
|
|
1565
|
+
options.target(testCase.input, testCase, { signal: caseSignal.signal })
|
|
1566
|
+
)
|
|
1567
|
+
));
|
|
1568
|
+
} finally {
|
|
1569
|
+
targetDurationMs = performance.now() - targetStartedAt;
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
} catch (error) {
|
|
1573
|
+
if (options.signal?.aborted === true) {
|
|
1574
|
+
throw options.signal.reason ?? new EvalAbortError();
|
|
1575
|
+
}
|
|
1576
|
+
targetStatus = "failed";
|
|
1577
|
+
targetError = error;
|
|
1578
|
+
}
|
|
1579
|
+
await progress({
|
|
1580
|
+
type: "target-complete",
|
|
1413
1581
|
suiteName: options.name,
|
|
1414
|
-
testCase,
|
|
1582
|
+
case: testCase,
|
|
1583
|
+
targetStatus,
|
|
1415
1584
|
output,
|
|
1416
|
-
targetError,
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
traceError: traceResult.error,
|
|
1421
|
-
reporters: options.reporters ?? [],
|
|
1422
|
-
reporterErrorPolicy: options.reporterErrorPolicy ?? "collect"
|
|
1585
|
+
error: targetError,
|
|
1586
|
+
durationMs: targetDurationMs,
|
|
1587
|
+
completedCases: completedCases(),
|
|
1588
|
+
totalCases: options.cases.length
|
|
1423
1589
|
});
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1590
|
+
const traceResult = await resolveTrace(options, testCase, output, targetError, targetStatus);
|
|
1591
|
+
const metrics = await Promise.all(
|
|
1592
|
+
options.metrics.map(
|
|
1593
|
+
(metric) => metricLimit(async () => {
|
|
1594
|
+
const metricStartedAt = performance.now();
|
|
1595
|
+
const outcome = targetStatus === "succeeded" ? await safeEvaluate(
|
|
1596
|
+
options.name,
|
|
1597
|
+
testCase,
|
|
1598
|
+
output,
|
|
1599
|
+
metric,
|
|
1600
|
+
caseSignal.signal
|
|
1601
|
+
) : EvalOutcome.invalid(`Target failed: ${errorMessage(targetError)}`, {
|
|
1602
|
+
kind: targetError instanceof EvalTimeoutError ? "timeout" : "target",
|
|
1603
|
+
error: targetError
|
|
1604
|
+
});
|
|
1605
|
+
const durationMs = performance.now() - metricStartedAt;
|
|
1606
|
+
const reporterErrors = await reportOutcome({
|
|
1607
|
+
run,
|
|
1608
|
+
suiteName: options.name,
|
|
1609
|
+
testCase,
|
|
1610
|
+
output,
|
|
1611
|
+
targetError,
|
|
1612
|
+
targetStatus,
|
|
1613
|
+
metric,
|
|
1614
|
+
outcome,
|
|
1615
|
+
trace: traceResult.trace,
|
|
1616
|
+
traceError: traceResult.error,
|
|
1617
|
+
reporters: options.reporters ?? [],
|
|
1618
|
+
reporterErrorPolicy: options.reporterErrorPolicy ?? "collect"
|
|
1619
|
+
});
|
|
1620
|
+
await progress({
|
|
1621
|
+
type: "metric-complete",
|
|
1622
|
+
suiteName: options.name,
|
|
1623
|
+
case: testCase,
|
|
1624
|
+
metricName: metric.name,
|
|
1625
|
+
outcome,
|
|
1626
|
+
durationMs,
|
|
1627
|
+
completedCases: completedCases(),
|
|
1628
|
+
totalCases: options.cases.length
|
|
1629
|
+
});
|
|
1630
|
+
const metricResult = {
|
|
1631
|
+
metricName: metric.name,
|
|
1632
|
+
required: metric.required ?? true,
|
|
1633
|
+
outcome,
|
|
1634
|
+
durationMs,
|
|
1635
|
+
reporterErrors
|
|
1636
|
+
};
|
|
1637
|
+
if (metric.direction !== void 0) metricResult.direction = metric.direction;
|
|
1638
|
+
if (metric.threshold !== void 0) metricResult.threshold = metric.threshold;
|
|
1639
|
+
return metricResult;
|
|
1640
|
+
})
|
|
1641
|
+
)
|
|
1642
|
+
);
|
|
1643
|
+
const scores = Object.fromEntries(
|
|
1644
|
+
metrics.map((metric) => [metric.metricName, metric.outcome])
|
|
1645
|
+
);
|
|
1646
|
+
const result = {
|
|
1647
|
+
case: testCase,
|
|
1648
|
+
outcome: caseOutcome(targetStatus, metrics),
|
|
1649
|
+
targetStatus,
|
|
1650
|
+
targetDurationMs,
|
|
1651
|
+
durationMs: performance.now() - caseStartedAt,
|
|
1652
|
+
metrics,
|
|
1653
|
+
scores,
|
|
1654
|
+
usage: emptyUsageSummary()
|
|
1429
1655
|
};
|
|
1430
|
-
if (
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
if (targetError !== void 0) {
|
|
1447
|
-
result.targetError = targetError;
|
|
1656
|
+
if (targetStatus === "succeeded") {
|
|
1657
|
+
result.output = output;
|
|
1658
|
+
}
|
|
1659
|
+
if (targetStatus === "failed") {
|
|
1660
|
+
result.targetError = targetError;
|
|
1661
|
+
}
|
|
1662
|
+
await progress({
|
|
1663
|
+
type: "case-complete",
|
|
1664
|
+
suiteName: options.name,
|
|
1665
|
+
result,
|
|
1666
|
+
completedCases: completedCases() + 1,
|
|
1667
|
+
totalCases: options.cases.length
|
|
1668
|
+
});
|
|
1669
|
+
return result;
|
|
1670
|
+
} finally {
|
|
1671
|
+
caseSignal.dispose();
|
|
1448
1672
|
}
|
|
1449
|
-
return result;
|
|
1450
1673
|
}
|
|
1451
|
-
async function resolveTrace(options, testCase, output, targetError) {
|
|
1674
|
+
async function resolveTrace(options, testCase, output, targetError, targetStatus) {
|
|
1452
1675
|
try {
|
|
1453
1676
|
const selector = options.trace ?? defaultEvalTraceSelector;
|
|
1454
1677
|
const trace = await selector({
|
|
1455
1678
|
suiteName: options.name,
|
|
1456
1679
|
case: testCase,
|
|
1457
1680
|
output,
|
|
1458
|
-
targetError
|
|
1681
|
+
targetError,
|
|
1682
|
+
targetStatus
|
|
1459
1683
|
});
|
|
1460
1684
|
return trace === void 0 ? {} : { trace };
|
|
1461
1685
|
} catch (error) {
|
|
1462
1686
|
return { error };
|
|
1463
1687
|
}
|
|
1464
1688
|
}
|
|
1465
|
-
async function safeEvaluate(suiteName, testCase, output, metric) {
|
|
1689
|
+
async function safeEvaluate(suiteName, testCase, output, metric, signal) {
|
|
1690
|
+
if (signal.aborted) {
|
|
1691
|
+
return EvalOutcome.fromError(
|
|
1692
|
+
signal.reason,
|
|
1693
|
+
signal.reason instanceof EvalTimeoutError ? "timeout" : "metric"
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1466
1696
|
try {
|
|
1467
|
-
return await
|
|
1697
|
+
return await abortable(
|
|
1698
|
+
signal,
|
|
1699
|
+
Promise.resolve(metric.evaluate({ suiteName, case: testCase, output, signal }))
|
|
1700
|
+
);
|
|
1468
1701
|
} catch (error) {
|
|
1469
|
-
return EvalOutcome.
|
|
1702
|
+
return EvalOutcome.fromError(error, error instanceof EvalTimeoutError ? "timeout" : "metric");
|
|
1470
1703
|
}
|
|
1471
1704
|
}
|
|
1472
1705
|
async function reportOutcome(args) {
|
|
@@ -1482,6 +1715,7 @@ async function reportOutcome(args) {
|
|
|
1482
1715
|
case: args.testCase,
|
|
1483
1716
|
output: args.output,
|
|
1484
1717
|
targetError: args.targetError,
|
|
1718
|
+
targetStatus: args.targetStatus,
|
|
1485
1719
|
trace: args.trace,
|
|
1486
1720
|
metric: args.metric,
|
|
1487
1721
|
outcome: args.outcome
|
|
@@ -1567,13 +1801,20 @@ function countCaseOutcomes(results) {
|
|
|
1567
1801
|
function emptyTotals() {
|
|
1568
1802
|
return { total: 0, passed: 0, failed: 0, invalid: 0 };
|
|
1569
1803
|
}
|
|
1804
|
+
function emptyUsageSummary() {
|
|
1805
|
+
return {
|
|
1806
|
+
target: Usage.empty(),
|
|
1807
|
+
evaluation: Usage.empty(),
|
|
1808
|
+
total: Usage.empty()
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1570
1811
|
function statusKey(status) {
|
|
1571
1812
|
if (status === "pass") return "passed";
|
|
1572
1813
|
if (status === "fail") return "failed";
|
|
1573
1814
|
return "invalid";
|
|
1574
1815
|
}
|
|
1575
|
-
function caseOutcome(
|
|
1576
|
-
if (
|
|
1816
|
+
function caseOutcome(targetStatus, metrics) {
|
|
1817
|
+
if (targetStatus === "failed") return "invalid";
|
|
1577
1818
|
const required = metrics.filter((metric) => metric.required);
|
|
1578
1819
|
if (required.some((metric) => metric.outcome.outcome === "invalid")) return "invalid";
|
|
1579
1820
|
if (required.some((metric) => metric.outcome.outcome === "fail")) return "fail";
|
|
@@ -1585,12 +1826,17 @@ async function aggregateResult(options, results) {
|
|
|
1585
1826
|
let targetCost = 0;
|
|
1586
1827
|
let evaluationCost = 0;
|
|
1587
1828
|
for (const result of results) {
|
|
1588
|
-
|
|
1829
|
+
let caseTargetUsage = Usage.empty();
|
|
1830
|
+
let caseEvaluationUsage = Usage.empty();
|
|
1831
|
+
let caseTargetCost = 0;
|
|
1832
|
+
let caseEvaluationCost = 0;
|
|
1833
|
+
if (result.targetStatus === "succeeded") {
|
|
1589
1834
|
const usage2 = await resolveTargetUsage(options, result.case, result.output);
|
|
1590
1835
|
if (usage2 !== void 0) {
|
|
1836
|
+
caseTargetUsage = usage2;
|
|
1591
1837
|
targetUsage = Usage.add(targetUsage, usage2);
|
|
1592
1838
|
if (options.cost !== void 0) {
|
|
1593
|
-
|
|
1839
|
+
caseTargetCost = await calculateCost(
|
|
1594
1840
|
options.cost.calculate({
|
|
1595
1841
|
kind: "target",
|
|
1596
1842
|
suiteName: options.name,
|
|
@@ -1599,6 +1845,7 @@ async function aggregateResult(options, results) {
|
|
|
1599
1845
|
usage: usage2
|
|
1600
1846
|
})
|
|
1601
1847
|
);
|
|
1848
|
+
targetCost += caseTargetCost;
|
|
1602
1849
|
}
|
|
1603
1850
|
}
|
|
1604
1851
|
}
|
|
@@ -1606,13 +1853,14 @@ async function aggregateResult(options, results) {
|
|
|
1606
1853
|
const usage2 = metricResult.outcome.usage;
|
|
1607
1854
|
if (usage2 === void 0) continue;
|
|
1608
1855
|
assertUsage(usage2, `Evaluation usage for metric ${metricResult.metricName}`);
|
|
1856
|
+
caseEvaluationUsage = Usage.add(caseEvaluationUsage, usage2);
|
|
1609
1857
|
evaluationUsage = Usage.add(evaluationUsage, usage2);
|
|
1610
|
-
if (options.cost !== void 0 && result.
|
|
1858
|
+
if (options.cost !== void 0 && result.targetStatus === "succeeded") {
|
|
1611
1859
|
const metric = options.metrics.find(
|
|
1612
1860
|
(candidate) => candidate.name === metricResult.metricName
|
|
1613
1861
|
);
|
|
1614
1862
|
if (metric !== void 0) {
|
|
1615
|
-
|
|
1863
|
+
const metricCost = await calculateCost(
|
|
1616
1864
|
options.cost.calculate({
|
|
1617
1865
|
kind: "evaluation",
|
|
1618
1866
|
suiteName: options.name,
|
|
@@ -1622,9 +1870,25 @@ async function aggregateResult(options, results) {
|
|
|
1622
1870
|
usage: usage2
|
|
1623
1871
|
})
|
|
1624
1872
|
);
|
|
1873
|
+
metricResult.cost = metricCost;
|
|
1874
|
+
caseEvaluationCost += metricCost;
|
|
1875
|
+
evaluationCost += metricCost;
|
|
1625
1876
|
}
|
|
1626
1877
|
}
|
|
1627
1878
|
}
|
|
1879
|
+
result.usage = {
|
|
1880
|
+
target: caseTargetUsage,
|
|
1881
|
+
evaluation: caseEvaluationUsage,
|
|
1882
|
+
total: Usage.add(caseTargetUsage, caseEvaluationUsage)
|
|
1883
|
+
};
|
|
1884
|
+
if (options.cost !== void 0) {
|
|
1885
|
+
result.cost = {
|
|
1886
|
+
currency: options.cost.currency,
|
|
1887
|
+
target: caseTargetCost,
|
|
1888
|
+
evaluation: caseEvaluationCost,
|
|
1889
|
+
total: caseTargetCost + caseEvaluationCost
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1628
1892
|
}
|
|
1629
1893
|
const usage = {
|
|
1630
1894
|
target: targetUsage,
|
|
@@ -1647,7 +1911,12 @@ async function aggregateResult(options, results) {
|
|
|
1647
1911
|
return aggregates;
|
|
1648
1912
|
}
|
|
1649
1913
|
async function resolveTargetUsage(options, testCase, output) {
|
|
1650
|
-
const usage = options.targetUsage === void 0 ? usageFromOutput(output) : await options.targetUsage({
|
|
1914
|
+
const usage = options.targetUsage === void 0 ? usageFromOutput(output) : await options.targetUsage({
|
|
1915
|
+
suiteName: options.name,
|
|
1916
|
+
case: testCase,
|
|
1917
|
+
output,
|
|
1918
|
+
signal: new AbortController().signal
|
|
1919
|
+
});
|
|
1651
1920
|
if (usage !== void 0) assertUsage(usage, `Target usage for case ${testCase.id}`);
|
|
1652
1921
|
return usage;
|
|
1653
1922
|
}
|
|
@@ -1671,6 +1940,25 @@ async function calculateCost(value) {
|
|
|
1671
1940
|
return cost;
|
|
1672
1941
|
}
|
|
1673
1942
|
function validateSuiteOptions(options) {
|
|
1943
|
+
if (options.name.trim().length === 0) {
|
|
1944
|
+
throw new TypeError("Evaluation suite name must not be empty");
|
|
1945
|
+
}
|
|
1946
|
+
if (options.cases.length === 0) {
|
|
1947
|
+
throw new TypeError("Evaluation suite must contain at least one case");
|
|
1948
|
+
}
|
|
1949
|
+
if (options.metrics.length === 0) {
|
|
1950
|
+
throw new TypeError("Evaluation suite must contain at least one metric");
|
|
1951
|
+
}
|
|
1952
|
+
for (const testCase of options.cases) {
|
|
1953
|
+
if (testCase.id.trim().length === 0) {
|
|
1954
|
+
throw new TypeError("Evaluation case id must not be empty");
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
for (const metric of options.metrics) {
|
|
1958
|
+
if (metric.name.trim().length === 0) {
|
|
1959
|
+
throw new TypeError("Evaluation metric name must not be empty");
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1674
1962
|
assertUnique(
|
|
1675
1963
|
options.cases.map((testCase) => testCase.id),
|
|
1676
1964
|
"Evaluation case id"
|
|
@@ -1682,6 +1970,26 @@ function validateSuiteOptions(options) {
|
|
|
1682
1970
|
if (options.cost !== void 0 && options.cost.currency.trim().length === 0) {
|
|
1683
1971
|
throw new TypeError("Evaluation cost currency must not be empty");
|
|
1684
1972
|
}
|
|
1973
|
+
for (const [label, value] of [
|
|
1974
|
+
["concurrency", options.concurrency],
|
|
1975
|
+
["targetConcurrency", options.targetConcurrency],
|
|
1976
|
+
["metricConcurrency", options.metricConcurrency]
|
|
1977
|
+
]) {
|
|
1978
|
+
if (value !== void 0 && (!Number.isSafeInteger(value) || value < 1)) {
|
|
1979
|
+
throw new RangeError(`Evaluation ${label} must be a positive integer`);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
if (options.caseTimeoutMs !== void 0 && (!Number.isSafeInteger(options.caseTimeoutMs) || options.caseTimeoutMs < 1)) {
|
|
1983
|
+
throw new RangeError("Evaluation caseTimeoutMs must be a positive integer");
|
|
1984
|
+
}
|
|
1985
|
+
if (options.shard !== void 0) {
|
|
1986
|
+
if (!Number.isSafeInteger(options.shard.count) || options.shard.count < 1) {
|
|
1987
|
+
throw new RangeError("Evaluation shard count must be a positive integer");
|
|
1988
|
+
}
|
|
1989
|
+
if (!Number.isSafeInteger(options.shard.index) || options.shard.index < 0 || options.shard.index >= options.shard.count) {
|
|
1990
|
+
throw new RangeError("Evaluation shard index must be between 0 and count - 1");
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1685
1993
|
}
|
|
1686
1994
|
function assertUnique(values, label) {
|
|
1687
1995
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1690,6 +1998,31 @@ function assertUnique(values, label) {
|
|
|
1690
1998
|
seen.add(value);
|
|
1691
1999
|
}
|
|
1692
2000
|
}
|
|
2001
|
+
function selectCases(options) {
|
|
2002
|
+
const requested = options.caseIds === void 0 ? void 0 : new Set(options.caseIds);
|
|
2003
|
+
if (requested !== void 0) {
|
|
2004
|
+
assertUnique([...options.caseIds], "Evaluation selected case id");
|
|
2005
|
+
const available = new Set(options.cases.map((testCase) => testCase.id));
|
|
2006
|
+
for (const id of requested) {
|
|
2007
|
+
if (!available.has(id))
|
|
2008
|
+
throw new TypeError(`Evaluation selected case id was not found: ${id}`);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
const filtered = options.cases.filter(
|
|
2012
|
+
(testCase, index) => (requested === void 0 || requested.has(testCase.id)) && (options.caseFilter === void 0 || options.caseFilter(testCase, index))
|
|
2013
|
+
);
|
|
2014
|
+
if (options.shard === void 0) return filtered;
|
|
2015
|
+
return filtered.filter((_, index) => index % options.shard.count === options.shard.index);
|
|
2016
|
+
}
|
|
2017
|
+
async function notifyProgress(options, event) {
|
|
2018
|
+
await options.onProgress?.(event);
|
|
2019
|
+
}
|
|
2020
|
+
function isAborted(signal) {
|
|
2021
|
+
return signal?.aborted === true;
|
|
2022
|
+
}
|
|
2023
|
+
function suiteAbortReason(signal) {
|
|
2024
|
+
return signal?.reason ?? new EvalAbortError();
|
|
2025
|
+
}
|
|
1693
2026
|
|
|
1694
2027
|
// src/evals/cli.ts
|
|
1695
2028
|
var EvalAssertionError = class extends Error {
|
|
@@ -1701,11 +2034,20 @@ ${mismatches.map((mismatch) => `- ${mismatch}`).join("\n")}`);
|
|
|
1701
2034
|
this.mismatches = mismatches;
|
|
1702
2035
|
}
|
|
1703
2036
|
};
|
|
2037
|
+
function defineEvalExpectations(_suite, expectations) {
|
|
2038
|
+
return expectations;
|
|
2039
|
+
}
|
|
2040
|
+
function formatEvalResult(result, options = {}) {
|
|
2041
|
+
const format = options.format ?? "pretty";
|
|
2042
|
+
if (format === "quiet") return "";
|
|
2043
|
+
validatePrintOptions(options);
|
|
2044
|
+
return format === "json" ? jsonResult(result, options) : prettyResult(result, options);
|
|
2045
|
+
}
|
|
1704
2046
|
function printEvalResult(result, options = {}) {
|
|
1705
2047
|
const format = options.format ?? "pretty";
|
|
1706
2048
|
if (format === "quiet") return;
|
|
1707
2049
|
const write = options.output?.stdout ?? ((text) => process.stdout.write(text));
|
|
1708
|
-
write(`${
|
|
2050
|
+
write(`${formatEvalResult(result, options)}
|
|
1709
2051
|
`);
|
|
1710
2052
|
}
|
|
1711
2053
|
function evalExitCode(result, expectations) {
|
|
@@ -1730,11 +2072,11 @@ function assertEvalOutcomes(result, expected) {
|
|
|
1730
2072
|
}
|
|
1731
2073
|
}
|
|
1732
2074
|
async function runEvalCli(options) {
|
|
1733
|
-
const { format, exitCode, expectations, output, ...suiteOptions } = options;
|
|
2075
|
+
const { format, exitCode, expectations, output, maxValueLength, redact: redact2, ...suiteOptions } = options;
|
|
1734
2076
|
const result = await runEvalSuite(
|
|
1735
2077
|
suiteOptions
|
|
1736
2078
|
);
|
|
1737
|
-
printEvalResult(result, { format, output });
|
|
2079
|
+
printEvalResult(result, { format, output, maxValueLength, redact: redact2 });
|
|
1738
2080
|
const code = evalExitCode(result, expectations);
|
|
1739
2081
|
const mismatches = expectationMismatches(result, expectations);
|
|
1740
2082
|
if (mismatches.length > 0 && format !== "quiet") {
|
|
@@ -1749,7 +2091,7 @@ ${mismatches.map((value) => `- ${value}`).join("\n")}
|
|
|
1749
2091
|
if (exitCode === true && code > currentExitCode) process.exitCode = code;
|
|
1750
2092
|
return result;
|
|
1751
2093
|
}
|
|
1752
|
-
function prettyResult(result) {
|
|
2094
|
+
function prettyResult(result, options) {
|
|
1753
2095
|
const lines = [
|
|
1754
2096
|
`${result.name} (${result.run.id})`,
|
|
1755
2097
|
`Cases: ${totalsText(result.cases)}`,
|
|
@@ -1757,20 +2099,53 @@ function prettyResult(result) {
|
|
|
1757
2099
|
];
|
|
1758
2100
|
for (const caseResult of result.results) {
|
|
1759
2101
|
lines.push(``, `[${caseResult.outcome.toUpperCase()}] ${caseResult.case.id}`);
|
|
1760
|
-
if (caseResult.
|
|
1761
|
-
|
|
1762
|
-
|
|
2102
|
+
if (caseResult.targetStatus === "succeeded") {
|
|
2103
|
+
lines.push(
|
|
2104
|
+
` output: ${displayValue(
|
|
2105
|
+
redact(caseResult.output, options, { kind: "output", caseId: caseResult.case.id }),
|
|
2106
|
+
options.maxValueLength
|
|
2107
|
+
)}`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
if (caseResult.targetStatus === "failed") {
|
|
2111
|
+
lines.push(
|
|
2112
|
+
` target error: ${errorText(
|
|
2113
|
+
redact(caseResult.targetError, options, { kind: "error", caseId: caseResult.case.id }),
|
|
2114
|
+
options.maxValueLength
|
|
2115
|
+
)}`
|
|
2116
|
+
);
|
|
1763
2117
|
}
|
|
1764
2118
|
for (const metric of caseResult.metrics) {
|
|
1765
2119
|
const parts = [` - ${metric.metricName}: ${metric.outcome.outcome}`];
|
|
1766
|
-
if (metric.outcome.score !== void 0)
|
|
1767
|
-
parts.push(
|
|
2120
|
+
if (metric.outcome.score !== void 0) {
|
|
2121
|
+
parts.push(
|
|
2122
|
+
`score=${displayValue(
|
|
2123
|
+
redact(metric.outcome.score, options, {
|
|
2124
|
+
kind: "score",
|
|
2125
|
+
caseId: caseResult.case.id,
|
|
2126
|
+
metricName: metric.metricName
|
|
2127
|
+
}),
|
|
2128
|
+
options.maxValueLength
|
|
2129
|
+
)}`
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
1768
2132
|
if (metric.threshold !== void 0) parts.push(`threshold=${metric.threshold}`);
|
|
1769
2133
|
if (metric.direction !== void 0) parts.push(`direction=${metric.direction}`);
|
|
1770
2134
|
if (!metric.required) parts.push("optional");
|
|
1771
2135
|
lines.push(parts.join(" | "));
|
|
1772
2136
|
const explanation = metric.outcome.comment ?? (metric.outcome.outcome === "invalid" ? metric.outcome.reason : void 0);
|
|
1773
|
-
if (explanation !== void 0)
|
|
2137
|
+
if (explanation !== void 0) {
|
|
2138
|
+
lines.push(
|
|
2139
|
+
` ${displayValue(
|
|
2140
|
+
redact(explanation, options, {
|
|
2141
|
+
kind: "comment",
|
|
2142
|
+
caseId: caseResult.case.id,
|
|
2143
|
+
metricName: metric.metricName
|
|
2144
|
+
}),
|
|
2145
|
+
options.maxValueLength
|
|
2146
|
+
)}`
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
1774
2149
|
}
|
|
1775
2150
|
}
|
|
1776
2151
|
lines.push(
|
|
@@ -1785,14 +2160,15 @@ function prettyResult(result) {
|
|
|
1785
2160
|
lines.push(`Duration: ${result.durationMs}ms`);
|
|
1786
2161
|
return lines.join("\n");
|
|
1787
2162
|
}
|
|
1788
|
-
function jsonResult(result) {
|
|
2163
|
+
function jsonResult(result, options) {
|
|
1789
2164
|
return JSON.stringify(
|
|
1790
|
-
result,
|
|
2165
|
+
redactJsonResult(result, options),
|
|
1791
2166
|
(_key, value) => {
|
|
1792
2167
|
if (value instanceof Error) {
|
|
1793
2168
|
return { name: value.name, message: value.message, stack: value.stack };
|
|
1794
2169
|
}
|
|
1795
2170
|
if (value instanceof RegExp) return String(value);
|
|
2171
|
+
if (typeof value === "string") return truncate(value, options.maxValueLength);
|
|
1796
2172
|
return value;
|
|
1797
2173
|
},
|
|
1798
2174
|
2
|
|
@@ -1885,16 +2261,107 @@ function requiredMetricCount(result, status) {
|
|
|
1885
2261
|
function totalsText(totals) {
|
|
1886
2262
|
return `${totals.total} total / ${totals.passed} pass / ${totals.failed} fail / ${totals.invalid} invalid`;
|
|
1887
2263
|
}
|
|
1888
|
-
function displayValue(value) {
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
2264
|
+
function displayValue(value, maxValueLength) {
|
|
2265
|
+
let text;
|
|
2266
|
+
if (typeof value === "string") text = value;
|
|
2267
|
+
else {
|
|
2268
|
+
try {
|
|
2269
|
+
text = JSON.stringify(value) ?? String(value);
|
|
2270
|
+
} catch {
|
|
2271
|
+
text = String(value);
|
|
2272
|
+
}
|
|
1894
2273
|
}
|
|
2274
|
+
return truncate(text, maxValueLength);
|
|
2275
|
+
}
|
|
2276
|
+
function errorText(error, maxValueLength) {
|
|
2277
|
+
return truncate(error instanceof Error ? error.message : displayValue(error), maxValueLength);
|
|
2278
|
+
}
|
|
2279
|
+
function truncate(value, maxValueLength) {
|
|
2280
|
+
return maxValueLength !== void 0 && value.length > maxValueLength ? `${value.slice(0, maxValueLength)}\u2026` : value;
|
|
2281
|
+
}
|
|
2282
|
+
function redact(value, options, context) {
|
|
2283
|
+
return options.redact === void 0 ? value : options.redact(value, context);
|
|
2284
|
+
}
|
|
2285
|
+
function redactJsonResult(result, options) {
|
|
2286
|
+
return {
|
|
2287
|
+
...result,
|
|
2288
|
+
run: {
|
|
2289
|
+
...result.run,
|
|
2290
|
+
metadata: result.run.metadata === void 0 ? void 0 : redact(result.run.metadata, options, {
|
|
2291
|
+
kind: "metadata"
|
|
2292
|
+
})
|
|
2293
|
+
},
|
|
2294
|
+
results: result.results.map((caseResult) => ({
|
|
2295
|
+
...caseResult,
|
|
2296
|
+
case: {
|
|
2297
|
+
...caseResult.case,
|
|
2298
|
+
input: redact(caseResult.case.input, options, {
|
|
2299
|
+
kind: "input",
|
|
2300
|
+
caseId: caseResult.case.id
|
|
2301
|
+
}),
|
|
2302
|
+
expected: redact(caseResult.case.expected, options, {
|
|
2303
|
+
kind: "expected",
|
|
2304
|
+
caseId: caseResult.case.id
|
|
2305
|
+
}),
|
|
2306
|
+
context: redact(caseResult.case.context, options, {
|
|
2307
|
+
kind: "context",
|
|
2308
|
+
caseId: caseResult.case.id
|
|
2309
|
+
}),
|
|
2310
|
+
retrievalContext: redact(caseResult.case.retrievalContext, options, {
|
|
2311
|
+
kind: "retrievalContext",
|
|
2312
|
+
caseId: caseResult.case.id
|
|
2313
|
+
}),
|
|
2314
|
+
metadata: caseResult.case.metadata === void 0 ? void 0 : redact(caseResult.case.metadata, options, {
|
|
2315
|
+
kind: "metadata",
|
|
2316
|
+
caseId: caseResult.case.id
|
|
2317
|
+
})
|
|
2318
|
+
},
|
|
2319
|
+
output: caseResult.targetStatus === "succeeded" ? redact(caseResult.output, options, { kind: "output", caseId: caseResult.case.id }) : void 0,
|
|
2320
|
+
targetError: caseResult.targetStatus === "failed" ? redact(caseResult.targetError, options, {
|
|
2321
|
+
kind: "error",
|
|
2322
|
+
caseId: caseResult.case.id
|
|
2323
|
+
}) : void 0,
|
|
2324
|
+
metrics: caseResult.metrics.map((metric) => ({
|
|
2325
|
+
...metric,
|
|
2326
|
+
reporterErrors: metric.reporterErrors.map(
|
|
2327
|
+
(error) => redact(error, options, {
|
|
2328
|
+
kind: "error",
|
|
2329
|
+
caseId: caseResult.case.id,
|
|
2330
|
+
metricName: metric.metricName
|
|
2331
|
+
})
|
|
2332
|
+
),
|
|
2333
|
+
outcome: redactOutcome(metric.outcome, options, caseResult.case.id, metric.metricName)
|
|
2334
|
+
})),
|
|
2335
|
+
scores: Object.fromEntries(
|
|
2336
|
+
caseResult.metrics.map((metric) => [
|
|
2337
|
+
metric.metricName,
|
|
2338
|
+
redactOutcome(metric.outcome, options, caseResult.case.id, metric.metricName)
|
|
2339
|
+
])
|
|
2340
|
+
)
|
|
2341
|
+
})),
|
|
2342
|
+
reporterErrors: result.reporterErrors.map((error) => redact(error, options, { kind: "error" }))
|
|
2343
|
+
};
|
|
1895
2344
|
}
|
|
1896
|
-
function
|
|
1897
|
-
return
|
|
2345
|
+
function redactOutcome(outcome, options, caseId, metricName) {
|
|
2346
|
+
return {
|
|
2347
|
+
...outcome,
|
|
2348
|
+
...outcome.score === void 0 ? {} : { score: redact(outcome.score, options, { kind: "score", caseId, metricName }) },
|
|
2349
|
+
...outcome.comment === void 0 ? {} : { comment: redact(outcome.comment, options, { kind: "comment", caseId, metricName }) },
|
|
2350
|
+
...!("reason" in outcome) || outcome.reason === void 0 ? {} : { reason: redact(outcome.reason, options, { kind: "comment", caseId, metricName }) },
|
|
2351
|
+
...outcome.metadata === void 0 ? {} : {
|
|
2352
|
+
metadata: redact(outcome.metadata, options, {
|
|
2353
|
+
kind: "metadata",
|
|
2354
|
+
caseId,
|
|
2355
|
+
metricName
|
|
2356
|
+
})
|
|
2357
|
+
},
|
|
2358
|
+
...!("error" in outcome) || outcome.error === void 0 ? {} : { error: redact(outcome.error, options, { kind: "error", caseId, metricName }) }
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
function validatePrintOptions(options) {
|
|
2362
|
+
if (options.maxValueLength !== void 0 && (!Number.isSafeInteger(options.maxValueLength) || options.maxValueLength < 1)) {
|
|
2363
|
+
throw new RangeError("Evaluation maxValueLength must be a positive integer.");
|
|
2364
|
+
}
|
|
1898
2365
|
}
|
|
1899
2366
|
|
|
1900
2367
|
// src/evals/metric.ts
|
|
@@ -1917,7 +2384,7 @@ function exactMatch(options = {}) {
|
|
|
1917
2384
|
if (expected === void 0) {
|
|
1918
2385
|
return EvalOutcome.invalid("No expected value provided for exact match.");
|
|
1919
2386
|
}
|
|
1920
|
-
const passed =
|
|
2387
|
+
const passed = evalValuesEqual(actual, expected);
|
|
1921
2388
|
return passed ? EvalOutcome.pass(true) : EvalOutcome.fail(false, { comment: `Expected ${formatValue(expected)}.` });
|
|
1922
2389
|
}
|
|
1923
2390
|
};
|
|
@@ -2078,12 +2545,13 @@ async function resolveOption(value, args) {
|
|
|
2078
2545
|
return typeof value === "function" ? value(args) : value;
|
|
2079
2546
|
}
|
|
2080
2547
|
function semanticSimilarity(options) {
|
|
2548
|
+
const threshold = unitInterval(options.threshold, "semanticSimilarity threshold");
|
|
2081
2549
|
return {
|
|
2082
2550
|
name: options.name ?? "semantic_similarity",
|
|
2083
2551
|
required: options.required ?? true,
|
|
2084
2552
|
dataType: "NUMERIC",
|
|
2085
2553
|
direction: "higher_is_better",
|
|
2086
|
-
threshold
|
|
2554
|
+
threshold,
|
|
2087
2555
|
async evaluate(args) {
|
|
2088
2556
|
const actual = await resolveActualText(options.actual, args);
|
|
2089
2557
|
const expected = await resolveExpected(options.expected, args);
|
|
@@ -2098,11 +2566,12 @@ function semanticSimilarity(options) {
|
|
|
2098
2566
|
embedText({ model: options.model, text: expected })
|
|
2099
2567
|
]);
|
|
2100
2568
|
const score = cosineSimilarity(actualEmbedding.vector, expectedEmbedding.vector);
|
|
2101
|
-
return score >=
|
|
2569
|
+
return score >= threshold ? EvalOutcome.pass(score) : EvalOutcome.fail(score, { comment: `Similarity below threshold ${threshold}.` });
|
|
2102
2570
|
}
|
|
2103
2571
|
};
|
|
2104
2572
|
}
|
|
2105
2573
|
function llmJudge(options) {
|
|
2574
|
+
const retries = extractionRetries(options.retries);
|
|
2106
2575
|
return {
|
|
2107
2576
|
name: options.name ?? "llm_judge",
|
|
2108
2577
|
required: options.required ?? true,
|
|
@@ -2113,24 +2582,27 @@ function llmJudge(options) {
|
|
|
2113
2582
|
outputSchema: options.schema,
|
|
2114
2583
|
instructions: options.instructions ?? "Judge the eval case by the requested schema. Submit the judgment using the schema.",
|
|
2115
2584
|
text: await resolveJudgePrompt(options.prompt, args),
|
|
2116
|
-
retries
|
|
2585
|
+
retries
|
|
2117
2586
|
});
|
|
2118
2587
|
return options.passes(result.output) ? EvalOutcome.pass(result.output, { usage: result.usage }) : EvalOutcome.fail(result.output, { usage: result.usage });
|
|
2119
2588
|
} catch (error) {
|
|
2120
|
-
return EvalOutcome.
|
|
2589
|
+
return EvalOutcome.fromError(error);
|
|
2121
2590
|
}
|
|
2122
2591
|
}
|
|
2123
2592
|
};
|
|
2124
2593
|
}
|
|
2125
2594
|
function llmScore(options) {
|
|
2126
2595
|
const criteria = Array.isArray(options.criteria) ? options.criteria.join("\n") : options.criteria;
|
|
2596
|
+
if (criteria.trim().length === 0) throw new TypeError("llmScore criteria must not be empty.");
|
|
2597
|
+
const threshold = unitInterval(options.threshold, "llmScore threshold");
|
|
2598
|
+
const retries = extractionRetries(options.retries);
|
|
2127
2599
|
return {
|
|
2128
2600
|
name: options.name ?? "llm_score",
|
|
2129
2601
|
required: options.required ?? true,
|
|
2130
2602
|
dataType: "NUMERIC",
|
|
2131
2603
|
projectScore: (score) => score.score,
|
|
2132
2604
|
direction: "higher_is_better",
|
|
2133
|
-
threshold
|
|
2605
|
+
threshold,
|
|
2134
2606
|
async evaluate(args) {
|
|
2135
2607
|
try {
|
|
2136
2608
|
const result = await extract({
|
|
@@ -2144,7 +2616,7 @@ ${criteria}
|
|
|
2144
2616
|
|
|
2145
2617
|
Return a score between 0 and 1 and brief feedback.`,
|
|
2146
2618
|
text: await resolveJudgePrompt(options.prompt, args),
|
|
2147
|
-
retries
|
|
2619
|
+
retries
|
|
2148
2620
|
});
|
|
2149
2621
|
const score = result.output;
|
|
2150
2622
|
if (score.score < 0 || score.score > 1) {
|
|
@@ -2153,38 +2625,53 @@ Return a score between 0 and 1 and brief feedback.`,
|
|
|
2153
2625
|
usage: result.usage
|
|
2154
2626
|
});
|
|
2155
2627
|
}
|
|
2156
|
-
return score.score >=
|
|
2628
|
+
return score.score >= threshold ? EvalOutcome.pass(score, { comment: score.feedback, usage: result.usage }) : EvalOutcome.fail(score, { comment: score.feedback, usage: result.usage });
|
|
2157
2629
|
} catch (error) {
|
|
2158
|
-
return EvalOutcome.
|
|
2630
|
+
return EvalOutcome.fromError(error);
|
|
2159
2631
|
}
|
|
2160
2632
|
}
|
|
2161
2633
|
};
|
|
2162
2634
|
}
|
|
2163
|
-
function
|
|
2164
|
-
const retryCount =
|
|
2635
|
+
function extractionRetries(retries) {
|
|
2636
|
+
const retryCount = retries ?? 0;
|
|
2637
|
+
if (!Number.isInteger(retryCount) || retryCount < 0) {
|
|
2638
|
+
throw new RangeError("Eval metric retries must be a non-negative integer.");
|
|
2639
|
+
}
|
|
2165
2640
|
if (retryCount === 0) {
|
|
2166
2641
|
return void 0;
|
|
2167
2642
|
}
|
|
2168
2643
|
return { maxAttempts: retryCount + 1 };
|
|
2169
2644
|
}
|
|
2645
|
+
function unitInterval(value, label) {
|
|
2646
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
2647
|
+
throw new RangeError(`${label} must be between 0 and 1.`);
|
|
2648
|
+
}
|
|
2649
|
+
return value;
|
|
2650
|
+
}
|
|
2170
2651
|
|
|
2171
2652
|
// src/evals/suite.ts
|
|
2172
2653
|
function defineEvalCases(cases) {
|
|
2173
2654
|
return cases;
|
|
2174
2655
|
}
|
|
2175
|
-
function
|
|
2176
|
-
if (options !== void 0) return options;
|
|
2656
|
+
function createEvalTypes() {
|
|
2177
2657
|
return {
|
|
2178
2658
|
defineMetric(metric) {
|
|
2179
2659
|
return metric;
|
|
2180
2660
|
}
|
|
2181
2661
|
};
|
|
2182
2662
|
}
|
|
2663
|
+
function defineEvalSuite(options) {
|
|
2664
|
+
if (options !== void 0) return options;
|
|
2665
|
+
return createEvalTypes();
|
|
2666
|
+
}
|
|
2183
2667
|
export {
|
|
2184
2668
|
AgentEvalSuspensionError,
|
|
2669
|
+
EvalAbortError,
|
|
2185
2670
|
EvalAssertionError,
|
|
2671
|
+
EvalFailFastError,
|
|
2186
2672
|
EvalOutcome,
|
|
2187
2673
|
EvalReporterDispatchError,
|
|
2674
|
+
EvalTimeoutError,
|
|
2188
2675
|
abstention,
|
|
2189
2676
|
agentEvalTarget,
|
|
2190
2677
|
answerRelevancy,
|
|
@@ -2193,14 +2680,17 @@ export {
|
|
|
2193
2680
|
contains,
|
|
2194
2681
|
containsAll,
|
|
2195
2682
|
containsAny,
|
|
2683
|
+
createEvalTypes,
|
|
2196
2684
|
defaultEvalTraceSelector,
|
|
2197
2685
|
defineEvalCases,
|
|
2686
|
+
defineEvalExpectations,
|
|
2198
2687
|
defineEvalSuite,
|
|
2199
2688
|
defineMetric,
|
|
2200
2689
|
doesNotMatch,
|
|
2201
2690
|
evalExitCode,
|
|
2202
2691
|
exactMatch,
|
|
2203
2692
|
faithfulness,
|
|
2693
|
+
formatEvalResult,
|
|
2204
2694
|
gEval,
|
|
2205
2695
|
hallucination,
|
|
2206
2696
|
jsonCorrectness,
|
|
@@ -2217,6 +2707,7 @@ export {
|
|
|
2217
2707
|
resolveEvalTraceRef,
|
|
2218
2708
|
runEvalCli,
|
|
2219
2709
|
runEvalSuite,
|
|
2710
|
+
selectEvalCaseIds,
|
|
2220
2711
|
selectPromptOutput,
|
|
2221
2712
|
semanticSimilarity,
|
|
2222
2713
|
summarization,
|