vitest-evals 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +112 -66
  2. package/dist/harness.d.mts +12 -1
  3. package/dist/harness.d.ts +12 -1
  4. package/dist/harness.js +8 -0
  5. package/dist/harness.js.map +1 -1
  6. package/dist/harness.mjs +7 -0
  7. package/dist/harness.mjs.map +1 -1
  8. package/dist/index.d.mts +48 -20
  9. package/dist/index.d.ts +48 -20
  10. package/dist/index.js +308 -21
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +304 -21
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/internal/scoring.d.mts +3 -3
  15. package/dist/internal/scoring.d.ts +3 -3
  16. package/dist/internal/scoring.js.map +1 -1
  17. package/dist/internal/toolCallScorer.js +42 -2
  18. package/dist/internal/toolCallScorer.js.map +1 -1
  19. package/dist/internal/toolCallScorer.mjs +42 -2
  20. package/dist/internal/toolCallScorer.mjs.map +1 -1
  21. package/dist/judges/factualityJudge.d.mts +151 -0
  22. package/dist/judges/factualityJudge.d.ts +151 -0
  23. package/dist/judges/factualityJudge.js +235 -0
  24. package/dist/judges/factualityJudge.js.map +1 -0
  25. package/dist/judges/factualityJudge.mjs +208 -0
  26. package/dist/judges/factualityJudge.mjs.map +1 -0
  27. package/dist/judges/index.d.mts +3 -1
  28. package/dist/judges/index.d.ts +3 -1
  29. package/dist/judges/index.js +447 -7
  30. package/dist/judges/index.js.map +1 -1
  31. package/dist/judges/index.mjs +443 -6
  32. package/dist/judges/index.mjs.map +1 -1
  33. package/dist/judges/judgeHarness.d.mts +122 -0
  34. package/dist/judges/judgeHarness.d.ts +122 -0
  35. package/dist/judges/judgeHarness.js +303 -0
  36. package/dist/judges/judgeHarness.js.map +1 -0
  37. package/dist/judges/judgeHarness.mjs +274 -0
  38. package/dist/judges/judgeHarness.mjs.map +1 -0
  39. package/dist/judges/structuredOutputJudge.d.mts +1 -0
  40. package/dist/judges/structuredOutputJudge.d.ts +1 -0
  41. package/dist/judges/toolCallJudge.d.mts +1 -0
  42. package/dist/judges/toolCallJudge.d.ts +1 -0
  43. package/dist/judges/toolCallJudge.js +42 -2
  44. package/dist/judges/toolCallJudge.js.map +1 -1
  45. package/dist/judges/toolCallJudge.mjs +42 -2
  46. package/dist/judges/toolCallJudge.mjs.map +1 -1
  47. package/dist/judges/types.d.mts +33 -6
  48. package/dist/judges/types.d.ts +33 -6
  49. package/dist/judges/types.js.map +1 -1
  50. package/dist/legacy/scorers/index.js +42 -2
  51. package/dist/legacy/scorers/index.js.map +1 -1
  52. package/dist/legacy/scorers/index.mjs +42 -2
  53. package/dist/legacy/scorers/index.mjs.map +1 -1
  54. package/dist/legacy/scorers/toolCallScorer.js +42 -2
  55. package/dist/legacy/scorers/toolCallScorer.js.map +1 -1
  56. package/dist/legacy/scorers/toolCallScorer.mjs +42 -2
  57. package/dist/legacy/scorers/toolCallScorer.mjs.map +1 -1
  58. package/dist/legacy.js +56 -3
  59. package/dist/legacy.js.map +1 -1
  60. package/dist/legacy.mjs +56 -3
  61. package/dist/legacy.mjs.map +1 -1
  62. package/dist/replay.js +1 -1
  63. package/dist/replay.js.map +1 -1
  64. package/dist/replay.mjs +1 -1
  65. package/dist/replay.mjs.map +1 -1
  66. package/dist/reporter.js.map +1 -1
  67. package/dist/reporter.mjs.map +1 -1
  68. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -184,6 +184,9 @@ function toolCalls(session) {
184
184
  function messagesByRole(session, role) {
185
185
  return session.messages.filter((message) => message.role === role);
186
186
  }
187
+ function hasNonEmptyMessageContent(message) {
188
+ return message.content !== void 0 && (typeof message.content !== "string" || message.content.trim().length > 0);
189
+ }
187
190
  function systemMessages(session) {
188
191
  return messagesByRole(session, "system");
189
192
  }
@@ -193,6 +196,9 @@ function userMessages(session) {
193
196
  function assistantMessages(session) {
194
197
  return messagesByRole(session, "assistant");
195
198
  }
199
+ function latestAssistantMessageContent(session) {
200
+ return [...assistantMessages(session)].reverse().find(hasNonEmptyMessageContent)?.content;
201
+ }
196
202
  function toolMessages(session) {
197
203
  return messagesByRole(session, "tool");
198
204
  }
@@ -231,6 +237,64 @@ function serializeError(error) {
231
237
  };
232
238
  }
233
239
 
240
+ // src/judges/judgeHarness.ts
241
+ function createJudgeHarness(options) {
242
+ return createHarness({
243
+ name: options.name ?? "judge-harness",
244
+ run: async ({ input, signal, metadata }) => {
245
+ return normalizeJudgeHarnessResult(
246
+ await options.run(input, { signal, metadata })
247
+ );
248
+ }
249
+ });
250
+ }
251
+ async function runJudgeHarness(judgeHarness, input, options = {}) {
252
+ const artifacts = {};
253
+ const run = await judgeHarness.run(input, {
254
+ metadata: options.metadata ?? {},
255
+ signal: options.signal,
256
+ artifacts,
257
+ setArtifact: (name, value) => {
258
+ artifacts[name] = value;
259
+ }
260
+ });
261
+ return run.output !== void 0 ? run.output : resolveJudgeHarnessAssistantOutput(run);
262
+ }
263
+ function createRunJudge(judgeHarness, signal) {
264
+ if (!judgeHarness) {
265
+ return void 0;
266
+ }
267
+ return (input, options) => runJudgeHarness(judgeHarness, input, {
268
+ metadata: options?.metadata,
269
+ signal
270
+ });
271
+ }
272
+ function normalizeJudgeHarnessResult(result) {
273
+ if (isHarnessRun(result)) {
274
+ return result;
275
+ }
276
+ if (hasOutputField(result)) {
277
+ return {
278
+ output: normalizeJudgeHarnessOutput(result.output)
279
+ };
280
+ }
281
+ return {
282
+ output: normalizeJudgeHarnessOutput(result)
283
+ };
284
+ }
285
+ function hasOutputField(value) {
286
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 1 && "output" in value;
287
+ }
288
+ function normalizeJudgeHarnessOutput(value) {
289
+ if (value === void 0) {
290
+ return void 0;
291
+ }
292
+ return normalizeContent(value);
293
+ }
294
+ function resolveJudgeHarnessAssistantOutput(run) {
295
+ return latestAssistantMessageContent(run.session) ?? "";
296
+ }
297
+
234
298
  // src/wrapText.ts
235
299
  function wrapText(text, width = 80) {
236
300
  if (!text || text.length <= width) {
@@ -253,6 +317,171 @@ function wrapText(text, width = 80) {
253
317
  return lines.join("\n");
254
318
  }
255
319
 
320
+ // src/judges/factualityJudge.ts
321
+ var FACTUALITY_CHOICE_SCORES = {
322
+ A: 0.4,
323
+ B: 0.6,
324
+ C: 1,
325
+ D: 0,
326
+ E: 1
327
+ };
328
+ var FACTUALITY_SYSTEM = "You are comparing factual content. Ignore differences in style, grammar, punctuation, and formatting.";
329
+ var FACTUALITY_RESPONSE_SCHEMA = {
330
+ type: "object",
331
+ additionalProperties: false,
332
+ required: ["choice", "rationale"],
333
+ properties: {
334
+ choice: {
335
+ enum: ["A", "B", "C", "D", "E"]
336
+ },
337
+ rationale: {
338
+ type: "string"
339
+ }
340
+ }
341
+ };
342
+ function FactualityJudge(config = {}) {
343
+ const judgeHarness = config.judgeHarness;
344
+ return {
345
+ name: config.name ?? "FactualityJudge",
346
+ judgeHarness,
347
+ assess: (opts) => assessFactuality(opts, judgeHarness)
348
+ };
349
+ }
350
+ async function assessFactuality(opts, configuredJudgeHarness) {
351
+ const metadata = opts.metadata;
352
+ const expected = opts.expected === void 0 ? metadata.expected : opts.expected;
353
+ if (isMissingExpectedAnswer(expected)) {
354
+ return {
355
+ score: 0,
356
+ metadata: {
357
+ rationale: "FactualityJudge requires a non-empty expert answer in `expected` or `metadata.expected`."
358
+ }
359
+ };
360
+ }
361
+ const runJudge = opts.runJudge ?? createRunJudge(
362
+ configuredJudgeHarness,
363
+ opts.signal
364
+ );
365
+ if (!runJudge) {
366
+ throw new Error(
367
+ "FactualityJudge requires a judgeHarness in FactualityJudge(...) config, describeEval(...) options, toSatisfyJudge(...) options, or JudgeContext.runJudge."
368
+ );
369
+ }
370
+ const verdict = await runJudge({
371
+ system: FACTUALITY_SYSTEM,
372
+ prompt: formatFactualityPrompt({
373
+ input: opts.input,
374
+ expected,
375
+ output: resolveJudgeOutput(opts)
376
+ }),
377
+ responseFormat: {
378
+ type: "json",
379
+ schema: FACTUALITY_RESPONSE_SCHEMA
380
+ }
381
+ });
382
+ return formatJudgeResult(parseFactualityJudgeVerdict(verdict));
383
+ }
384
+ function isMissingExpectedAnswer(value) {
385
+ return value == null || typeof value === "string" && value.trim().length === 0;
386
+ }
387
+ function resolveJudgeOutput(opts) {
388
+ if (opts.output !== void 0) {
389
+ return opts.output;
390
+ }
391
+ return latestAssistantMessageContent(opts.session) ?? "";
392
+ }
393
+ function parseFactualityJudgeVerdict(value) {
394
+ const parsed = typeof value === "string" ? parseJsonObject(value) : value;
395
+ if (!parsed || typeof parsed !== "object") {
396
+ throw new Error(
397
+ "FactualityJudge judgeHarness must return an object with `choice` and `rationale`."
398
+ );
399
+ }
400
+ const verdict = parsed;
401
+ if (!isFactualityChoice(verdict.choice)) {
402
+ throw new Error(
403
+ "FactualityJudge judgeHarness must return choice A, B, C, D, or E."
404
+ );
405
+ }
406
+ if (typeof verdict.rationale !== "string") {
407
+ throw new Error(
408
+ "FactualityJudge judgeHarness must return a string `rationale`."
409
+ );
410
+ }
411
+ return {
412
+ choice: verdict.choice,
413
+ rationale: verdict.rationale
414
+ };
415
+ }
416
+ function parseJsonObject(value) {
417
+ try {
418
+ return JSON.parse(value);
419
+ } catch {
420
+ const fencedJson = value.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
421
+ if (!fencedJson) {
422
+ throw new Error(
423
+ "FactualityJudge judgeHarness must return JSON with `choice` and `rationale`."
424
+ );
425
+ }
426
+ return JSON.parse(fencedJson[1]);
427
+ }
428
+ }
429
+ function isFactualityChoice(value) {
430
+ return value === "A" || value === "B" || value === "C" || value === "D" || value === "E";
431
+ }
432
+ function formatFactualityPrompt({
433
+ input,
434
+ expected,
435
+ output
436
+ }) {
437
+ const comparison = formatJudgeValue({
438
+ question: input ?? "",
439
+ expert_answer: expected,
440
+ submitted_answer: output ?? ""
441
+ });
442
+ return `Compare the submitted answer with the expert answer.
443
+
444
+ Comparison payload:
445
+ ${comparison}
446
+
447
+ Select exactly one option:
448
+ A: The submission is a fully consistent subset of the expert answer.
449
+ B: The submission is a fully consistent superset of the expert answer.
450
+ C: The submission contains the same factual details as the expert answer.
451
+ D: The submission disagrees with the expert answer.
452
+ E: The answers differ only in ways that do not affect factuality.
453
+
454
+ Return JSON with exactly these fields:
455
+ {
456
+ "choice": "C",
457
+ "rationale": "Brief explanation for the selected choice"
458
+ }
459
+
460
+ The choice value must be one of A, B, C, D, or E.`;
461
+ }
462
+ function formatJudgeValue(value) {
463
+ if (typeof value === "string") {
464
+ return value;
465
+ }
466
+ if (value === void 0) {
467
+ return "";
468
+ }
469
+ try {
470
+ return JSON.stringify(value, null, 2) ?? String(value);
471
+ } catch {
472
+ return String(value);
473
+ }
474
+ }
475
+ function formatJudgeResult(object) {
476
+ return {
477
+ score: FACTUALITY_CHOICE_SCORES[object.choice],
478
+ metadata: {
479
+ rationale: object.rationale,
480
+ choice: object.choice
481
+ }
482
+ };
483
+ }
484
+
256
485
  // src/internal/matchers.ts
257
486
  function isRecord(value) {
258
487
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -630,8 +859,8 @@ function evaluateOrderedTools(expected, actual, options) {
630
859
  score: expectedIndex / expected.length,
631
860
  metadata: {
632
861
  rationale: `Tool '${expectedTool.name}' called with incorrect arguments at position ${expectedIndex + 1} (${expectedIndex}/${expected.length} tools matched correctly)`,
633
- expected: expectedTool.arguments,
634
- actual: actualTool.arguments,
862
+ expected: normalizeContent(expectedTool.arguments),
863
+ actual: actualTool.arguments === void 0 ? void 0 : normalizeContent(actualTool.arguments),
635
864
  matched: expectedIndex,
636
865
  total: expected.length
637
866
  }
@@ -763,8 +992,8 @@ function ToolCallJudge(config = {}) {
763
992
  const metadata = opts.metadata;
764
993
  return scorer({
765
994
  ...opts,
766
- input: formatJudgeValue(opts.input),
767
- output: formatJudgeValue(opts.output),
995
+ input: formatJudgeValue2(opts.input),
996
+ output: formatJudgeValue2(opts.output),
768
997
  expectedTools: normalizeExpectedTools(
769
998
  opts.expectedTools ?? metadata.expectedTools
770
999
  )
@@ -777,7 +1006,7 @@ function normalizeExpectedTools(expectedTools) {
777
1006
  (tool) => typeof tool === "string" ? { name: tool } : tool
778
1007
  );
779
1008
  }
780
- function formatJudgeValue(value) {
1009
+ function formatJudgeValue2(value) {
781
1010
  if (typeof value === "string") {
782
1011
  return value;
783
1012
  }
@@ -800,9 +1029,17 @@ var evalTest = test.extend("harness", async () => {
800
1029
  }).extend(
801
1030
  "automaticJudges",
802
1031
  []
803
- ).extend("judgeThreshold", void 0).extend(
1032
+ ).extend("judgeThreshold", void 0).extend("judgeHarness", void 0).extend("explicitJudgeHarness", void 0).extend(
804
1033
  "run",
805
- async ({ automaticJudges, harness, judgeThreshold, signal, task }) => {
1034
+ async ({
1035
+ automaticJudges,
1036
+ explicitJudgeHarness,
1037
+ harness,
1038
+ judgeHarness,
1039
+ judgeThreshold,
1040
+ signal,
1041
+ task
1042
+ }) => {
806
1043
  return async (input, options) => {
807
1044
  const resolvedHarness = harness;
808
1045
  const metadata = createMetadata(options?.metadata);
@@ -830,6 +1067,7 @@ var evalTest = test.extend("harness", async () => {
830
1067
  partialRun,
831
1068
  resolvedHarness,
832
1069
  input,
1070
+ explicitJudgeHarness,
833
1071
  metadata,
834
1072
  signal
835
1073
  );
@@ -840,7 +1078,14 @@ var evalTest = test.extend("harness", async () => {
840
1078
  run.artifacts = artifacts;
841
1079
  }
842
1080
  setHarnessMeta(task, resolvedHarness.name, run);
843
- recordJudgeRunContext(run, resolvedHarness, input, metadata, signal);
1081
+ recordJudgeRunContext(
1082
+ run,
1083
+ resolvedHarness,
1084
+ input,
1085
+ explicitJudgeHarness,
1086
+ metadata,
1087
+ signal
1088
+ );
844
1089
  if (automaticJudges.length > 0) {
845
1090
  await applyAutomaticJudges(
846
1091
  task,
@@ -848,6 +1093,7 @@ var evalTest = test.extend("harness", async () => {
848
1093
  judgeThreshold,
849
1094
  resolvedHarness,
850
1095
  input,
1096
+ judgeHarness,
851
1097
  metadata,
852
1098
  run,
853
1099
  signal
@@ -862,6 +1108,7 @@ expect.extend({
862
1108
  const { threshold = 1, ...context } = options ?? {};
863
1109
  const judgeOptions = buildJudgeAssertionOptions(
864
1110
  received,
1111
+ judge,
865
1112
  context,
866
1113
  isEvalTaskLike(this.task) ? this.task : void 0
867
1114
  );
@@ -902,9 +1149,13 @@ function formatJudgeOutputForMessage(output) {
902
1149
  function describeEval(name, options, define) {
903
1150
  const suite = options.skipIf ? describe.skipIf(options.skipIf()) : describe;
904
1151
  return suite(name, () => {
1152
+ const automaticJudges = options.judges ?? [];
1153
+ const explicitJudgeHarness = options.judgeHarness ?? resolveDefaultJudgeHarness(automaticJudges);
905
1154
  const it = evalTest.override({
906
1155
  harness: options.harness,
907
- automaticJudges: options.judges ?? [],
1156
+ automaticJudges,
1157
+ judgeHarness: options.judgeHarness,
1158
+ explicitJudgeHarness,
908
1159
  judgeThreshold: options.judgeThreshold
909
1160
  });
910
1161
  define(it);
@@ -913,10 +1164,14 @@ function describeEval(name, options, define) {
913
1164
  function createMetadata(metadata) {
914
1165
  return { ...metadata ?? {} };
915
1166
  }
916
- async function applyAutomaticJudges(task, judges, threshold, harness, input, metadata, run, signal) {
1167
+ async function applyAutomaticJudges(task, judges, threshold, harness, input, judgeHarness, metadata, run, signal) {
917
1168
  const runToolCalls = toolCalls(run.session);
918
1169
  const scores = await Promise.all(
919
1170
  judges.map((judge) => {
1171
+ const runJudge = createRunJudge(
1172
+ resolveJudgeHarnessForJudge(judge, judgeHarness),
1173
+ signal
1174
+ );
920
1175
  const judgeOptions = {
921
1176
  input,
922
1177
  output: run.output,
@@ -925,7 +1180,8 @@ async function applyAutomaticJudges(task, judges, threshold, harness, input, met
925
1180
  run,
926
1181
  session: run.session,
927
1182
  signal,
928
- harness
1183
+ harness,
1184
+ runJudge
929
1185
  };
930
1186
  return Promise.resolve(judge.assess(judgeOptions));
931
1187
  })
@@ -965,10 +1221,11 @@ function setHarnessMeta(task, name, run) {
965
1221
  run
966
1222
  };
967
1223
  }
968
- function recordJudgeRunContext(run, harness, input, metadata, signal) {
1224
+ function recordJudgeRunContext(run, harness, input, judgeHarness, metadata, signal) {
969
1225
  const context = {
970
1226
  harness,
971
1227
  input,
1228
+ judgeHarness,
972
1229
  metadata,
973
1230
  run,
974
1231
  signal
@@ -1029,13 +1286,15 @@ function formatJudgeTextOutput(run) {
1029
1286
  }
1030
1287
  return typeof assistantOutput === "string" ? assistantOutput : JSON.stringify(assistantOutput);
1031
1288
  }
1032
- function buildJudgeAssertionOptions(received, options, task) {
1289
+ function buildJudgeAssertionOptions(received, judge, options, task) {
1033
1290
  const registeredContext = resolveRegisteredJudgeRunContext(
1034
1291
  received,
1035
1292
  options,
1036
1293
  task
1037
1294
  );
1038
1295
  const harness = options.harness ?? registeredContext?.harness;
1296
+ const judgeHarness = options.judgeHarness ?? resolveJudgeHarnessForJudge(judge, registeredContext?.judgeHarness);
1297
+ const runJudge = createRunJudge(judgeHarness, registeredContext?.signal);
1039
1298
  const signal = registeredContext?.signal;
1040
1299
  const metadata = options.metadata ?? registeredContext?.metadata ?? {};
1041
1300
  const input = options.input ?? registeredContext?.input ?? void 0;
@@ -1055,8 +1314,9 @@ function buildJudgeAssertionOptions(received, options, task) {
1055
1314
  options.output
1056
1315
  );
1057
1316
  const resolvedToolCalls = options.toolCalls ?? toolCalls(run.session);
1317
+ const { judgeHarness: _judgeHarness, ...judgeParams } = options;
1058
1318
  return {
1059
- ...options,
1319
+ ...judgeParams,
1060
1320
  input: resolvedInput,
1061
1321
  output,
1062
1322
  metadata,
@@ -1064,9 +1324,23 @@ function buildJudgeAssertionOptions(received, options, task) {
1064
1324
  session: options.session ?? run.session,
1065
1325
  signal,
1066
1326
  toolCalls: resolvedToolCalls,
1067
- harness
1327
+ harness,
1328
+ runJudge
1068
1329
  };
1069
1330
  }
1331
+ function resolveJudgeHarnessForJudge(judge, fallback) {
1332
+ return judge.judgeHarness ?? fallback;
1333
+ }
1334
+ function resolveDefaultJudgeHarness(judges) {
1335
+ const configured = judges.map((judge) => judge.judgeHarness).filter(
1336
+ (judgeHarness) => Boolean(judgeHarness)
1337
+ );
1338
+ const [first] = configured;
1339
+ if (!first || configured.some((judgeHarness) => judgeHarness !== first)) {
1340
+ return void 0;
1341
+ }
1342
+ return first;
1343
+ }
1070
1344
  function resolveRegisteredJudgeRunContext(received, options, task) {
1071
1345
  if (options.run) {
1072
1346
  return getRegisteredJudgeRunContext(options.run);
@@ -1156,11 +1430,8 @@ function inferJudgeOutputValue(received, session) {
1156
1430
  return normalizeJudgeJsonValue(received);
1157
1431
  }
1158
1432
  function resolveAssistantOutput(session) {
1159
- const assistantContent = [...assistantMessages(session)].reverse().find(hasAssistantOutputContent);
1160
- return assistantContent?.content !== void 0 ? normalizeContent(assistantContent.content) : void 0;
1161
- }
1162
- function hasAssistantOutputContent(message) {
1163
- return message.content !== void 0 && (typeof message.content !== "string" || message.content.trim().length > 0);
1433
+ const assistantContent = latestAssistantMessageContent(session);
1434
+ return assistantContent !== void 0 ? normalizeContent(assistantContent) : void 0;
1164
1435
  }
1165
1436
  function normalizeJudgeJsonValue(value) {
1166
1437
  if (value === void 0) {
@@ -1189,7 +1460,15 @@ reason ${wrapText(s.metadata.rationale)}` : ""}${formattedOutput}`;
1189
1460
  return scoreLine;
1190
1461
  }).join("\n\n");
1191
1462
  }
1192
- function createJudge(name, assessOrAssessor, assess) {
1463
+ function createJudge(nameOrConfig, assessOrAssessor, assess) {
1464
+ if (typeof nameOrConfig !== "string") {
1465
+ return {
1466
+ name: nameOrConfig.name,
1467
+ judgeHarness: nameOrConfig.judgeHarness,
1468
+ assess: nameOrConfig.assess
1469
+ };
1470
+ }
1471
+ const name = nameOrConfig;
1193
1472
  if (!assess) {
1194
1473
  return {
1195
1474
  name,
@@ -1209,17 +1488,21 @@ function createJudge(name, assessOrAssessor, assess) {
1209
1488
  };
1210
1489
  }
1211
1490
  export {
1491
+ FactualityJudge,
1212
1492
  StructuredOutputJudge,
1213
1493
  ToolCallJudge,
1214
1494
  assistantMessages,
1215
1495
  attachHarnessRunToError,
1216
1496
  createHarness,
1217
1497
  createJudge,
1498
+ createJudgeHarness,
1218
1499
  describeEval,
1219
1500
  formatScores,
1220
1501
  getHarnessRunFromError,
1502
+ latestAssistantMessageContent,
1221
1503
  messagesByRole,
1222
1504
  normalizeHarnessRun,
1505
+ runJudgeHarness,
1223
1506
  systemMessages,
1224
1507
  toolCalls,
1225
1508
  toolMessages,