@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.
Files changed (62) hide show
  1. package/README.md +168 -3
  2. package/dist/agent/index.d.ts +12 -12
  3. package/dist/agent/index.js +5 -5
  4. package/dist/agent/interactions/index.d.ts +1 -1
  5. package/dist/{agent-B-3jCRmI.d.ts → agent-DTmzs1Qr.d.ts} +16 -13
  6. package/dist/chunk-4UQALF5Y.js +117 -0
  7. package/dist/chunk-4UQALF5Y.js.map +1 -0
  8. package/dist/{chunk-HNRNTFMQ.js → chunk-GAOZ263Y.js} +15 -4
  9. package/dist/chunk-GAOZ263Y.js.map +1 -0
  10. package/dist/{chunk-XMVOBX43.js → chunk-JLJMVRRE.js} +2 -2
  11. package/dist/{chunk-DHJL44R2.js → chunk-JTJU56ZV.js} +2 -2
  12. package/dist/{chunk-TB5EKZM7.js → chunk-OI3LSMJG.js} +30 -1
  13. package/dist/chunk-OI3LSMJG.js.map +1 -0
  14. package/dist/{chunk-34EDX4ZL.js → chunk-Q5BCNYED.js} +2 -2
  15. package/dist/completion/index.d.ts +3 -3
  16. package/dist/completion/index.js +10 -2
  17. package/dist/{dynamic-tools-aIZlg5x1.d.ts → dynamic-tools-CNHSPXr1.d.ts} +3 -3
  18. package/dist/evals/index.d.ts +195 -49
  19. package/dist/evals/index.js +617 -126
  20. package/dist/evals/index.js.map +1 -1
  21. package/dist/extractor/index.d.ts +1 -1
  22. package/dist/extractor/index.js +3 -3
  23. package/dist/guardrails/index.d.ts +4 -4
  24. package/dist/image-generation/index.d.ts +1 -1
  25. package/dist/index.d.ts +14 -14
  26. package/dist/index.js +10 -5
  27. package/dist/internal/agent.d.ts +11 -11
  28. package/dist/internal/agent.js +6 -5
  29. package/dist/internal/agent.js.map +1 -1
  30. package/dist/mcp/index.d.ts +4 -4
  31. package/dist/memory/index.d.ts +3 -3
  32. package/dist/memory/index.js +2 -2
  33. package/dist/{middleware-kcF8AusP.d.ts → middleware-mY_lpjDp.d.ts} +1 -1
  34. package/dist/observability/index.d.ts +6 -6
  35. package/dist/observability/index.js +3 -3
  36. package/dist/pipeline/index.d.ts +12 -12
  37. package/dist/pipeline/index.js +3 -3
  38. package/dist/pipeline/index.js.map +1 -1
  39. package/dist/{provider-output-error-BEwDgVv3.d.ts → provider-output-error-OQ4fjw5W.d.ts} +10 -2
  40. package/dist/skills/index.d.ts +4 -4
  41. package/dist/speech-generation/index.d.ts +1 -1
  42. package/dist/{text-ZLECpQE7.d.ts → text-CmjXxakS.d.ts} +1 -1
  43. package/dist/{think-tool-BwCsJ365.d.ts → think-tool-YWsV98I5.d.ts} +1 -1
  44. package/dist/tool/index.d.ts +6 -6
  45. package/dist/{tool-BpqpoRSE.d.ts → tool-DQMZpZSS.d.ts} +1 -1
  46. package/dist/transcription/index.d.ts +1 -1
  47. package/dist/{types-DC1U1XwW.d.ts → types-C1xYKXF2.d.ts} +1 -1
  48. package/dist/{types-CXNE592e.d.ts → types-CODGMHnp.d.ts} +1 -1
  49. package/dist/{types-DL7wX6hJ.d.ts → types-CRda8x7p.d.ts} +8 -7
  50. package/dist/{types-DmzRCbU5.d.ts → types-CafU6Ux5.d.ts} +1 -1
  51. package/dist/{types-DgvozfPc.d.ts → types-CiHjqE6J.d.ts} +19 -3
  52. package/dist/{types-DjqRHeAi.d.ts → types-DfRPfehE.d.ts} +2 -2
  53. package/dist/{types-DhkodEft.d.ts → types-h0EL6Xn0.d.ts} +1 -1
  54. package/dist/vector-store/index.d.ts +4 -4
  55. package/package.json +1 -1
  56. package/dist/chunk-D4PACGPA.js +0 -37
  57. package/dist/chunk-D4PACGPA.js.map +0 -1
  58. package/dist/chunk-HNRNTFMQ.js.map +0 -1
  59. package/dist/chunk-TB5EKZM7.js.map +0 -1
  60. /package/dist/{chunk-XMVOBX43.js.map → chunk-JLJMVRRE.js.map} +0 -0
  61. /package/dist/{chunk-DHJL44R2.js.map → chunk-JTJU56ZV.js.map} +0 -0
  62. /package/dist/{chunk-34EDX4ZL.js.map → chunk-Q5BCNYED.js.map} +0 -0
@@ -10,11 +10,11 @@ import {
10
10
  } from "../chunk-IZNOP6JG.js";
11
11
  import {
12
12
  extract
13
- } from "../chunk-DHJL44R2.js";
14
- import "../chunk-D4PACGPA.js";
13
+ } from "../chunk-JTJU56ZV.js";
14
+ import "../chunk-4UQALF5Y.js";
15
15
  import {
16
16
  Usage
17
- } from "../chunk-TB5EKZM7.js";
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 stableComparable(value) {
36
- if (typeof value === "string") {
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
- outcome.comment = options.comment;
139
- }
140
- if (options.metadata !== void 0) {
141
- outcome.metadata = options.metadata;
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
- return typeof value === "string" ? value : JSON.stringify(value);
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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.invalid(errorMessage(error));
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(options, startedAtMs);
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: options.cases.length,
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(options, run);
1331
- aggregates = await aggregateResult(options, results);
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 concurrency = Math.max(1, Math.trunc(options.concurrency ?? 1));
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(concurrency, options.cases.length) }, () => worker())
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
- let output;
1401
- let targetError;
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
- output = await options.target(testCase.input, testCase);
1404
- } catch (error) {
1405
- targetError = error;
1406
- }
1407
- const traceResult = await resolveTrace(options, testCase, output, targetError);
1408
- const metrics = [];
1409
- for (const metric of options.metrics) {
1410
- const outcome = targetError === void 0 ? await safeEvaluate(options.name, testCase, output, metric) : EvalOutcome.invalid(`Target failed: ${errorMessage(targetError)}`);
1411
- const reporterErrors = await reportOutcome({
1412
- run,
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
- metric,
1418
- outcome,
1419
- trace: traceResult.trace,
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 metricResult = {
1425
- metricName: metric.name,
1426
- required: metric.required ?? true,
1427
- outcome,
1428
- reporterErrors
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 (metric.direction !== void 0) metricResult.direction = metric.direction;
1431
- if (metric.threshold !== void 0) metricResult.threshold = metric.threshold;
1432
- metrics.push(metricResult);
1433
- }
1434
- const scores = Object.fromEntries(
1435
- metrics.map((metric) => [metric.metricName, metric.outcome])
1436
- );
1437
- const result = {
1438
- case: testCase,
1439
- outcome: caseOutcome(targetError, metrics),
1440
- metrics,
1441
- scores
1442
- };
1443
- if (output !== void 0) {
1444
- result.output = output;
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 metric.evaluate({ suiteName, case: testCase, output });
1697
+ return await abortable(
1698
+ signal,
1699
+ Promise.resolve(metric.evaluate({ suiteName, case: testCase, output, signal }))
1700
+ );
1468
1701
  } catch (error) {
1469
- return EvalOutcome.invalid(errorMessage(error));
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(targetError, metrics) {
1576
- if (targetError !== void 0) return "invalid";
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
- if (result.output !== void 0) {
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
- targetCost += await calculateCost(
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.output !== void 0) {
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
- evaluationCost += await calculateCost(
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({ suiteName: options.name, case: testCase, output });
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(`${format === "json" ? jsonResult(result) : prettyResult(result)}
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.output !== void 0) lines.push(` output: ${displayValue(caseResult.output)}`);
1761
- if (caseResult.targetError !== void 0) {
1762
- lines.push(` target error: ${errorText(caseResult.targetError)}`);
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(`score=${displayValue(metric.outcome.score)}`);
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) lines.push(` ${explanation}`);
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
- if (typeof value === "string") return value;
1890
- try {
1891
- return JSON.stringify(value);
1892
- } catch {
1893
- return String(value);
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 errorText(error) {
1897
- return error instanceof Error ? error.message : displayValue(error);
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 = stableComparable(actual) === stableComparable(expected);
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: options.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 >= options.threshold ? EvalOutcome.pass(score) : EvalOutcome.fail(score, { comment: `Similarity below threshold ${options.threshold}.` });
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: evalExtractionRetries(options.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.invalid(errorMessage(error));
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: options.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: evalExtractionRetries(options.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 >= options.threshold ? EvalOutcome.pass(score, { comment: score.feedback, usage: result.usage }) : EvalOutcome.fail(score, { comment: score.feedback, usage: result.usage });
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.invalid(errorMessage(error));
2630
+ return EvalOutcome.fromError(error);
2159
2631
  }
2160
2632
  }
2161
2633
  };
2162
2634
  }
2163
- function evalExtractionRetries(retries) {
2164
- const retryCount = Math.max(0, Math.trunc(retries ?? 0));
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 defineEvalSuite(options) {
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,