@anvia/core 0.22.0 → 0.24.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.
- package/dist/agent/index.js +7 -7
- package/dist/{chunk-VXONXP2G.js → chunk-BALBBVI3.js} +5 -5
- package/dist/{chunk-YEBIVZM2.js → chunk-DALYP4PX.js} +4 -4
- package/dist/{chunk-2NDQHV7V.js → chunk-HYUYZEAJ.js} +5 -5
- package/dist/{chunk-4HWN7734.js → chunk-JI5YZWNT.js} +3 -3
- package/dist/{chunk-4BGN6PYF.js → chunk-MELYDKWQ.js} +4 -4
- package/dist/{chunk-BCA4VZ5W.js → chunk-NCPN4SJR.js} +2 -2
- package/dist/{chunk-KYEBWN3E.js → chunk-YLMDBQO4.js} +4 -4
- package/dist/evals/index.d.ts +154 -4
- package/dist/evals/index.js +1155 -38
- package/dist/evals/index.js.map +1 -1
- package/dist/extractor/index.js +8 -8
- package/dist/index.js +8 -8
- package/dist/internal/agent.js +6 -6
- package/dist/request/index.js +5 -5
- package/dist/skills/index.js +5 -5
- package/dist/tool/index.js +4 -4
- package/dist/vector-store/index.js +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-VXONXP2G.js.map → chunk-BALBBVI3.js.map} +0 -0
- /package/dist/{chunk-YEBIVZM2.js.map → chunk-DALYP4PX.js.map} +0 -0
- /package/dist/{chunk-2NDQHV7V.js.map → chunk-HYUYZEAJ.js.map} +0 -0
- /package/dist/{chunk-4HWN7734.js.map → chunk-JI5YZWNT.js.map} +0 -0
- /package/dist/{chunk-4BGN6PYF.js.map → chunk-MELYDKWQ.js.map} +0 -0
- /package/dist/{chunk-BCA4VZ5W.js.map → chunk-NCPN4SJR.js.map} +0 -0
- /package/dist/{chunk-KYEBWN3E.js.map → chunk-YLMDBQO4.js.map} +0 -0
package/dist/evals/index.js
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ExtractorBuilder
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-YLMDBQO4.js";
|
|
4
|
+
import "../chunk-JI5YZWNT.js";
|
|
5
|
+
import "../chunk-DALYP4PX.js";
|
|
6
|
+
import "../chunk-BALBBVI3.js";
|
|
7
7
|
import "../chunk-YK4WAAS4.js";
|
|
8
8
|
import "../chunk-XUUY2L2D.js";
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-HYUYZEAJ.js";
|
|
10
|
+
import "../chunk-MELYDKWQ.js";
|
|
11
11
|
import "../chunk-2ODTMRHP.js";
|
|
12
12
|
import "../chunk-UQX6EXWG.js";
|
|
13
13
|
import "../chunk-MRQLEK2B.js";
|
|
14
|
-
import "../chunk-Q25IWLBT.js";
|
|
15
|
-
import "../chunk-MC3CCKEB.js";
|
|
16
|
-
import "../chunk-ADH7NNCS.js";
|
|
17
|
-
import "../chunk-WQKHFADH.js";
|
|
18
14
|
import {
|
|
19
15
|
cosineSimilarity,
|
|
20
16
|
embedText
|
|
@@ -22,23 +18,15 @@ import {
|
|
|
22
18
|
import {
|
|
23
19
|
mapWithConcurrency
|
|
24
20
|
} from "../chunk-OIMLU4SF.js";
|
|
21
|
+
import "../chunk-Q25IWLBT.js";
|
|
22
|
+
import "../chunk-MC3CCKEB.js";
|
|
23
|
+
import {
|
|
24
|
+
Usage
|
|
25
|
+
} from "../chunk-ADH7NNCS.js";
|
|
26
|
+
import "../chunk-WQKHFADH.js";
|
|
25
27
|
import "../chunk-CWUJUSOS.js";
|
|
26
28
|
|
|
27
|
-
// src/evals/
|
|
28
|
-
function agentEvalTarget(agent, options = {}) {
|
|
29
|
-
return async (input, testCase) => {
|
|
30
|
-
const prompt = options.prompt?.(input, testCase) ?? String(input);
|
|
31
|
-
const response = await agent.prompt(prompt).send();
|
|
32
|
-
return options.output === void 0 ? response : options.output(response, testCase);
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/evals/metric.ts
|
|
37
|
-
function defineMetric(metric) {
|
|
38
|
-
return metric;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// src/evals/metrics.ts
|
|
29
|
+
// src/evals/advanced-metrics.ts
|
|
42
30
|
import { z } from "zod";
|
|
43
31
|
|
|
44
32
|
// src/evals/format.ts
|
|
@@ -68,6 +56,35 @@ function errorMessage(error) {
|
|
|
68
56
|
return error instanceof Error ? error.message : String(error);
|
|
69
57
|
}
|
|
70
58
|
|
|
59
|
+
// src/evals/judge.ts
|
|
60
|
+
async function runJudge(args) {
|
|
61
|
+
const result = await new ExtractorBuilder(args.model, args.schema).instructions(args.instructions).temperature(0).retries(args.retries).build().extractWithUsage(args.prompt);
|
|
62
|
+
return { data: result.data, usage: result.usage };
|
|
63
|
+
}
|
|
64
|
+
function addUsage(...values) {
|
|
65
|
+
return values.reduce((total, usage) => Usage.add(total, usage), Usage.empty());
|
|
66
|
+
}
|
|
67
|
+
function evaluationMetadata(details, usage) {
|
|
68
|
+
const evaluation = {
|
|
69
|
+
...details,
|
|
70
|
+
usage: usageToJson(usage)
|
|
71
|
+
};
|
|
72
|
+
return { evaluation };
|
|
73
|
+
}
|
|
74
|
+
function usageToJson(usage) {
|
|
75
|
+
const value = {
|
|
76
|
+
inputTokens: usage.inputTokens,
|
|
77
|
+
outputTokens: usage.outputTokens,
|
|
78
|
+
totalTokens: usage.totalTokens,
|
|
79
|
+
cachedInputTokens: usage.cachedInputTokens,
|
|
80
|
+
cacheCreationInputTokens: usage.cacheCreationInputTokens
|
|
81
|
+
};
|
|
82
|
+
if (usage.details !== void 0) {
|
|
83
|
+
value.details = { ...usage.details };
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
71
88
|
// src/evals/outcome.ts
|
|
72
89
|
var EvalOutcome = {
|
|
73
90
|
pass(score, options = {}) {
|
|
@@ -145,7 +162,872 @@ async function resolveJudgePrompt(selector, args) {
|
|
|
145
162
|
].join("\n\n");
|
|
146
163
|
}
|
|
147
164
|
|
|
165
|
+
// src/evals/advanced-metrics.ts
|
|
166
|
+
var statementsSchema = z.object({ statements: z.array(z.string()) });
|
|
167
|
+
var factsSchema = z.object({ facts: z.array(z.string()) });
|
|
168
|
+
var questionsSchema = z.object({ questions: z.array(z.string()) });
|
|
169
|
+
var answersSchema = z.object({ answers: z.array(z.enum(["yes", "no"])) });
|
|
170
|
+
var verdictsSchema = z.object({
|
|
171
|
+
verdicts: z.array(
|
|
172
|
+
z.object({
|
|
173
|
+
verdict: z.enum(["yes", "no", "idk"]),
|
|
174
|
+
reason: z.string().optional()
|
|
175
|
+
})
|
|
176
|
+
)
|
|
177
|
+
});
|
|
178
|
+
var binaryVerdictsSchema = z.object({
|
|
179
|
+
verdicts: z.array(
|
|
180
|
+
z.object({
|
|
181
|
+
verdict: z.enum(["yes", "no"]),
|
|
182
|
+
reason: z.string()
|
|
183
|
+
})
|
|
184
|
+
)
|
|
185
|
+
});
|
|
186
|
+
var binaryVerdictSchema = z.object({
|
|
187
|
+
verdict: z.enum(["yes", "no"]),
|
|
188
|
+
reason: z.string()
|
|
189
|
+
});
|
|
190
|
+
var reasonSchema = z.object({ reason: z.string() });
|
|
191
|
+
function answerRelevancy(options) {
|
|
192
|
+
const config = metricConfig(options, "answer_relevancy");
|
|
193
|
+
return numericMetric(config.name, async (args) => {
|
|
194
|
+
try {
|
|
195
|
+
const input = await resolveInput(options.input, args);
|
|
196
|
+
const actual = await resolveActualText(options.actual, args);
|
|
197
|
+
const statementResult = await runJudge({
|
|
198
|
+
model: options.model,
|
|
199
|
+
schema: statementsSchema,
|
|
200
|
+
instructions: "Break the answer into concise, independently assessable statements. Return every substantive statement using the schema.",
|
|
201
|
+
prompt: `Answer:
|
|
202
|
+
${actual}`,
|
|
203
|
+
retries: config.retries
|
|
204
|
+
});
|
|
205
|
+
const statements = statementResult.data.statements;
|
|
206
|
+
let verdicts = [];
|
|
207
|
+
let usage = statementResult.usage;
|
|
208
|
+
if (statements.length > 0) {
|
|
209
|
+
const verdictResult = await runJudge({
|
|
210
|
+
model: options.model,
|
|
211
|
+
schema: verdictsSchema,
|
|
212
|
+
instructions: "Classify each answer statement for relevance to the user input. Use yes for relevant, no for irrelevant, and idk only when relevance is genuinely indeterminate. Preserve order and return one verdict per statement.",
|
|
213
|
+
prompt: jsonPrompt({ input, statements }),
|
|
214
|
+
retries: config.retries
|
|
215
|
+
});
|
|
216
|
+
verdicts = verdictResult.data.verdicts;
|
|
217
|
+
assertSameLength("answer relevancy verdicts", statements, verdicts);
|
|
218
|
+
usage = addUsage(usage, verdictResult.usage);
|
|
219
|
+
}
|
|
220
|
+
const score = verdicts.length === 0 ? 1 : verdicts.filter((verdict) => verdict.verdict !== "no").length / verdicts.length;
|
|
221
|
+
const reasonResult = await maybeReason({
|
|
222
|
+
model: options.model,
|
|
223
|
+
includeReason: config.includeReason,
|
|
224
|
+
retries: config.retries,
|
|
225
|
+
metric: "answer relevancy",
|
|
226
|
+
score,
|
|
227
|
+
evidence: { input, verdicts }
|
|
228
|
+
});
|
|
229
|
+
usage = addUsage(usage, reasonResult.usage);
|
|
230
|
+
return higherOutcome({
|
|
231
|
+
score,
|
|
232
|
+
threshold: config.threshold,
|
|
233
|
+
strictMode: config.strictMode,
|
|
234
|
+
comment: reasonResult.reason,
|
|
235
|
+
details: { statements, verdicts },
|
|
236
|
+
usage
|
|
237
|
+
});
|
|
238
|
+
} catch (error) {
|
|
239
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function promptAlignment(options) {
|
|
244
|
+
if (options.promptInstructions.length === 0) {
|
|
245
|
+
throw new TypeError("promptAlignment requires at least one prompt instruction.");
|
|
246
|
+
}
|
|
247
|
+
const config = metricConfig(options, "prompt_alignment");
|
|
248
|
+
return numericMetric(config.name, async (args) => {
|
|
249
|
+
try {
|
|
250
|
+
const input = await resolveInput(options.input, args);
|
|
251
|
+
const actual = await resolveActualText(options.actual, args);
|
|
252
|
+
const verdictResult = await runJudge({
|
|
253
|
+
model: options.model,
|
|
254
|
+
schema: binaryVerdictsSchema,
|
|
255
|
+
instructions: "Determine whether the answer follows each prompt instruction. Preserve order and return exactly one yes or no verdict per instruction.",
|
|
256
|
+
prompt: jsonPrompt({ input, actual, instructions: options.promptInstructions }),
|
|
257
|
+
retries: config.retries
|
|
258
|
+
});
|
|
259
|
+
const verdicts = verdictResult.data.verdicts;
|
|
260
|
+
assertSameLength("prompt alignment verdicts", options.promptInstructions, verdicts);
|
|
261
|
+
const score = verdicts.filter((verdict) => verdict.verdict === "yes").length / verdicts.length;
|
|
262
|
+
const reasonResult = await maybeReason({
|
|
263
|
+
model: options.model,
|
|
264
|
+
includeReason: config.includeReason,
|
|
265
|
+
retries: config.retries,
|
|
266
|
+
metric: "prompt alignment",
|
|
267
|
+
score,
|
|
268
|
+
evidence: { verdicts }
|
|
269
|
+
});
|
|
270
|
+
const usage = addUsage(verdictResult.usage, reasonResult.usage);
|
|
271
|
+
return higherOutcome({
|
|
272
|
+
score,
|
|
273
|
+
threshold: config.threshold,
|
|
274
|
+
strictMode: config.strictMode,
|
|
275
|
+
comment: reasonResult.reason,
|
|
276
|
+
details: { promptInstructions: options.promptInstructions, verdicts },
|
|
277
|
+
usage
|
|
278
|
+
});
|
|
279
|
+
} catch (error) {
|
|
280
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
function jsonCorrectness(options) {
|
|
285
|
+
const threshold = validateThreshold(options.threshold ?? 0.5);
|
|
286
|
+
const retries = validateRetries(options.retries ?? 0);
|
|
287
|
+
const includeReason = options.includeReason ?? true;
|
|
288
|
+
return numericMetric(options.name ?? "json_correctness", async (args) => {
|
|
289
|
+
try {
|
|
290
|
+
const actual = await resolveActualText(options.actual, args);
|
|
291
|
+
let parsed;
|
|
292
|
+
let validationError;
|
|
293
|
+
try {
|
|
294
|
+
parsed = JSON.parse(actual);
|
|
295
|
+
const result = options.schema.safeParse(parsed);
|
|
296
|
+
if (!result.success) {
|
|
297
|
+
validationError = z.prettifyError(result.error);
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
validationError = errorMessage(error);
|
|
301
|
+
}
|
|
302
|
+
const score = validationError === void 0 ? 1 : 0;
|
|
303
|
+
let comment;
|
|
304
|
+
let usage = Usage.empty();
|
|
305
|
+
if (includeReason) {
|
|
306
|
+
if (score === 1) {
|
|
307
|
+
comment = "The generated JSON is syntactically valid and matches the expected schema.";
|
|
308
|
+
} else if (options.model === void 0) {
|
|
309
|
+
comment = validationError;
|
|
310
|
+
} else {
|
|
311
|
+
const reasonResult = await runJudge({
|
|
312
|
+
model: options.model,
|
|
313
|
+
schema: reasonSchema,
|
|
314
|
+
instructions: "Briefly explain why the generated JSON does not match the expected schema. Focus on actionable syntax, field, and type problems.",
|
|
315
|
+
prompt: jsonPrompt({ actual, validationError }),
|
|
316
|
+
retries
|
|
317
|
+
});
|
|
318
|
+
comment = reasonResult.data.reason;
|
|
319
|
+
usage = reasonResult.usage;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return higherOutcome({
|
|
323
|
+
score,
|
|
324
|
+
threshold,
|
|
325
|
+
strictMode: options.strictMode ?? true,
|
|
326
|
+
comment,
|
|
327
|
+
details: validationError === void 0 ? {} : { validationError },
|
|
328
|
+
usage
|
|
329
|
+
});
|
|
330
|
+
} catch (error) {
|
|
331
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
function hallucination(options) {
|
|
336
|
+
const config = metricConfig(options, "hallucination");
|
|
337
|
+
return numericMetric(config.name, async (args) => {
|
|
338
|
+
try {
|
|
339
|
+
const actual = await resolveActualText(options.actual, args);
|
|
340
|
+
const context = await resolveStringList(options.context, args.case.context, args, "context");
|
|
341
|
+
const verdictResult = await runJudge({
|
|
342
|
+
model: options.model,
|
|
343
|
+
schema: binaryVerdictsSchema,
|
|
344
|
+
instructions: "Compare the answer with each trusted context. Use yes when the answer is factually aligned with that context and no when it contradicts it. Preserve order and return one verdict per context.",
|
|
345
|
+
prompt: jsonPrompt({ actual, context }),
|
|
346
|
+
retries: config.retries
|
|
347
|
+
});
|
|
348
|
+
const verdicts = verdictResult.data.verdicts;
|
|
349
|
+
assertSameLength("hallucination verdicts", context, verdicts);
|
|
350
|
+
const score = verdicts.filter((verdict) => verdict.verdict === "no").length / verdicts.length;
|
|
351
|
+
const reasonResult = await maybeReason({
|
|
352
|
+
model: options.model,
|
|
353
|
+
includeReason: config.includeReason,
|
|
354
|
+
retries: config.retries,
|
|
355
|
+
metric: "hallucination",
|
|
356
|
+
score,
|
|
357
|
+
evidence: { verdicts }
|
|
358
|
+
});
|
|
359
|
+
const usage = addUsage(verdictResult.usage, reasonResult.usage);
|
|
360
|
+
return lowerOutcome({
|
|
361
|
+
score,
|
|
362
|
+
threshold: config.threshold,
|
|
363
|
+
strictMode: config.strictMode,
|
|
364
|
+
comment: reasonResult.reason,
|
|
365
|
+
details: { verdicts },
|
|
366
|
+
usage
|
|
367
|
+
});
|
|
368
|
+
} catch (error) {
|
|
369
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
function faithfulness(options) {
|
|
374
|
+
const config = metricConfig(options, "faithfulness");
|
|
375
|
+
const truthsExtractionLimit = validateOptionalNonNegativeInteger(
|
|
376
|
+
options.truthsExtractionLimit,
|
|
377
|
+
"truthsExtractionLimit"
|
|
378
|
+
);
|
|
379
|
+
return numericMetric(config.name, async (args) => {
|
|
380
|
+
try {
|
|
381
|
+
const actual = await resolveActualText(options.actual, args);
|
|
382
|
+
const retrievalContext = await resolveStringList(
|
|
383
|
+
options.retrievalContext,
|
|
384
|
+
args.case.retrievalContext,
|
|
385
|
+
args,
|
|
386
|
+
"retrievalContext"
|
|
387
|
+
);
|
|
388
|
+
const [truthResult, claimResult] = await Promise.all([
|
|
389
|
+
runJudge({
|
|
390
|
+
model: options.model,
|
|
391
|
+
schema: factsSchema,
|
|
392
|
+
instructions: truthsExtractionInstructions(truthsExtractionLimit),
|
|
393
|
+
prompt: jsonPrompt({ retrievalContext }),
|
|
394
|
+
retries: config.retries
|
|
395
|
+
}),
|
|
396
|
+
runJudge({
|
|
397
|
+
model: options.model,
|
|
398
|
+
schema: factsSchema,
|
|
399
|
+
instructions: "Extract every concise factual claim made by the answer. Return claims in the facts array and omit opinions or purely stylistic text.",
|
|
400
|
+
prompt: `Answer:
|
|
401
|
+
${actual}`,
|
|
402
|
+
retries: config.retries
|
|
403
|
+
})
|
|
404
|
+
]);
|
|
405
|
+
const truths = limitValues(truthResult.data.facts, truthsExtractionLimit);
|
|
406
|
+
const claims = claimResult.data.facts;
|
|
407
|
+
let verdicts = [];
|
|
408
|
+
let usage = addUsage(truthResult.usage, claimResult.usage);
|
|
409
|
+
if (claims.length > 0) {
|
|
410
|
+
const verdictResult = await runJudge({
|
|
411
|
+
model: options.model,
|
|
412
|
+
schema: verdictsSchema,
|
|
413
|
+
instructions: "Determine whether each answer claim is supported by the supplied truths. Use yes for supported, no for contradicted or unsupported, and idk for genuinely ambiguous support. Preserve order and return one verdict per claim.",
|
|
414
|
+
prompt: jsonPrompt({ truths, claims }),
|
|
415
|
+
retries: config.retries
|
|
416
|
+
});
|
|
417
|
+
verdicts = verdictResult.data.verdicts;
|
|
418
|
+
assertSameLength("faithfulness verdicts", claims, verdicts);
|
|
419
|
+
usage = addUsage(usage, verdictResult.usage);
|
|
420
|
+
}
|
|
421
|
+
const penalizeAmbiguousClaims = options.penalizeAmbiguousClaims ?? false;
|
|
422
|
+
const supported = verdicts.filter(
|
|
423
|
+
(verdict) => verdict.verdict === "yes" || verdict.verdict === "idk" && !penalizeAmbiguousClaims
|
|
424
|
+
).length;
|
|
425
|
+
const score = verdicts.length === 0 ? 1 : supported / verdicts.length;
|
|
426
|
+
const reasonResult = await maybeReason({
|
|
427
|
+
model: options.model,
|
|
428
|
+
includeReason: config.includeReason,
|
|
429
|
+
retries: config.retries,
|
|
430
|
+
metric: "faithfulness",
|
|
431
|
+
score,
|
|
432
|
+
evidence: { verdicts, penalizeAmbiguousClaims }
|
|
433
|
+
});
|
|
434
|
+
usage = addUsage(usage, reasonResult.usage);
|
|
435
|
+
return higherOutcome({
|
|
436
|
+
score,
|
|
437
|
+
threshold: config.threshold,
|
|
438
|
+
strictMode: config.strictMode,
|
|
439
|
+
comment: reasonResult.reason,
|
|
440
|
+
details: { truths, claims, verdicts, penalizeAmbiguousClaims },
|
|
441
|
+
usage
|
|
442
|
+
});
|
|
443
|
+
} catch (error) {
|
|
444
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
function summarization(options) {
|
|
449
|
+
const config = metricConfig(options, "summarization");
|
|
450
|
+
const questionCount = validatePositiveInteger(options.questionCount ?? 5, "questionCount");
|
|
451
|
+
const truthsExtractionLimit = validateOptionalNonNegativeInteger(
|
|
452
|
+
options.truthsExtractionLimit,
|
|
453
|
+
"truthsExtractionLimit"
|
|
454
|
+
);
|
|
455
|
+
const suppliedQuestions = options.assessmentQuestions !== void 0 && options.assessmentQuestions.length > 0 ? [...options.assessmentQuestions] : void 0;
|
|
456
|
+
return numericMetric(config.name, async (args) => {
|
|
457
|
+
try {
|
|
458
|
+
const input = await resolveInput(options.input, args);
|
|
459
|
+
const actual = await resolveActualText(options.actual, args);
|
|
460
|
+
const questionPromise = suppliedQuestions === void 0 ? runJudge({
|
|
461
|
+
model: options.model,
|
|
462
|
+
schema: questionsSchema,
|
|
463
|
+
instructions: `Generate exactly ${questionCount} important yes-or-no assessment questions whose answers capture the source text's essential information.`,
|
|
464
|
+
prompt: `Source text:
|
|
465
|
+
${input}`,
|
|
466
|
+
retries: config.retries
|
|
467
|
+
}) : Promise.resolve({
|
|
468
|
+
data: { questions: suppliedQuestions },
|
|
469
|
+
usage: Usage.empty()
|
|
470
|
+
});
|
|
471
|
+
const [truthResult, claimResult, questionResult] = await Promise.all([
|
|
472
|
+
runJudge({
|
|
473
|
+
model: options.model,
|
|
474
|
+
schema: factsSchema,
|
|
475
|
+
instructions: truthsExtractionInstructions(truthsExtractionLimit),
|
|
476
|
+
prompt: `Source text:
|
|
477
|
+
${input}`,
|
|
478
|
+
retries: config.retries
|
|
479
|
+
}),
|
|
480
|
+
runJudge({
|
|
481
|
+
model: options.model,
|
|
482
|
+
schema: factsSchema,
|
|
483
|
+
instructions: "Extract every concise factual claim made by the summary. Return claims in the facts array.",
|
|
484
|
+
prompt: `Summary:
|
|
485
|
+
${actual}`,
|
|
486
|
+
retries: config.retries
|
|
487
|
+
}),
|
|
488
|
+
questionPromise
|
|
489
|
+
]);
|
|
490
|
+
const truths = limitValues(truthResult.data.facts, truthsExtractionLimit);
|
|
491
|
+
const claims = claimResult.data.facts;
|
|
492
|
+
const questions = questionResult.data.questions;
|
|
493
|
+
if (questions.length === 0) {
|
|
494
|
+
throw new Error("Summarization assessment questions must not be empty.");
|
|
495
|
+
}
|
|
496
|
+
const sourceAnswerPromise = runJudge({
|
|
497
|
+
model: options.model,
|
|
498
|
+
schema: answersSchema,
|
|
499
|
+
instructions: "Answer each assessment question using only the supplied text. Return one yes or no answer per question in the same order.",
|
|
500
|
+
prompt: jsonPrompt({ questions, text: input }),
|
|
501
|
+
retries: config.retries
|
|
502
|
+
});
|
|
503
|
+
const summaryAnswerPromise = runJudge({
|
|
504
|
+
model: options.model,
|
|
505
|
+
schema: answersSchema,
|
|
506
|
+
instructions: "Answer each assessment question using only the supplied text. Return one yes or no answer per question in the same order.",
|
|
507
|
+
prompt: jsonPrompt({ questions, text: actual }),
|
|
508
|
+
retries: config.retries
|
|
509
|
+
});
|
|
510
|
+
const alignmentPromise = claims.length === 0 ? Promise.resolve({ data: { verdicts: [] }, usage: Usage.empty() }) : runJudge({
|
|
511
|
+
model: options.model,
|
|
512
|
+
schema: verdictsSchema,
|
|
513
|
+
instructions: "Determine whether each summary claim is supported by the source truths. Use yes for supported, no for contradicted, and idk for unsupported filler or ambiguity. Preserve order.",
|
|
514
|
+
prompt: jsonPrompt({ truths, claims }),
|
|
515
|
+
retries: config.retries
|
|
516
|
+
});
|
|
517
|
+
const [sourceAnswerResult, summaryAnswerResult, alignmentResult] = await Promise.all([
|
|
518
|
+
sourceAnswerPromise,
|
|
519
|
+
summaryAnswerPromise,
|
|
520
|
+
alignmentPromise
|
|
521
|
+
]);
|
|
522
|
+
assertSameLength("source assessment answers", questions, sourceAnswerResult.data.answers);
|
|
523
|
+
assertSameLength("summary assessment answers", questions, summaryAnswerResult.data.answers);
|
|
524
|
+
assertSameLength("summarization alignment verdicts", claims, alignmentResult.data.verdicts);
|
|
525
|
+
const alignmentVerdicts = alignmentResult.data.verdicts;
|
|
526
|
+
const alignmentScore = alignmentVerdicts.length === 0 ? 0 : alignmentVerdicts.filter((verdict) => verdict.verdict === "yes").length / alignmentVerdicts.length;
|
|
527
|
+
let coverageTotal = 0;
|
|
528
|
+
let coverageMatched = 0;
|
|
529
|
+
const coverageVerdicts = questions.map((question, index) => {
|
|
530
|
+
const originalVerdict = sourceAnswerResult.data.answers[index];
|
|
531
|
+
const summaryVerdict = summaryAnswerResult.data.answers[index];
|
|
532
|
+
if (originalVerdict === "yes") {
|
|
533
|
+
coverageTotal += 1;
|
|
534
|
+
if (summaryVerdict === "yes") coverageMatched += 1;
|
|
535
|
+
}
|
|
536
|
+
return { question, originalVerdict, summaryVerdict };
|
|
537
|
+
});
|
|
538
|
+
const coverageScore = coverageTotal === 0 ? 0 : coverageMatched / coverageTotal;
|
|
539
|
+
const score = Math.min(alignmentScore, coverageScore);
|
|
540
|
+
const reasonResult = await maybeReason({
|
|
541
|
+
model: options.model,
|
|
542
|
+
includeReason: config.includeReason,
|
|
543
|
+
retries: config.retries,
|
|
544
|
+
metric: "summarization",
|
|
545
|
+
score,
|
|
546
|
+
evidence: { alignmentVerdicts, coverageVerdicts, alignmentScore, coverageScore }
|
|
547
|
+
});
|
|
548
|
+
const usage = addUsage(
|
|
549
|
+
truthResult.usage,
|
|
550
|
+
claimResult.usage,
|
|
551
|
+
questionResult.usage,
|
|
552
|
+
sourceAnswerResult.usage,
|
|
553
|
+
summaryAnswerResult.usage,
|
|
554
|
+
alignmentResult.usage,
|
|
555
|
+
reasonResult.usage
|
|
556
|
+
);
|
|
557
|
+
return higherOutcome({
|
|
558
|
+
score,
|
|
559
|
+
threshold: config.threshold,
|
|
560
|
+
strictMode: config.strictMode,
|
|
561
|
+
comment: reasonResult.reason,
|
|
562
|
+
details: {
|
|
563
|
+
truths,
|
|
564
|
+
claims,
|
|
565
|
+
assessmentQuestions: questions,
|
|
566
|
+
alignmentVerdicts,
|
|
567
|
+
coverageVerdicts,
|
|
568
|
+
scoreBreakdown: { alignment: alignmentScore, coverage: coverageScore }
|
|
569
|
+
},
|
|
570
|
+
usage
|
|
571
|
+
});
|
|
572
|
+
} catch (error) {
|
|
573
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
function gEval(options) {
|
|
578
|
+
if (options.name.trim().length === 0) throw new TypeError("gEval name must not be empty.");
|
|
579
|
+
if (options.evaluationParams.length === 0) {
|
|
580
|
+
throw new TypeError("gEval requires at least one evaluation parameter.");
|
|
581
|
+
}
|
|
582
|
+
if (options.criteria === void 0 === (options.evaluationSteps === void 0)) {
|
|
583
|
+
throw new TypeError("gEval requires exactly one of criteria or evaluationSteps.");
|
|
584
|
+
}
|
|
585
|
+
if (options.criteria !== void 0 && options.criteria.trim().length === 0) {
|
|
586
|
+
throw new TypeError("gEval criteria must not be empty.");
|
|
587
|
+
}
|
|
588
|
+
if (options.evaluationSteps !== void 0 && options.evaluationSteps.length === 0) {
|
|
589
|
+
throw new TypeError("gEval evaluationSteps must not be empty.");
|
|
590
|
+
}
|
|
591
|
+
const config = metricConfig(options, options.name);
|
|
592
|
+
const rubric = validateRubric(options.rubric);
|
|
593
|
+
const scoreRange = rubric.length === 0 ? [0, 10] : [rubric[0]?.scoreRange[0] ?? 0, rubric.at(-1)?.scoreRange[1] ?? 10];
|
|
594
|
+
let generatedStepsPromise;
|
|
595
|
+
let generatedUsageClaimed = false;
|
|
596
|
+
async function resolveSteps() {
|
|
597
|
+
if (options.evaluationSteps !== void 0) {
|
|
598
|
+
return { steps: options.evaluationSteps, usage: Usage.empty() };
|
|
599
|
+
}
|
|
600
|
+
if (generatedStepsPromise === void 0) {
|
|
601
|
+
generatedStepsPromise = runJudge({
|
|
602
|
+
model: options.model,
|
|
603
|
+
schema: z.object({ steps: z.array(z.string()) }),
|
|
604
|
+
instructions: "Generate three or four concise evaluation steps from the criteria. Explain how the selected parameters should be judged in relation to one another.",
|
|
605
|
+
prompt: jsonPrompt({ criteria: options.criteria, parameters: options.evaluationParams }),
|
|
606
|
+
retries: config.retries
|
|
607
|
+
}).catch((error) => {
|
|
608
|
+
generatedStepsPromise = void 0;
|
|
609
|
+
throw error;
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
const result = await generatedStepsPromise;
|
|
613
|
+
if (result.data.steps.length === 0) throw new Error("G-Eval generated no evaluation steps.");
|
|
614
|
+
const usage = generatedUsageClaimed ? Usage.empty() : result.usage;
|
|
615
|
+
generatedUsageClaimed = true;
|
|
616
|
+
return { steps: result.data.steps, usage };
|
|
617
|
+
}
|
|
618
|
+
return numericMetric(config.name, async (args) => {
|
|
619
|
+
try {
|
|
620
|
+
const parameters = await resolveGEvalParameters(options, args);
|
|
621
|
+
const stepsResult = await resolveSteps();
|
|
622
|
+
const scoreResult = await runJudge({
|
|
623
|
+
model: options.model,
|
|
624
|
+
schema: z.object({ score: z.number(), reason: z.string() }),
|
|
625
|
+
instructions: config.strictMode ? "Apply the evaluation steps and return score 1 only for complete compliance, otherwise 0. Give a concise evidence-based reason." : `Apply the evaluation steps and return an integer score from ${scoreRange[0]} through ${scoreRange[1]}, plus a concise evidence-based reason.`,
|
|
626
|
+
prompt: jsonPrompt({
|
|
627
|
+
evaluationSteps: stepsResult.steps,
|
|
628
|
+
rubric,
|
|
629
|
+
parameters
|
|
630
|
+
}),
|
|
631
|
+
retries: config.retries
|
|
632
|
+
});
|
|
633
|
+
const rawScore = scoreResult.data.score;
|
|
634
|
+
if (!Number.isFinite(rawScore) || config.strictMode && rawScore !== 0 && rawScore !== 1 || !config.strictMode && (rawScore < scoreRange[0] || rawScore > scoreRange[1])) {
|
|
635
|
+
throw new RangeError(`G-Eval score ${rawScore} is outside the requested range.`);
|
|
636
|
+
}
|
|
637
|
+
const score = config.strictMode ? rawScore : (rawScore - scoreRange[0]) / (scoreRange[1] - scoreRange[0]);
|
|
638
|
+
const usage = addUsage(stepsResult.usage, scoreResult.usage);
|
|
639
|
+
return higherOutcome({
|
|
640
|
+
score,
|
|
641
|
+
threshold: config.threshold,
|
|
642
|
+
strictMode: config.strictMode,
|
|
643
|
+
comment: config.includeReason ? scoreResult.data.reason : void 0,
|
|
644
|
+
details: {
|
|
645
|
+
evaluationSteps: stepsResult.steps,
|
|
646
|
+
evaluationParams: options.evaluationParams,
|
|
647
|
+
rawScore,
|
|
648
|
+
scoreRange: [scoreRange[0], scoreRange[1]],
|
|
649
|
+
rubric: rubric.map((entry) => ({
|
|
650
|
+
scoreRange: [entry.scoreRange[0], entry.scoreRange[1]],
|
|
651
|
+
expectedOutcome: entry.expectedOutcome
|
|
652
|
+
}))
|
|
653
|
+
},
|
|
654
|
+
usage
|
|
655
|
+
});
|
|
656
|
+
} catch (error) {
|
|
657
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
function turnRelevancy(options) {
|
|
662
|
+
const config = metricConfig(options, "turn_relevancy");
|
|
663
|
+
const windowSize = validatePositiveInteger(options.windowSize ?? 10, "windowSize");
|
|
664
|
+
const concurrency = validatePositiveInteger(options.concurrency ?? 4, "concurrency");
|
|
665
|
+
return numericMetric(config.name, async (args) => {
|
|
666
|
+
try {
|
|
667
|
+
const turns = await resolveTurns(options.turns, args);
|
|
668
|
+
const interactions = unitInteractions(turns);
|
|
669
|
+
const windows = interactions.map(
|
|
670
|
+
(_, index) => interactions.slice(Math.max(0, index - windowSize + 1), index + 1).flat()
|
|
671
|
+
);
|
|
672
|
+
const verdictResults = await mapWithConcurrency(
|
|
673
|
+
windows,
|
|
674
|
+
concurrency,
|
|
675
|
+
(window) => runJudge({
|
|
676
|
+
model: options.model,
|
|
677
|
+
schema: binaryVerdictSchema,
|
|
678
|
+
instructions: "Judge whether the final assistant reply is relevant to the preceding conversation. Return yes for relevant and no for irrelevant, with a concise reason.",
|
|
679
|
+
prompt: jsonPrompt({ turns: window }),
|
|
680
|
+
retries: config.retries
|
|
681
|
+
})
|
|
682
|
+
);
|
|
683
|
+
const verdicts = verdictResults.map((result, index) => ({
|
|
684
|
+
interaction: index + 1,
|
|
685
|
+
...result.data
|
|
686
|
+
}));
|
|
687
|
+
const score = verdicts.length === 0 ? 1 : verdicts.filter((verdict) => verdict.verdict === "yes").length / verdicts.length;
|
|
688
|
+
const reasonResult = await maybeReason({
|
|
689
|
+
model: options.model,
|
|
690
|
+
includeReason: config.includeReason,
|
|
691
|
+
retries: config.retries,
|
|
692
|
+
metric: "turn relevancy",
|
|
693
|
+
score,
|
|
694
|
+
evidence: { verdicts }
|
|
695
|
+
});
|
|
696
|
+
const usage = addUsage(...verdictResults.map((result) => result.usage), reasonResult.usage);
|
|
697
|
+
return higherOutcome({
|
|
698
|
+
score,
|
|
699
|
+
threshold: config.threshold,
|
|
700
|
+
strictMode: config.strictMode,
|
|
701
|
+
comment: reasonResult.reason,
|
|
702
|
+
details: { windowSize, concurrency, interactionCount: interactions.length, verdicts },
|
|
703
|
+
usage
|
|
704
|
+
});
|
|
705
|
+
} catch (error) {
|
|
706
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
function knowledgeRetention(options) {
|
|
711
|
+
const config = metricConfig(options, "knowledge_retention");
|
|
712
|
+
const concurrency = validatePositiveInteger(options.concurrency ?? 4, "concurrency");
|
|
713
|
+
return numericMetric(config.name, async (args) => {
|
|
714
|
+
try {
|
|
715
|
+
const turns = await resolveTurns(options.turns, args);
|
|
716
|
+
const userTurns = turns.map((turn, index) => ({ turn, index })).filter((entry) => entry.turn.role === "user");
|
|
717
|
+
const knowledgeResults = await mapWithConcurrency(
|
|
718
|
+
userTurns,
|
|
719
|
+
concurrency,
|
|
720
|
+
(entry) => runJudge({
|
|
721
|
+
model: options.model,
|
|
722
|
+
schema: factsSchema,
|
|
723
|
+
instructions: "Extract durable factual information newly supplied by the final user message. Use prior turns only to resolve references. Return concise facts; return an empty array when nothing new was supplied.",
|
|
724
|
+
prompt: jsonPrompt({
|
|
725
|
+
previousTurns: turns.slice(0, entry.index),
|
|
726
|
+
userMessage: entry.turn.content
|
|
727
|
+
}),
|
|
728
|
+
retries: config.retries
|
|
729
|
+
})
|
|
730
|
+
);
|
|
731
|
+
const knowledge = userTurns.map((entry, index) => ({
|
|
732
|
+
turnIndex: entry.index,
|
|
733
|
+
facts: knowledgeResults[index]?.data.facts ?? []
|
|
734
|
+
}));
|
|
735
|
+
const assistantChecks = turns.map((turn, index) => ({ turn, index })).filter((entry) => entry.turn.role === "assistant").map((entry) => ({
|
|
736
|
+
...entry,
|
|
737
|
+
facts: knowledge.filter((item) => item.turnIndex < entry.index).flatMap((item) => item.facts)
|
|
738
|
+
})).filter((entry) => entry.facts.length > 0);
|
|
739
|
+
const verdictResults = await mapWithConcurrency(
|
|
740
|
+
assistantChecks,
|
|
741
|
+
concurrency,
|
|
742
|
+
(entry) => runJudge({
|
|
743
|
+
model: options.model,
|
|
744
|
+
schema: z.object({ attrition: z.boolean(), reason: z.string() }),
|
|
745
|
+
instructions: "Determine whether the assistant reply forgets, contradicts, or unnecessarily asks again for information already supplied by the user. Set attrition true only when knowledge was lost.",
|
|
746
|
+
prompt: jsonPrompt({ knownFacts: entry.facts, assistantReply: entry.turn.content }),
|
|
747
|
+
retries: config.retries
|
|
748
|
+
})
|
|
749
|
+
);
|
|
750
|
+
const verdicts = assistantChecks.map((entry, index) => ({
|
|
751
|
+
turnIndex: entry.index,
|
|
752
|
+
attrition: verdictResults[index]?.data.attrition ?? true,
|
|
753
|
+
reason: verdictResults[index]?.data.reason ?? "Missing knowledge-retention verdict."
|
|
754
|
+
}));
|
|
755
|
+
const score = verdicts.length === 0 ? 1 : verdicts.filter((verdict) => !verdict.attrition).length / verdicts.length;
|
|
756
|
+
const reasonResult = await maybeReason({
|
|
757
|
+
model: options.model,
|
|
758
|
+
includeReason: config.includeReason,
|
|
759
|
+
retries: config.retries,
|
|
760
|
+
metric: "knowledge retention",
|
|
761
|
+
score,
|
|
762
|
+
evidence: { verdicts }
|
|
763
|
+
});
|
|
764
|
+
const usage = addUsage(
|
|
765
|
+
...knowledgeResults.map((result) => result.usage),
|
|
766
|
+
...verdictResults.map((result) => result.usage),
|
|
767
|
+
reasonResult.usage
|
|
768
|
+
);
|
|
769
|
+
return higherOutcome({
|
|
770
|
+
score,
|
|
771
|
+
threshold: config.threshold,
|
|
772
|
+
strictMode: config.strictMode,
|
|
773
|
+
comment: reasonResult.reason,
|
|
774
|
+
details: { concurrency, knowledge, verdicts },
|
|
775
|
+
usage
|
|
776
|
+
});
|
|
777
|
+
} catch (error) {
|
|
778
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
function numericMetric(name, evaluate) {
|
|
783
|
+
return { name, dataType: "NUMERIC", evaluate };
|
|
784
|
+
}
|
|
785
|
+
function metricConfig(options, defaultName) {
|
|
786
|
+
return {
|
|
787
|
+
name: options.name ?? defaultName,
|
|
788
|
+
threshold: validateThreshold(options.threshold ?? 0.5),
|
|
789
|
+
strictMode: options.strictMode ?? false,
|
|
790
|
+
includeReason: options.includeReason ?? true,
|
|
791
|
+
retries: validateRetries(options.retries ?? 0)
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
function higherOutcome(args) {
|
|
795
|
+
const score = args.strictMode ? args.score === 1 ? 1 : 0 : args.score;
|
|
796
|
+
const threshold = args.strictMode ? 1 : args.threshold;
|
|
797
|
+
const options = {
|
|
798
|
+
comment: args.comment,
|
|
799
|
+
metadata: evaluationMetadata(
|
|
800
|
+
{
|
|
801
|
+
...args.details,
|
|
802
|
+
scoreDirection: "higher_is_better",
|
|
803
|
+
threshold,
|
|
804
|
+
strictMode: args.strictMode
|
|
805
|
+
},
|
|
806
|
+
args.usage
|
|
807
|
+
)
|
|
808
|
+
};
|
|
809
|
+
return score >= threshold ? EvalOutcome.pass(score, options) : EvalOutcome.fail(score, options);
|
|
810
|
+
}
|
|
811
|
+
function lowerOutcome(args) {
|
|
812
|
+
const score = args.strictMode ? args.score === 0 ? 0 : 1 : args.score;
|
|
813
|
+
const threshold = args.strictMode ? 0 : args.threshold;
|
|
814
|
+
const options = {
|
|
815
|
+
comment: args.comment,
|
|
816
|
+
metadata: evaluationMetadata(
|
|
817
|
+
{
|
|
818
|
+
...args.details,
|
|
819
|
+
scoreDirection: "lower_is_better",
|
|
820
|
+
threshold,
|
|
821
|
+
strictMode: args.strictMode
|
|
822
|
+
},
|
|
823
|
+
args.usage
|
|
824
|
+
)
|
|
825
|
+
};
|
|
826
|
+
return score <= threshold ? EvalOutcome.pass(score, options) : EvalOutcome.fail(score, options);
|
|
827
|
+
}
|
|
828
|
+
async function maybeReason(args) {
|
|
829
|
+
if (!args.includeReason) return { usage: Usage.empty() };
|
|
830
|
+
const result = await runJudge({
|
|
831
|
+
model: args.model,
|
|
832
|
+
schema: reasonSchema,
|
|
833
|
+
instructions: `Write a concise final explanation for the ${args.metric} score. Ground it only in the supplied evidence and do not repeat the numeric score.`,
|
|
834
|
+
prompt: jsonPrompt({ score: args.score, evidence: args.evidence }),
|
|
835
|
+
retries: args.retries
|
|
836
|
+
});
|
|
837
|
+
return { reason: result.data.reason, usage: result.usage };
|
|
838
|
+
}
|
|
839
|
+
async function resolveInput(selector, args) {
|
|
840
|
+
return selector === void 0 ? formatValue(args.case.input) : selector(args);
|
|
841
|
+
}
|
|
842
|
+
async function resolveStringList(selectorOrValue, fallback, args, label) {
|
|
843
|
+
const value = selectorOrValue === void 0 ? fallback : typeof selectorOrValue === "function" ? await selectorOrValue(args) : selectorOrValue;
|
|
844
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((item) => typeof item !== "string")) {
|
|
845
|
+
throw new TypeError(`${label} must be a non-empty array of strings.`);
|
|
846
|
+
}
|
|
847
|
+
return value;
|
|
848
|
+
}
|
|
849
|
+
async function resolveGEvalParameters(options, args) {
|
|
850
|
+
const parameters = {};
|
|
851
|
+
for (const parameter of options.evaluationParams) {
|
|
852
|
+
if (parameter === "input") parameters.input = await resolveInput(options.input, args);
|
|
853
|
+
if (parameter === "actualOutput") {
|
|
854
|
+
parameters.actualOutput = await resolveActualText(options.actual, args);
|
|
855
|
+
}
|
|
856
|
+
if (parameter === "expectedOutput") {
|
|
857
|
+
const expected = options.expected === void 0 ? args.case.expected : await options.expected(args);
|
|
858
|
+
if (expected === void 0) throw new Error("G-Eval expectedOutput is missing.");
|
|
859
|
+
parameters.expectedOutput = toJsonValue(expected);
|
|
860
|
+
}
|
|
861
|
+
if (parameter === "context") {
|
|
862
|
+
parameters.context = await resolveStringList(
|
|
863
|
+
options.context,
|
|
864
|
+
args.case.context,
|
|
865
|
+
args,
|
|
866
|
+
"context"
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
if (parameter === "retrievalContext") {
|
|
870
|
+
parameters.retrievalContext = await resolveStringList(
|
|
871
|
+
options.retrievalContext,
|
|
872
|
+
args.case.retrievalContext,
|
|
873
|
+
args,
|
|
874
|
+
"retrievalContext"
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
if (parameter === "metadata") parameters.metadata = args.case.metadata ?? {};
|
|
878
|
+
}
|
|
879
|
+
return parameters;
|
|
880
|
+
}
|
|
881
|
+
function validateRubric(rubric) {
|
|
882
|
+
if (rubric === void 0 || rubric.length === 0) return [];
|
|
883
|
+
const sorted = [...rubric].sort((left, right) => left.scoreRange[0] - right.scoreRange[0]);
|
|
884
|
+
for (const [index, entry] of sorted.entries()) {
|
|
885
|
+
const [start, end] = entry.scoreRange;
|
|
886
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end > 10 || start > end) {
|
|
887
|
+
throw new RangeError(
|
|
888
|
+
"G-Eval rubric score ranges must be ordered integers from 0 through 10."
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
if (entry.expectedOutcome.trim().length === 0) {
|
|
892
|
+
throw new TypeError("G-Eval rubric expectedOutcome must not be empty.");
|
|
893
|
+
}
|
|
894
|
+
const next = sorted[index + 1];
|
|
895
|
+
if (next !== void 0 && end >= next.scoreRange[0]) {
|
|
896
|
+
throw new RangeError("G-Eval rubric score ranges must not overlap.");
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const first = sorted[0];
|
|
900
|
+
const last = sorted.at(-1);
|
|
901
|
+
if (first !== void 0 && last !== void 0 && first.scoreRange[0] === last.scoreRange[1]) {
|
|
902
|
+
throw new RangeError("G-Eval rubric score range must span more than one value.");
|
|
903
|
+
}
|
|
904
|
+
return sorted;
|
|
905
|
+
}
|
|
906
|
+
function truthsExtractionInstructions(limit) {
|
|
907
|
+
return limit === void 0 ? "Extract concise, factual, undisputed truths from the supplied source material, ordered by importance. Return them in the facts array." : `Extract at most ${limit} concise, factual, undisputed truths from the supplied source material, ordered by importance. Return them in the facts array.`;
|
|
908
|
+
}
|
|
909
|
+
function limitValues(values, limit) {
|
|
910
|
+
return limit === void 0 ? values : values.slice(0, limit);
|
|
911
|
+
}
|
|
912
|
+
function validateThreshold(value) {
|
|
913
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) {
|
|
914
|
+
throw new RangeError("Eval metric threshold must be between 0 and 1.");
|
|
915
|
+
}
|
|
916
|
+
return value;
|
|
917
|
+
}
|
|
918
|
+
function validateRetries(value) {
|
|
919
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
920
|
+
throw new RangeError("Eval metric retries must be a non-negative integer.");
|
|
921
|
+
}
|
|
922
|
+
return value;
|
|
923
|
+
}
|
|
924
|
+
function validatePositiveInteger(value, label) {
|
|
925
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
926
|
+
throw new RangeError(`${label} must be a positive integer.`);
|
|
927
|
+
}
|
|
928
|
+
return value;
|
|
929
|
+
}
|
|
930
|
+
function validateOptionalNonNegativeInteger(value, label) {
|
|
931
|
+
if (value === void 0) return void 0;
|
|
932
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
933
|
+
throw new RangeError(`${label} must be a non-negative integer.`);
|
|
934
|
+
}
|
|
935
|
+
return value;
|
|
936
|
+
}
|
|
937
|
+
function assertSameLength(label, inputs, outputs) {
|
|
938
|
+
if (inputs.length !== outputs.length) {
|
|
939
|
+
throw new Error(`${label} count ${outputs.length} did not match input count ${inputs.length}.`);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
function jsonPrompt(value) {
|
|
943
|
+
return JSON.stringify(value, null, 2);
|
|
944
|
+
}
|
|
945
|
+
function toJsonValue(value) {
|
|
946
|
+
try {
|
|
947
|
+
return JSON.parse(JSON.stringify(value));
|
|
948
|
+
} catch {
|
|
949
|
+
return String(value);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
function normalizeEvalTurns(value) {
|
|
953
|
+
const source = conversationArray(value);
|
|
954
|
+
if (source === void 0) return void 0;
|
|
955
|
+
const turns = [];
|
|
956
|
+
for (const entry of source) {
|
|
957
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
958
|
+
const role = entry.role;
|
|
959
|
+
if (role !== "user" && role !== "assistant") continue;
|
|
960
|
+
const content = entry.content;
|
|
961
|
+
const text = contentText(content);
|
|
962
|
+
if (text.length === 0) continue;
|
|
963
|
+
const metadata = entry.metadata;
|
|
964
|
+
turns.push(
|
|
965
|
+
typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? { role, content: text, metadata } : { role, content: text }
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
return turns.length === 0 ? void 0 : turns;
|
|
969
|
+
}
|
|
970
|
+
async function resolveTurns(selector, args) {
|
|
971
|
+
const source = selector === void 0 ? args.output : await selector(args);
|
|
972
|
+
const turns = normalizeEvalTurns(source);
|
|
973
|
+
if (turns === void 0) {
|
|
974
|
+
throw new TypeError(
|
|
975
|
+
"Conversational eval requires non-empty EvalTurn[], Message[], or an output with messages."
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
return turns;
|
|
979
|
+
}
|
|
980
|
+
function unitInteractions(turns) {
|
|
981
|
+
const interactions = [];
|
|
982
|
+
let current = [];
|
|
983
|
+
let hasUser = false;
|
|
984
|
+
for (const turn of turns) {
|
|
985
|
+
if (current.at(-1)?.role === "assistant" && turn.role === "user" && hasUser) {
|
|
986
|
+
interactions.push(current);
|
|
987
|
+
current = [turn];
|
|
988
|
+
hasUser = true;
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
current.push(turn);
|
|
992
|
+
if (turn.role === "user") hasUser = true;
|
|
993
|
+
}
|
|
994
|
+
if (current.length > 1 && current.at(-1)?.role === "assistant" && hasUser) {
|
|
995
|
+
interactions.push(current);
|
|
996
|
+
}
|
|
997
|
+
return interactions;
|
|
998
|
+
}
|
|
999
|
+
function conversationArray(value) {
|
|
1000
|
+
if (Array.isArray(value)) return value;
|
|
1001
|
+
if (typeof value === "object" && value !== null && "messages" in value) {
|
|
1002
|
+
const messages = value.messages;
|
|
1003
|
+
return Array.isArray(messages) ? messages : void 0;
|
|
1004
|
+
}
|
|
1005
|
+
return void 0;
|
|
1006
|
+
}
|
|
1007
|
+
function contentText(content) {
|
|
1008
|
+
if (typeof content === "string") return content;
|
|
1009
|
+
if (!Array.isArray(content)) return "";
|
|
1010
|
+
return content.flatMap(
|
|
1011
|
+
(part) => typeof part === "object" && part !== null && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string" ? [part.text] : []
|
|
1012
|
+
).join("\n");
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// src/evals/agent-target.ts
|
|
1016
|
+
function agentEvalTarget(agent, options = {}) {
|
|
1017
|
+
return async (input, testCase) => {
|
|
1018
|
+
const prompt = options.prompt?.(input, testCase) ?? String(input);
|
|
1019
|
+
const response = await agent.prompt(prompt).send();
|
|
1020
|
+
return options.output === void 0 ? response : options.output(response, testCase);
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// src/evals/metric.ts
|
|
1025
|
+
function defineMetric(metric) {
|
|
1026
|
+
return metric;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
148
1029
|
// src/evals/metrics.ts
|
|
1030
|
+
import { z as z2 } from "zod";
|
|
149
1031
|
function exactMatch(options = {}) {
|
|
150
1032
|
return {
|
|
151
1033
|
name: options.name ?? "exact_match",
|
|
@@ -224,9 +1106,9 @@ function llmScore(options) {
|
|
|
224
1106
|
const criteria = Array.isArray(options.criteria) ? options.criteria.join("\n") : options.criteria;
|
|
225
1107
|
const extractor = new ExtractorBuilder(
|
|
226
1108
|
options.model,
|
|
227
|
-
|
|
228
|
-
score:
|
|
229
|
-
feedback:
|
|
1109
|
+
z2.object({
|
|
1110
|
+
score: z2.number(),
|
|
1111
|
+
feedback: z2.string()
|
|
230
1112
|
})
|
|
231
1113
|
).instructions(
|
|
232
1114
|
options.instructions ?? `Score the eval case against these criteria:
|
|
@@ -252,23 +1134,174 @@ Return a score between 0 and 1 and brief feedback.`
|
|
|
252
1134
|
};
|
|
253
1135
|
}
|
|
254
1136
|
|
|
1137
|
+
// src/evals/reporting.ts
|
|
1138
|
+
function projectEvalOutcome(outcome, dataType) {
|
|
1139
|
+
const value = projectScoreValue(outcome, dataType);
|
|
1140
|
+
const projection = {
|
|
1141
|
+
outcome: outcome.outcome,
|
|
1142
|
+
value,
|
|
1143
|
+
label: typeof value === "string" ? value : outcome.outcome
|
|
1144
|
+
};
|
|
1145
|
+
if (typeof value === "number") projection.numericValue = value;
|
|
1146
|
+
if (typeof value === "string") projection.categoricalValue = value;
|
|
1147
|
+
const explanation = outcome.comment ?? (outcome.outcome === "invalid" ? outcome.reason : void 0);
|
|
1148
|
+
if (explanation !== void 0) projection.explanation = explanation;
|
|
1149
|
+
return projection;
|
|
1150
|
+
}
|
|
1151
|
+
function resolveEvalTraceRef(args) {
|
|
1152
|
+
return traceFromCarrier(args.output) ?? traceFromCarrier(args.input) ?? traceFromMetadata(args.metadata);
|
|
1153
|
+
}
|
|
1154
|
+
function defaultEvalTraceSelector(args) {
|
|
1155
|
+
return resolveEvalTraceRef({
|
|
1156
|
+
output: args.output,
|
|
1157
|
+
input: args.case.input,
|
|
1158
|
+
metadata: args.case.metadata
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
function projectScoreValue(outcome, dataType) {
|
|
1162
|
+
const score = outcome.score;
|
|
1163
|
+
if (dataType === "CATEGORICAL") {
|
|
1164
|
+
if (typeof score === "string") return score;
|
|
1165
|
+
if (typeof score === "number") return String(score);
|
|
1166
|
+
if (typeof score === "boolean") return score ? "true" : "false";
|
|
1167
|
+
if (score === null || score === void 0) return outcome.outcome;
|
|
1168
|
+
try {
|
|
1169
|
+
return JSON.stringify(score) ?? outcome.outcome;
|
|
1170
|
+
} catch {
|
|
1171
|
+
return outcome.outcome;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (dataType === "BOOLEAN") {
|
|
1175
|
+
if (typeof score === "boolean") return score ? 1 : 0;
|
|
1176
|
+
if (typeof score === "number") return score === 0 ? 0 : 1;
|
|
1177
|
+
return outcome.outcome === "pass" ? 1 : 0;
|
|
1178
|
+
}
|
|
1179
|
+
if (typeof score === "number") return score;
|
|
1180
|
+
if (typeof score === "boolean") return score ? 1 : 0;
|
|
1181
|
+
if (typeof score === "object" && score !== null && "score" in score && typeof score.score === "number") {
|
|
1182
|
+
return score.score;
|
|
1183
|
+
}
|
|
1184
|
+
return outcome.outcome === "pass" ? 1 : 0;
|
|
1185
|
+
}
|
|
1186
|
+
function traceFromCarrier(value) {
|
|
1187
|
+
if (typeof value !== "object" || value === null || !("trace" in value)) return void 0;
|
|
1188
|
+
return readTraceRef(value.trace);
|
|
1189
|
+
}
|
|
1190
|
+
function traceFromMetadata(metadata) {
|
|
1191
|
+
if (metadata === void 0) return void 0;
|
|
1192
|
+
return readTraceRef({
|
|
1193
|
+
traceId: metadata.traceId,
|
|
1194
|
+
observationId: metadata.observationId,
|
|
1195
|
+
responseId: metadata.responseId
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
function readTraceRef(value) {
|
|
1199
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
1200
|
+
const traceId = value.traceId;
|
|
1201
|
+
if (typeof traceId !== "string" || traceId.length === 0) return void 0;
|
|
1202
|
+
const observationId = value.observationId;
|
|
1203
|
+
const responseId = value.responseId;
|
|
1204
|
+
const trace = { traceId };
|
|
1205
|
+
if (typeof observationId === "string" && observationId.length > 0) {
|
|
1206
|
+
trace.observationId = observationId;
|
|
1207
|
+
}
|
|
1208
|
+
if (typeof responseId === "string" && responseId.length > 0) trace.responseId = responseId;
|
|
1209
|
+
return trace;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
255
1212
|
// src/evals/runner.ts
|
|
256
1213
|
async function runEvalSuite(options) {
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
1214
|
+
const startedAtMs = Date.now();
|
|
1215
|
+
const run = resolveRun(options, startedAtMs);
|
|
1216
|
+
const reporters = options.reporters ?? [];
|
|
1217
|
+
const lifecycle = {
|
|
1218
|
+
run,
|
|
1219
|
+
suiteName: options.name,
|
|
1220
|
+
caseCount: options.cases.length,
|
|
1221
|
+
metricNames: options.metrics.map((metric) => metric.name)
|
|
1222
|
+
};
|
|
1223
|
+
let reporterErrors;
|
|
1224
|
+
try {
|
|
1225
|
+
reporterErrors = await notifyRunStart(
|
|
1226
|
+
reporters,
|
|
1227
|
+
lifecycle,
|
|
1228
|
+
options.failOnReporterError === true
|
|
1229
|
+
);
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
await notifyRunEnd(reporters, {
|
|
1232
|
+
...lifecycle,
|
|
1233
|
+
status: "failed",
|
|
1234
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1235
|
+
durationMs: Date.now() - startedAtMs,
|
|
1236
|
+
error
|
|
1237
|
+
});
|
|
1238
|
+
throw error;
|
|
1239
|
+
}
|
|
1240
|
+
let results;
|
|
1241
|
+
try {
|
|
1242
|
+
results = await runEvalCases(options, run);
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
await notifyRunEnd(reporters, {
|
|
1245
|
+
...lifecycle,
|
|
1246
|
+
status: "failed",
|
|
1247
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1248
|
+
durationMs: Date.now() - startedAtMs,
|
|
1249
|
+
error
|
|
1250
|
+
});
|
|
1251
|
+
throw error;
|
|
1252
|
+
}
|
|
263
1253
|
const counts = countOutcomes(results);
|
|
264
|
-
|
|
1254
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1255
|
+
const result = {
|
|
265
1256
|
name: options.name,
|
|
1257
|
+
run: { ...run, completedAt },
|
|
266
1258
|
results,
|
|
267
1259
|
...counts,
|
|
268
|
-
durationMs: Date.now() -
|
|
1260
|
+
durationMs: Date.now() - startedAtMs,
|
|
1261
|
+
reporterErrors
|
|
269
1262
|
};
|
|
1263
|
+
result.reporterErrors.push(
|
|
1264
|
+
...await notifyRunEnd(
|
|
1265
|
+
reporters,
|
|
1266
|
+
{
|
|
1267
|
+
...lifecycle,
|
|
1268
|
+
status: "completed",
|
|
1269
|
+
completedAt,
|
|
1270
|
+
durationMs: result.durationMs,
|
|
1271
|
+
...counts
|
|
1272
|
+
},
|
|
1273
|
+
options.failOnReporterError === true
|
|
1274
|
+
)
|
|
1275
|
+
);
|
|
1276
|
+
return result;
|
|
270
1277
|
}
|
|
271
|
-
async function
|
|
1278
|
+
async function runEvalCases(options, run) {
|
|
1279
|
+
const concurrency = Math.max(1, Math.trunc(options.concurrency ?? 1));
|
|
1280
|
+
const results = new Array(options.cases.length);
|
|
1281
|
+
let nextIndex = 0;
|
|
1282
|
+
let failure;
|
|
1283
|
+
async function worker() {
|
|
1284
|
+
while (failure === void 0 && nextIndex < options.cases.length) {
|
|
1285
|
+
const index = nextIndex;
|
|
1286
|
+
nextIndex += 1;
|
|
1287
|
+
try {
|
|
1288
|
+
results[index] = await runEvalCase(
|
|
1289
|
+
options,
|
|
1290
|
+
options.cases[index],
|
|
1291
|
+
run
|
|
1292
|
+
);
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
failure ??= { error };
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
await Promise.all(
|
|
1299
|
+
Array.from({ length: Math.min(concurrency, options.cases.length) }, () => worker())
|
|
1300
|
+
);
|
|
1301
|
+
if (failure !== void 0) throw failure.error;
|
|
1302
|
+
return results;
|
|
1303
|
+
}
|
|
1304
|
+
async function runEvalCase(options, testCase, run) {
|
|
272
1305
|
let output;
|
|
273
1306
|
let targetError;
|
|
274
1307
|
try {
|
|
@@ -276,16 +1309,20 @@ async function runEvalCase(options, testCase) {
|
|
|
276
1309
|
} catch (error) {
|
|
277
1310
|
targetError = error;
|
|
278
1311
|
}
|
|
1312
|
+
const traceResult = await resolveTrace(options, testCase, output, targetError);
|
|
279
1313
|
const metrics = [];
|
|
280
1314
|
for (const metric of options.metrics) {
|
|
281
1315
|
const outcome = targetError === void 0 ? await safeEvaluate(options.name, testCase, output, metric) : EvalOutcome.invalid(`Target failed: ${errorMessage(targetError)}`);
|
|
282
1316
|
const reporterErrors = await reportOutcome({
|
|
1317
|
+
run,
|
|
283
1318
|
suiteName: options.name,
|
|
284
1319
|
testCase,
|
|
285
1320
|
output,
|
|
286
1321
|
targetError,
|
|
287
1322
|
metric,
|
|
288
1323
|
outcome,
|
|
1324
|
+
trace: traceResult.trace,
|
|
1325
|
+
traceError: traceResult.error,
|
|
289
1326
|
reporters: options.reporters ?? [],
|
|
290
1327
|
failOnReporterError: options.failOnReporterError === true
|
|
291
1328
|
});
|
|
@@ -303,6 +1340,20 @@ async function runEvalCase(options, testCase) {
|
|
|
303
1340
|
}
|
|
304
1341
|
return result;
|
|
305
1342
|
}
|
|
1343
|
+
async function resolveTrace(options, testCase, output, targetError) {
|
|
1344
|
+
try {
|
|
1345
|
+
const selector = options.trace ?? defaultEvalTraceSelector;
|
|
1346
|
+
const trace = await selector({
|
|
1347
|
+
suiteName: options.name,
|
|
1348
|
+
case: testCase,
|
|
1349
|
+
output,
|
|
1350
|
+
targetError
|
|
1351
|
+
});
|
|
1352
|
+
return trace === void 0 ? {} : { trace };
|
|
1353
|
+
} catch (error) {
|
|
1354
|
+
return { error };
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
306
1357
|
async function safeEvaluate(suiteName, testCase, output, metric) {
|
|
307
1358
|
try {
|
|
308
1359
|
return await metric.evaluate({ suiteName, case: testCase, output });
|
|
@@ -312,13 +1363,20 @@ async function safeEvaluate(suiteName, testCase, output, metric) {
|
|
|
312
1363
|
}
|
|
313
1364
|
async function reportOutcome(args) {
|
|
314
1365
|
const errors = [];
|
|
1366
|
+
if (args.traceError !== void 0) {
|
|
1367
|
+
if (args.failOnReporterError) throw args.traceError;
|
|
1368
|
+
errors.push(args.traceError);
|
|
1369
|
+
return errors;
|
|
1370
|
+
}
|
|
315
1371
|
for (const reporter of args.reporters) {
|
|
316
1372
|
try {
|
|
317
1373
|
await reporter.report({
|
|
1374
|
+
run: args.run,
|
|
318
1375
|
suiteName: args.suiteName,
|
|
319
1376
|
case: args.testCase,
|
|
320
1377
|
output: args.output,
|
|
321
1378
|
targetError: args.targetError,
|
|
1379
|
+
trace: args.trace,
|
|
322
1380
|
metric: args.metric,
|
|
323
1381
|
outcome: args.outcome
|
|
324
1382
|
});
|
|
@@ -331,6 +1389,53 @@ async function reportOutcome(args) {
|
|
|
331
1389
|
}
|
|
332
1390
|
return errors;
|
|
333
1391
|
}
|
|
1392
|
+
function resolveRun(options, startedAtMs) {
|
|
1393
|
+
const id = options.run?.id ?? globalThis.crypto.randomUUID();
|
|
1394
|
+
if (id.trim().length === 0 || id.length > 128) {
|
|
1395
|
+
throw new TypeError("Evaluation run id must contain 1 to 128 characters");
|
|
1396
|
+
}
|
|
1397
|
+
for (const [label, value] of [
|
|
1398
|
+
["dataset name", options.run?.datasetName],
|
|
1399
|
+
["dataset version", options.run?.datasetVersion]
|
|
1400
|
+
]) {
|
|
1401
|
+
if (value !== void 0 && (value.trim().length === 0 || value.length > 256)) {
|
|
1402
|
+
throw new TypeError(`Evaluation run ${label} must contain 1 to 256 characters`);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return {
|
|
1406
|
+
id,
|
|
1407
|
+
startedAt: new Date(startedAtMs).toISOString(),
|
|
1408
|
+
...options.run?.datasetName === void 0 ? {} : { datasetName: options.run.datasetName },
|
|
1409
|
+
...options.run?.datasetVersion === void 0 ? {} : { datasetVersion: options.run.datasetVersion },
|
|
1410
|
+
...options.run?.metadata === void 0 ? {} : { metadata: options.run.metadata }
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
async function notifyRunStart(reporters, args, failOnReporterError) {
|
|
1414
|
+
const errors = [];
|
|
1415
|
+
for (const reporter of reporters) {
|
|
1416
|
+
if (reporter.onRunStart === void 0) continue;
|
|
1417
|
+
try {
|
|
1418
|
+
await reporter.onRunStart(args);
|
|
1419
|
+
} catch (error) {
|
|
1420
|
+
if (failOnReporterError) throw error;
|
|
1421
|
+
errors.push(error);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return errors;
|
|
1425
|
+
}
|
|
1426
|
+
async function notifyRunEnd(reporters, args, failOnReporterError = false) {
|
|
1427
|
+
const errors = [];
|
|
1428
|
+
for (const reporter of reporters) {
|
|
1429
|
+
if (reporter.onRunEnd === void 0) continue;
|
|
1430
|
+
try {
|
|
1431
|
+
await reporter.onRunEnd(args);
|
|
1432
|
+
} catch (error) {
|
|
1433
|
+
if (failOnReporterError) throw error;
|
|
1434
|
+
errors.push(error);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return errors;
|
|
1438
|
+
}
|
|
334
1439
|
function countOutcomes(results) {
|
|
335
1440
|
let passed = 0;
|
|
336
1441
|
let failed = 0;
|
|
@@ -347,12 +1452,24 @@ function countOutcomes(results) {
|
|
|
347
1452
|
export {
|
|
348
1453
|
EvalOutcome,
|
|
349
1454
|
agentEvalTarget,
|
|
1455
|
+
answerRelevancy,
|
|
350
1456
|
contains,
|
|
1457
|
+
defaultEvalTraceSelector,
|
|
351
1458
|
defineMetric,
|
|
352
1459
|
exactMatch,
|
|
1460
|
+
faithfulness,
|
|
1461
|
+
gEval,
|
|
1462
|
+
hallucination,
|
|
1463
|
+
jsonCorrectness,
|
|
1464
|
+
knowledgeRetention,
|
|
353
1465
|
llmJudge,
|
|
354
1466
|
llmScore,
|
|
1467
|
+
projectEvalOutcome,
|
|
1468
|
+
promptAlignment,
|
|
1469
|
+
resolveEvalTraceRef,
|
|
355
1470
|
runEvalSuite,
|
|
356
|
-
semanticSimilarity
|
|
1471
|
+
semanticSimilarity,
|
|
1472
|
+
summarization,
|
|
1473
|
+
turnRelevancy
|
|
357
1474
|
};
|
|
358
1475
|
//# sourceMappingURL=index.js.map
|