@anvia/core 0.24.0 → 0.25.1
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-HYUYZEAJ.js → chunk-2NDQHV7V.js} +5 -5
- package/dist/{chunk-MELYDKWQ.js → chunk-4BGN6PYF.js} +4 -4
- package/dist/{chunk-JI5YZWNT.js → chunk-4HWN7734.js} +3 -3
- package/dist/{chunk-NCPN4SJR.js → chunk-BCA4VZ5W.js} +2 -2
- package/dist/{chunk-YLMDBQO4.js → chunk-KYEBWN3E.js} +4 -4
- package/dist/{chunk-BALBBVI3.js → chunk-VXONXP2G.js} +5 -5
- package/dist/{chunk-DALYP4PX.js → chunk-YEBIVZM2.js} +4 -4
- package/dist/evals/index.d.ts +364 -45
- package/dist/evals/index.js +838 -193
- 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 +3 -3
- /package/dist/{chunk-HYUYZEAJ.js.map → chunk-2NDQHV7V.js.map} +0 -0
- /package/dist/{chunk-MELYDKWQ.js.map → chunk-4BGN6PYF.js.map} +0 -0
- /package/dist/{chunk-JI5YZWNT.js.map → chunk-4HWN7734.js.map} +0 -0
- /package/dist/{chunk-NCPN4SJR.js.map → chunk-BCA4VZ5W.js.map} +0 -0
- /package/dist/{chunk-YLMDBQO4.js.map → chunk-KYEBWN3E.js.map} +0 -0
- /package/dist/{chunk-BALBBVI3.js.map → chunk-VXONXP2G.js.map} +0 -0
- /package/dist/{chunk-DALYP4PX.js.map → chunk-YEBIVZM2.js.map} +0 -0
package/dist/evals/index.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ExtractorBuilder
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-KYEBWN3E.js";
|
|
4
|
+
import "../chunk-4HWN7734.js";
|
|
5
|
+
import "../chunk-YEBIVZM2.js";
|
|
6
|
+
import "../chunk-VXONXP2G.js";
|
|
7
7
|
import "../chunk-YK4WAAS4.js";
|
|
8
8
|
import "../chunk-XUUY2L2D.js";
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-2NDQHV7V.js";
|
|
10
|
+
import "../chunk-4BGN6PYF.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 {
|
|
17
|
+
Usage
|
|
18
|
+
} from "../chunk-ADH7NNCS.js";
|
|
19
|
+
import "../chunk-WQKHFADH.js";
|
|
14
20
|
import {
|
|
15
21
|
cosineSimilarity,
|
|
16
22
|
embedText
|
|
@@ -18,12 +24,6 @@ import {
|
|
|
18
24
|
import {
|
|
19
25
|
mapWithConcurrency
|
|
20
26
|
} 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";
|
|
27
27
|
import "../chunk-CWUJUSOS.js";
|
|
28
28
|
|
|
29
29
|
// src/evals/advanced-metrics.ts
|
|
@@ -100,6 +100,9 @@ var EvalOutcome = {
|
|
|
100
100
|
if (options.metadata !== void 0) {
|
|
101
101
|
outcome.metadata = options.metadata;
|
|
102
102
|
}
|
|
103
|
+
if (options.usage !== void 0) {
|
|
104
|
+
outcome.usage = options.usage;
|
|
105
|
+
}
|
|
103
106
|
return outcome;
|
|
104
107
|
},
|
|
105
108
|
fail(score, options = {}) {
|
|
@@ -115,6 +118,9 @@ var EvalOutcome = {
|
|
|
115
118
|
if (options.metadata !== void 0) {
|
|
116
119
|
outcome.metadata = options.metadata;
|
|
117
120
|
}
|
|
121
|
+
if (options.usage !== void 0) {
|
|
122
|
+
outcome.usage = options.usage;
|
|
123
|
+
}
|
|
118
124
|
return outcome;
|
|
119
125
|
},
|
|
120
126
|
invalid(reason, options = {}) {
|
|
@@ -131,11 +137,20 @@ var EvalOutcome = {
|
|
|
131
137
|
if (options.metadata !== void 0) {
|
|
132
138
|
outcome.metadata = options.metadata;
|
|
133
139
|
}
|
|
140
|
+
if (options.usage !== void 0) {
|
|
141
|
+
outcome.usage = options.usage;
|
|
142
|
+
}
|
|
134
143
|
return outcome;
|
|
135
144
|
}
|
|
136
145
|
};
|
|
137
146
|
|
|
138
147
|
// src/evals/selectors.ts
|
|
148
|
+
function selectPromptOutput(args) {
|
|
149
|
+
if (typeof args.output !== "object" || args.output === null || !("output" in args.output) || typeof args.output.output !== "string") {
|
|
150
|
+
throw new TypeError("selectPromptOutput requires an output object with a string output field.");
|
|
151
|
+
}
|
|
152
|
+
return args.output.output;
|
|
153
|
+
}
|
|
139
154
|
async function resolveActual(selector, args) {
|
|
140
155
|
return selector === void 0 ? defaultOutputValue(args.output) : selector(args);
|
|
141
156
|
}
|
|
@@ -188,9 +203,14 @@ var binaryVerdictSchema = z.object({
|
|
|
188
203
|
reason: z.string()
|
|
189
204
|
});
|
|
190
205
|
var reasonSchema = z.object({ reason: z.string() });
|
|
206
|
+
var abstentionJudgmentSchema = z.object({
|
|
207
|
+
behavior: z.enum(["abstention", "confident_answer"]),
|
|
208
|
+
grounded: z.boolean(),
|
|
209
|
+
reason: z.string()
|
|
210
|
+
});
|
|
191
211
|
function answerRelevancy(options) {
|
|
192
212
|
const config = metricConfig(options, "answer_relevancy");
|
|
193
|
-
return numericMetric(config.name, async (args) => {
|
|
213
|
+
return numericMetric(config.name, config, "higher_is_better", async (args) => {
|
|
194
214
|
try {
|
|
195
215
|
const input = await resolveInput(options.input, args);
|
|
196
216
|
const actual = await resolveActualText(options.actual, args);
|
|
@@ -245,7 +265,7 @@ function promptAlignment(options) {
|
|
|
245
265
|
throw new TypeError("promptAlignment requires at least one prompt instruction.");
|
|
246
266
|
}
|
|
247
267
|
const config = metricConfig(options, "prompt_alignment");
|
|
248
|
-
return numericMetric(config.name, async (args) => {
|
|
268
|
+
return numericMetric(config.name, config, "higher_is_better", async (args) => {
|
|
249
269
|
try {
|
|
250
270
|
const input = await resolveInput(options.input, args);
|
|
251
271
|
const actual = await resolveActualText(options.actual, args);
|
|
@@ -285,56 +305,65 @@ function jsonCorrectness(options) {
|
|
|
285
305
|
const threshold = validateThreshold(options.threshold ?? 0.5);
|
|
286
306
|
const retries = validateRetries(options.retries ?? 0);
|
|
287
307
|
const includeReason = options.includeReason ?? true;
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
308
|
+
const strictMode = options.strictMode ?? true;
|
|
309
|
+
return numericMetric(
|
|
310
|
+
options.name ?? "json_correctness",
|
|
311
|
+
{
|
|
312
|
+
threshold: strictMode ? 1 : threshold,
|
|
313
|
+
required: options.required ?? true
|
|
314
|
+
},
|
|
315
|
+
"higher_is_better",
|
|
316
|
+
async (args) => {
|
|
293
317
|
try {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
318
|
+
const actual = await resolveActualText(options.actual, args);
|
|
319
|
+
let parsed;
|
|
320
|
+
let validationError;
|
|
321
|
+
try {
|
|
322
|
+
parsed = JSON.parse(actual);
|
|
323
|
+
const result = options.schema.safeParse(parsed);
|
|
324
|
+
if (!result.success) {
|
|
325
|
+
validationError = z.prettifyError(result.error);
|
|
326
|
+
}
|
|
327
|
+
} catch (error) {
|
|
328
|
+
validationError = errorMessage(error);
|
|
298
329
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
comment = reasonResult.data.reason;
|
|
319
|
-
usage = reasonResult.usage;
|
|
330
|
+
const score = validationError === void 0 ? 1 : 0;
|
|
331
|
+
let comment;
|
|
332
|
+
let usage = Usage.empty();
|
|
333
|
+
if (includeReason) {
|
|
334
|
+
if (score === 1) {
|
|
335
|
+
comment = "The generated JSON is syntactically valid and matches the expected schema.";
|
|
336
|
+
} else if (options.model === void 0) {
|
|
337
|
+
comment = validationError;
|
|
338
|
+
} else {
|
|
339
|
+
const reasonResult = await runJudge({
|
|
340
|
+
model: options.model,
|
|
341
|
+
schema: reasonSchema,
|
|
342
|
+
instructions: "Briefly explain why the generated JSON does not match the expected schema. Focus on actionable syntax, field, and type problems.",
|
|
343
|
+
prompt: jsonPrompt({ actual, validationError }),
|
|
344
|
+
retries
|
|
345
|
+
});
|
|
346
|
+
comment = reasonResult.data.reason;
|
|
347
|
+
usage = reasonResult.usage;
|
|
348
|
+
}
|
|
320
349
|
}
|
|
350
|
+
return higherOutcome({
|
|
351
|
+
score,
|
|
352
|
+
threshold,
|
|
353
|
+
strictMode,
|
|
354
|
+
comment,
|
|
355
|
+
details: validationError === void 0 ? {} : { validationError },
|
|
356
|
+
usage
|
|
357
|
+
});
|
|
358
|
+
} catch (error) {
|
|
359
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
321
360
|
}
|
|
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
361
|
}
|
|
333
|
-
|
|
362
|
+
);
|
|
334
363
|
}
|
|
335
364
|
function hallucination(options) {
|
|
336
365
|
const config = metricConfig(options, "hallucination");
|
|
337
|
-
return numericMetric(config.name, async (args) => {
|
|
366
|
+
return numericMetric(config.name, config, "lower_is_better", async (args) => {
|
|
338
367
|
try {
|
|
339
368
|
const actual = await resolveActualText(options.actual, args);
|
|
340
369
|
const context = await resolveStringList(options.context, args.case.context, args, "context");
|
|
@@ -376,7 +405,7 @@ function faithfulness(options) {
|
|
|
376
405
|
options.truthsExtractionLimit,
|
|
377
406
|
"truthsExtractionLimit"
|
|
378
407
|
);
|
|
379
|
-
return numericMetric(config.name, async (args) => {
|
|
408
|
+
return numericMetric(config.name, config, "higher_is_better", async (args) => {
|
|
380
409
|
try {
|
|
381
410
|
const actual = await resolveActualText(options.actual, args);
|
|
382
411
|
const retrievalContext = await resolveStringList(
|
|
@@ -445,6 +474,70 @@ ${actual}`,
|
|
|
445
474
|
}
|
|
446
475
|
});
|
|
447
476
|
}
|
|
477
|
+
function abstention(options) {
|
|
478
|
+
const retries = validateRetries(options.retries ?? 0);
|
|
479
|
+
return {
|
|
480
|
+
name: options.name ?? "abstention",
|
|
481
|
+
required: options.required ?? true,
|
|
482
|
+
dataType: "CATEGORICAL",
|
|
483
|
+
async evaluate(args) {
|
|
484
|
+
try {
|
|
485
|
+
const actual = await resolveActualText(options.actual, args);
|
|
486
|
+
const shouldAbstain = await resolveExpected(options.shouldAbstain, args);
|
|
487
|
+
if (typeof shouldAbstain !== "boolean") {
|
|
488
|
+
return EvalOutcome.invalid("abstention shouldAbstain must resolve to a boolean.");
|
|
489
|
+
}
|
|
490
|
+
const context = await resolveAbstentionContext(options.context, args);
|
|
491
|
+
if (!shouldAbstain && context.length === 0) {
|
|
492
|
+
return EvalOutcome.invalid(
|
|
493
|
+
"abstention context must be non-empty when shouldAbstain is false."
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
const judgment = await runJudge({
|
|
497
|
+
model: options.model,
|
|
498
|
+
schema: abstentionJudgmentSchema,
|
|
499
|
+
instructions: "Classify whether the answer abstains or gives a confident answer. For a confident answer, grounded is true only when every substantive factual claim is supported by the supplied context. For an abstention, set grounded to false. Return a concise evidence-based reason.",
|
|
500
|
+
prompt: jsonPrompt({ actual, context }),
|
|
501
|
+
retries
|
|
502
|
+
});
|
|
503
|
+
const category = abstentionCategory(
|
|
504
|
+
shouldAbstain,
|
|
505
|
+
judgment.data.behavior,
|
|
506
|
+
judgment.data.grounded
|
|
507
|
+
);
|
|
508
|
+
const outcomeOptions = {
|
|
509
|
+
comment: options.includeReason === false ? void 0 : judgment.data.reason,
|
|
510
|
+
metadata: evaluationMetadata(
|
|
511
|
+
{
|
|
512
|
+
behavior: judgment.data.behavior,
|
|
513
|
+
grounded: judgment.data.grounded,
|
|
514
|
+
shouldAbstain
|
|
515
|
+
},
|
|
516
|
+
judgment.usage
|
|
517
|
+
),
|
|
518
|
+
usage: judgment.usage
|
|
519
|
+
};
|
|
520
|
+
return category === "correct_abstention" || category === "correct_grounded_answer" ? EvalOutcome.pass(category, outcomeOptions) : EvalOutcome.fail(category, outcomeOptions);
|
|
521
|
+
} catch (error) {
|
|
522
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
async function resolveAbstentionContext(selectorOrValue, args) {
|
|
528
|
+
const context = selectorOrValue === void 0 ? args.case.retrievalContext ?? [] : typeof selectorOrValue === "function" ? await selectorOrValue(args) : selectorOrValue;
|
|
529
|
+
if (!Array.isArray(context) || context.some((value) => typeof value !== "string")) {
|
|
530
|
+
throw new TypeError("abstention context must be an array of strings.");
|
|
531
|
+
}
|
|
532
|
+
return context;
|
|
533
|
+
}
|
|
534
|
+
function abstentionCategory(shouldAbstain, behavior, grounded) {
|
|
535
|
+
if (behavior === "abstention") {
|
|
536
|
+
return shouldAbstain ? "correct_abstention" : "unnecessary_abstention";
|
|
537
|
+
}
|
|
538
|
+
if (shouldAbstain || !grounded) return "unsupported_confident_answer";
|
|
539
|
+
return "correct_grounded_answer";
|
|
540
|
+
}
|
|
448
541
|
function summarization(options) {
|
|
449
542
|
const config = metricConfig(options, "summarization");
|
|
450
543
|
const questionCount = validatePositiveInteger(options.questionCount ?? 5, "questionCount");
|
|
@@ -453,7 +546,7 @@ function summarization(options) {
|
|
|
453
546
|
"truthsExtractionLimit"
|
|
454
547
|
);
|
|
455
548
|
const suppliedQuestions = options.assessmentQuestions !== void 0 && options.assessmentQuestions.length > 0 ? [...options.assessmentQuestions] : void 0;
|
|
456
|
-
return numericMetric(config.name, async (args) => {
|
|
549
|
+
return numericMetric(config.name, config, "higher_is_better", async (args) => {
|
|
457
550
|
try {
|
|
458
551
|
const input = await resolveInput(options.input, args);
|
|
459
552
|
const actual = await resolveActualText(options.actual, args);
|
|
@@ -615,7 +708,7 @@ function gEval(options) {
|
|
|
615
708
|
generatedUsageClaimed = true;
|
|
616
709
|
return { steps: result.data.steps, usage };
|
|
617
710
|
}
|
|
618
|
-
return numericMetric(config.name, async (args) => {
|
|
711
|
+
return numericMetric(config.name, config, "higher_is_better", async (args) => {
|
|
619
712
|
try {
|
|
620
713
|
const parameters = await resolveGEvalParameters(options, args);
|
|
621
714
|
const stepsResult = await resolveSteps();
|
|
@@ -662,7 +755,7 @@ function turnRelevancy(options) {
|
|
|
662
755
|
const config = metricConfig(options, "turn_relevancy");
|
|
663
756
|
const windowSize = validatePositiveInteger(options.windowSize ?? 10, "windowSize");
|
|
664
757
|
const concurrency = validatePositiveInteger(options.concurrency ?? 4, "concurrency");
|
|
665
|
-
return numericMetric(config.name, async (args) => {
|
|
758
|
+
return numericMetric(config.name, config, "higher_is_better", async (args) => {
|
|
666
759
|
try {
|
|
667
760
|
const turns = await resolveTurns(options.turns, args);
|
|
668
761
|
const interactions = unitInteractions(turns);
|
|
@@ -710,7 +803,7 @@ function turnRelevancy(options) {
|
|
|
710
803
|
function knowledgeRetention(options) {
|
|
711
804
|
const config = metricConfig(options, "knowledge_retention");
|
|
712
805
|
const concurrency = validatePositiveInteger(options.concurrency ?? 4, "concurrency");
|
|
713
|
-
return numericMetric(config.name, async (args) => {
|
|
806
|
+
return numericMetric(config.name, config, "higher_is_better", async (args) => {
|
|
714
807
|
try {
|
|
715
808
|
const turns = await resolveTurns(options.turns, args);
|
|
716
809
|
const userTurns = turns.map((turn, index) => ({ turn, index })).filter((entry) => entry.turn.role === "user");
|
|
@@ -779,8 +872,15 @@ function knowledgeRetention(options) {
|
|
|
779
872
|
}
|
|
780
873
|
});
|
|
781
874
|
}
|
|
782
|
-
function numericMetric(name, evaluate) {
|
|
783
|
-
return {
|
|
875
|
+
function numericMetric(name, config, direction, evaluate) {
|
|
876
|
+
return {
|
|
877
|
+
name,
|
|
878
|
+
required: config.required,
|
|
879
|
+
direction,
|
|
880
|
+
threshold: config.strictMode === true ? direction === "higher_is_better" ? 1 : 0 : config.threshold,
|
|
881
|
+
dataType: "NUMERIC",
|
|
882
|
+
evaluate
|
|
883
|
+
};
|
|
784
884
|
}
|
|
785
885
|
function metricConfig(options, defaultName) {
|
|
786
886
|
return {
|
|
@@ -788,7 +888,8 @@ function metricConfig(options, defaultName) {
|
|
|
788
888
|
threshold: validateThreshold(options.threshold ?? 0.5),
|
|
789
889
|
strictMode: options.strictMode ?? false,
|
|
790
890
|
includeReason: options.includeReason ?? true,
|
|
791
|
-
retries: validateRetries(options.retries ?? 0)
|
|
891
|
+
retries: validateRetries(options.retries ?? 0),
|
|
892
|
+
required: options.required ?? true
|
|
792
893
|
};
|
|
793
894
|
}
|
|
794
895
|
function higherOutcome(args) {
|
|
@@ -796,6 +897,7 @@ function higherOutcome(args) {
|
|
|
796
897
|
const threshold = args.strictMode ? 1 : args.threshold;
|
|
797
898
|
const options = {
|
|
798
899
|
comment: args.comment,
|
|
900
|
+
usage: args.usage,
|
|
799
901
|
metadata: evaluationMetadata(
|
|
800
902
|
{
|
|
801
903
|
...args.details,
|
|
@@ -813,6 +915,7 @@ function lowerOutcome(args) {
|
|
|
813
915
|
const threshold = args.strictMode ? 0 : args.threshold;
|
|
814
916
|
const options = {
|
|
815
917
|
comment: args.comment,
|
|
918
|
+
usage: args.usage,
|
|
816
919
|
metadata: evaluationMetadata(
|
|
817
920
|
{
|
|
818
921
|
...args.details,
|
|
@@ -1021,122 +1124,9 @@ function agentEvalTarget(agent, options = {}) {
|
|
|
1021
1124
|
};
|
|
1022
1125
|
}
|
|
1023
1126
|
|
|
1024
|
-
// src/evals/metric.ts
|
|
1025
|
-
function defineMetric(metric) {
|
|
1026
|
-
return metric;
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
// src/evals/metrics.ts
|
|
1030
|
-
import { z as z2 } from "zod";
|
|
1031
|
-
function exactMatch(options = {}) {
|
|
1032
|
-
return {
|
|
1033
|
-
name: options.name ?? "exact_match",
|
|
1034
|
-
async evaluate(args) {
|
|
1035
|
-
const actual = await resolveActual(options.actual, args);
|
|
1036
|
-
const expected = await resolveExpected(options.expected, args);
|
|
1037
|
-
if (expected === void 0) {
|
|
1038
|
-
return EvalOutcome.invalid("No expected value provided for exact match.");
|
|
1039
|
-
}
|
|
1040
|
-
const passed = stableComparable(actual) === stableComparable(expected);
|
|
1041
|
-
return passed ? EvalOutcome.pass(true) : EvalOutcome.fail(false, { comment: `Expected ${formatValue(expected)}.` });
|
|
1042
|
-
}
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
function contains(options = {}) {
|
|
1046
|
-
return {
|
|
1047
|
-
name: options.name ?? "contains",
|
|
1048
|
-
async evaluate(args) {
|
|
1049
|
-
const actual = await resolveActualText(options.actual, args);
|
|
1050
|
-
const expected = await resolveExpected(options.expected, args);
|
|
1051
|
-
if (expected === void 0) {
|
|
1052
|
-
return EvalOutcome.invalid("No expected value provided for contains.");
|
|
1053
|
-
}
|
|
1054
|
-
if (typeof expected !== "string" && !(expected instanceof RegExp)) {
|
|
1055
|
-
return EvalOutcome.invalid("Contains expected value must be a string or RegExp.");
|
|
1056
|
-
}
|
|
1057
|
-
const passed = expected instanceof RegExp ? regexMatches(expected, actual) : actual.includes(expected);
|
|
1058
|
-
return passed ? EvalOutcome.pass(true) : EvalOutcome.fail(false, { comment: `Output did not contain ${String(expected)}.` });
|
|
1059
|
-
}
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
function regexMatches(pattern, text) {
|
|
1063
|
-
pattern.lastIndex = 0;
|
|
1064
|
-
const matched = pattern.test(text);
|
|
1065
|
-
pattern.lastIndex = 0;
|
|
1066
|
-
return matched;
|
|
1067
|
-
}
|
|
1068
|
-
function semanticSimilarity(options) {
|
|
1069
|
-
return {
|
|
1070
|
-
name: options.name ?? "semantic_similarity",
|
|
1071
|
-
async evaluate(args) {
|
|
1072
|
-
const actual = await resolveActualText(options.actual, args);
|
|
1073
|
-
const expected = await resolveExpected(options.expected, args);
|
|
1074
|
-
if (expected === void 0) {
|
|
1075
|
-
return EvalOutcome.invalid("No expected value provided for semantic similarity.");
|
|
1076
|
-
}
|
|
1077
|
-
if (typeof expected !== "string") {
|
|
1078
|
-
return EvalOutcome.invalid("Semantic similarity expected value must be a string.");
|
|
1079
|
-
}
|
|
1080
|
-
const [actualEmbedding, expectedEmbedding] = await Promise.all([
|
|
1081
|
-
embedText(options.model, actual),
|
|
1082
|
-
embedText(options.model, expected)
|
|
1083
|
-
]);
|
|
1084
|
-
const score = cosineSimilarity(actualEmbedding.vector, expectedEmbedding.vector);
|
|
1085
|
-
return score >= options.threshold ? EvalOutcome.pass(score) : EvalOutcome.fail(score, { comment: `Similarity below threshold ${options.threshold}.` });
|
|
1086
|
-
}
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
function llmJudge(options) {
|
|
1090
|
-
const extractor = new ExtractorBuilder(options.model, options.schema).instructions(
|
|
1091
|
-
options.instructions ?? "Judge the eval case by the requested schema. Submit the judgment using the schema."
|
|
1092
|
-
).retries(options.retries ?? 0).build();
|
|
1093
|
-
return {
|
|
1094
|
-
name: options.name ?? "llm_judge",
|
|
1095
|
-
async evaluate(args) {
|
|
1096
|
-
try {
|
|
1097
|
-
const judgment = await extractor.extract(await resolveJudgePrompt(options.prompt, args));
|
|
1098
|
-
return options.passes(judgment) ? EvalOutcome.pass(judgment) : EvalOutcome.fail(judgment);
|
|
1099
|
-
} catch (error) {
|
|
1100
|
-
return EvalOutcome.invalid(errorMessage(error));
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
|
-
function llmScore(options) {
|
|
1106
|
-
const criteria = Array.isArray(options.criteria) ? options.criteria.join("\n") : options.criteria;
|
|
1107
|
-
const extractor = new ExtractorBuilder(
|
|
1108
|
-
options.model,
|
|
1109
|
-
z2.object({
|
|
1110
|
-
score: z2.number(),
|
|
1111
|
-
feedback: z2.string()
|
|
1112
|
-
})
|
|
1113
|
-
).instructions(
|
|
1114
|
-
options.instructions ?? `Score the eval case against these criteria:
|
|
1115
|
-
${criteria}
|
|
1116
|
-
|
|
1117
|
-
Return a score between 0 and 1 and brief feedback.`
|
|
1118
|
-
).retries(options.retries ?? 0).build();
|
|
1119
|
-
return {
|
|
1120
|
-
name: options.name ?? "llm_score",
|
|
1121
|
-
async evaluate(args) {
|
|
1122
|
-
try {
|
|
1123
|
-
const score = await extractor.extract(await resolveJudgePrompt(options.prompt, args));
|
|
1124
|
-
if (score.score < 0 || score.score > 1) {
|
|
1125
|
-
return EvalOutcome.invalid(`Score ${score.score} outside valid range [0, 1].`, {
|
|
1126
|
-
score
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
return score.score >= options.threshold ? EvalOutcome.pass(score, { comment: score.feedback }) : EvalOutcome.fail(score, { comment: score.feedback });
|
|
1130
|
-
} catch (error) {
|
|
1131
|
-
return EvalOutcome.invalid(errorMessage(error));
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
};
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
1127
|
// src/evals/reporting.ts
|
|
1138
|
-
function projectEvalOutcome(outcome, dataType) {
|
|
1139
|
-
const value = projectScoreValue(outcome, dataType);
|
|
1128
|
+
function projectEvalOutcome(outcome, dataType, projectScore) {
|
|
1129
|
+
const value = projectScoreValue(outcome, dataType, projectScore);
|
|
1140
1130
|
const projection = {
|
|
1141
1131
|
outcome: outcome.outcome,
|
|
1142
1132
|
value,
|
|
@@ -1158,8 +1148,13 @@ function defaultEvalTraceSelector(args) {
|
|
|
1158
1148
|
metadata: args.case.metadata
|
|
1159
1149
|
});
|
|
1160
1150
|
}
|
|
1161
|
-
function projectScoreValue(outcome, dataType) {
|
|
1151
|
+
function projectScoreValue(outcome, dataType, projectScore) {
|
|
1162
1152
|
const score = outcome.score;
|
|
1153
|
+
if (score !== void 0 && projectScore !== void 0) {
|
|
1154
|
+
const projected = projectScore(score);
|
|
1155
|
+
if (typeof projected === "boolean") return projected ? 1 : 0;
|
|
1156
|
+
return projected;
|
|
1157
|
+
}
|
|
1163
1158
|
if (dataType === "CATEGORICAL") {
|
|
1164
1159
|
if (typeof score === "string") return score;
|
|
1165
1160
|
if (typeof score === "number") return String(score);
|
|
@@ -1211,6 +1206,7 @@ function readTraceRef(value) {
|
|
|
1211
1206
|
|
|
1212
1207
|
// src/evals/runner.ts
|
|
1213
1208
|
async function runEvalSuite(options) {
|
|
1209
|
+
validateSuiteOptions(options);
|
|
1214
1210
|
const startedAtMs = Date.now();
|
|
1215
1211
|
const run = resolveRun(options, startedAtMs);
|
|
1216
1212
|
const reporters = options.reporters ?? [];
|
|
@@ -1238,8 +1234,10 @@ async function runEvalSuite(options) {
|
|
|
1238
1234
|
throw error;
|
|
1239
1235
|
}
|
|
1240
1236
|
let results;
|
|
1237
|
+
let aggregates;
|
|
1241
1238
|
try {
|
|
1242
1239
|
results = await runEvalCases(options, run);
|
|
1240
|
+
aggregates = await aggregateResult(options, results);
|
|
1243
1241
|
} catch (error) {
|
|
1244
1242
|
await notifyRunEnd(reporters, {
|
|
1245
1243
|
...lifecycle,
|
|
@@ -1250,13 +1248,15 @@ async function runEvalSuite(options) {
|
|
|
1250
1248
|
});
|
|
1251
1249
|
throw error;
|
|
1252
1250
|
}
|
|
1253
|
-
const counts = countOutcomes(results);
|
|
1254
1251
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1255
1252
|
const result = {
|
|
1256
1253
|
name: options.name,
|
|
1257
1254
|
run: { ...run, completedAt },
|
|
1258
1255
|
results,
|
|
1259
|
-
|
|
1256
|
+
metrics: aggregates.metrics,
|
|
1257
|
+
cases: aggregates.cases,
|
|
1258
|
+
usage: aggregates.usage,
|
|
1259
|
+
...aggregates.cost === void 0 ? {} : { cost: aggregates.cost },
|
|
1260
1260
|
durationMs: Date.now() - startedAtMs,
|
|
1261
1261
|
reporterErrors
|
|
1262
1262
|
};
|
|
@@ -1268,7 +1268,10 @@ async function runEvalSuite(options) {
|
|
|
1268
1268
|
status: "completed",
|
|
1269
1269
|
completedAt,
|
|
1270
1270
|
durationMs: result.durationMs,
|
|
1271
|
-
|
|
1271
|
+
metrics: result.metrics,
|
|
1272
|
+
cases: result.cases,
|
|
1273
|
+
usage: result.usage,
|
|
1274
|
+
...result.cost === void 0 ? {} : { cost: result.cost }
|
|
1272
1275
|
},
|
|
1273
1276
|
options.failOnReporterError === true
|
|
1274
1277
|
)
|
|
@@ -1326,11 +1329,24 @@ async function runEvalCase(options, testCase, run) {
|
|
|
1326
1329
|
reporters: options.reporters ?? [],
|
|
1327
1330
|
failOnReporterError: options.failOnReporterError === true
|
|
1328
1331
|
});
|
|
1329
|
-
|
|
1332
|
+
const metricResult = {
|
|
1333
|
+
metricName: metric.name,
|
|
1334
|
+
required: metric.required ?? true,
|
|
1335
|
+
outcome,
|
|
1336
|
+
reporterErrors
|
|
1337
|
+
};
|
|
1338
|
+
if (metric.direction !== void 0) metricResult.direction = metric.direction;
|
|
1339
|
+
if (metric.threshold !== void 0) metricResult.threshold = metric.threshold;
|
|
1340
|
+
metrics.push(metricResult);
|
|
1330
1341
|
}
|
|
1342
|
+
const scores = Object.fromEntries(
|
|
1343
|
+
metrics.map((metric) => [metric.metricName, metric.outcome])
|
|
1344
|
+
);
|
|
1331
1345
|
const result = {
|
|
1332
1346
|
case: testCase,
|
|
1333
|
-
metrics
|
|
1347
|
+
outcome: caseOutcome(targetError, metrics),
|
|
1348
|
+
metrics,
|
|
1349
|
+
scores
|
|
1334
1350
|
};
|
|
1335
1351
|
if (output !== void 0) {
|
|
1336
1352
|
result.output = output;
|
|
@@ -1436,26 +1452,648 @@ async function notifyRunEnd(reporters, args, failOnReporterError = false) {
|
|
|
1436
1452
|
}
|
|
1437
1453
|
return errors;
|
|
1438
1454
|
}
|
|
1439
|
-
function
|
|
1440
|
-
|
|
1441
|
-
let failed = 0;
|
|
1442
|
-
let invalid = 0;
|
|
1455
|
+
function countMetricOutcomes(results) {
|
|
1456
|
+
const totals = emptyTotals();
|
|
1443
1457
|
for (const result of results) {
|
|
1444
1458
|
for (const metric of result.metrics) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1459
|
+
totals.total += 1;
|
|
1460
|
+
totals[statusKey(metric.outcome.outcome)] += 1;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
return totals;
|
|
1464
|
+
}
|
|
1465
|
+
function countCaseOutcomes(results) {
|
|
1466
|
+
const totals = emptyTotals();
|
|
1467
|
+
for (const result of results) {
|
|
1468
|
+
totals.total += 1;
|
|
1469
|
+
totals[statusKey(result.outcome)] += 1;
|
|
1470
|
+
}
|
|
1471
|
+
return totals;
|
|
1472
|
+
}
|
|
1473
|
+
function emptyTotals() {
|
|
1474
|
+
return { total: 0, passed: 0, failed: 0, invalid: 0 };
|
|
1475
|
+
}
|
|
1476
|
+
function statusKey(status) {
|
|
1477
|
+
if (status === "pass") return "passed";
|
|
1478
|
+
if (status === "fail") return "failed";
|
|
1479
|
+
return "invalid";
|
|
1480
|
+
}
|
|
1481
|
+
function caseOutcome(targetError, metrics) {
|
|
1482
|
+
if (targetError !== void 0) return "invalid";
|
|
1483
|
+
const required = metrics.filter((metric) => metric.required);
|
|
1484
|
+
if (required.some((metric) => metric.outcome.outcome === "invalid")) return "invalid";
|
|
1485
|
+
if (required.some((metric) => metric.outcome.outcome === "fail")) return "fail";
|
|
1486
|
+
return "pass";
|
|
1487
|
+
}
|
|
1488
|
+
async function aggregateResult(options, results) {
|
|
1489
|
+
let targetUsage = Usage.empty();
|
|
1490
|
+
let evaluationUsage = Usage.empty();
|
|
1491
|
+
let targetCost = 0;
|
|
1492
|
+
let evaluationCost = 0;
|
|
1493
|
+
for (const result of results) {
|
|
1494
|
+
if (result.output !== void 0) {
|
|
1495
|
+
const usage2 = await resolveTargetUsage(options, result.case, result.output);
|
|
1496
|
+
if (usage2 !== void 0) {
|
|
1497
|
+
targetUsage = Usage.add(targetUsage, usage2);
|
|
1498
|
+
if (options.cost !== void 0) {
|
|
1499
|
+
targetCost += await calculateCost(
|
|
1500
|
+
options.cost.calculate({
|
|
1501
|
+
kind: "target",
|
|
1502
|
+
suiteName: options.name,
|
|
1503
|
+
case: result.case,
|
|
1504
|
+
output: result.output,
|
|
1505
|
+
usage: usage2
|
|
1506
|
+
})
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
for (const metricResult of result.metrics) {
|
|
1512
|
+
const usage2 = metricResult.outcome.usage;
|
|
1513
|
+
if (usage2 === void 0) continue;
|
|
1514
|
+
assertUsage(usage2, `Evaluation usage for metric ${metricResult.metricName}`);
|
|
1515
|
+
evaluationUsage = Usage.add(evaluationUsage, usage2);
|
|
1516
|
+
if (options.cost !== void 0 && result.output !== void 0) {
|
|
1517
|
+
const metric = options.metrics.find(
|
|
1518
|
+
(candidate) => candidate.name === metricResult.metricName
|
|
1519
|
+
);
|
|
1520
|
+
if (metric !== void 0) {
|
|
1521
|
+
evaluationCost += await calculateCost(
|
|
1522
|
+
options.cost.calculate({
|
|
1523
|
+
kind: "evaluation",
|
|
1524
|
+
suiteName: options.name,
|
|
1525
|
+
case: result.case,
|
|
1526
|
+
output: result.output,
|
|
1527
|
+
metric,
|
|
1528
|
+
usage: usage2
|
|
1529
|
+
})
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
const usage = {
|
|
1536
|
+
target: targetUsage,
|
|
1537
|
+
evaluation: evaluationUsage,
|
|
1538
|
+
total: Usage.add(targetUsage, evaluationUsage)
|
|
1539
|
+
};
|
|
1540
|
+
const aggregates = {
|
|
1541
|
+
metrics: countMetricOutcomes(results),
|
|
1542
|
+
cases: countCaseOutcomes(results),
|
|
1543
|
+
usage
|
|
1544
|
+
};
|
|
1545
|
+
if (options.cost !== void 0) {
|
|
1546
|
+
aggregates.cost = {
|
|
1547
|
+
currency: options.cost.currency,
|
|
1548
|
+
target: targetCost,
|
|
1549
|
+
evaluation: evaluationCost,
|
|
1550
|
+
total: targetCost + evaluationCost
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
return aggregates;
|
|
1554
|
+
}
|
|
1555
|
+
async function resolveTargetUsage(options, testCase, output) {
|
|
1556
|
+
const usage = options.targetUsage === void 0 ? usageFromOutput(output) : await options.targetUsage({ suiteName: options.name, case: testCase, output });
|
|
1557
|
+
if (usage !== void 0) assertUsage(usage, `Target usage for case ${testCase.id}`);
|
|
1558
|
+
return usage;
|
|
1559
|
+
}
|
|
1560
|
+
function usageFromOutput(output) {
|
|
1561
|
+
if (typeof output !== "object" || output === null || !("usage" in output)) return void 0;
|
|
1562
|
+
return output.usage;
|
|
1563
|
+
}
|
|
1564
|
+
function assertUsage(usage, label) {
|
|
1565
|
+
for (const [key, value] of Object.entries(usage)) {
|
|
1566
|
+
if (key === "details") continue;
|
|
1567
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
1568
|
+
throw new TypeError(`${label} must contain finite, non-negative token counts`);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
async function calculateCost(value) {
|
|
1573
|
+
const cost = await value;
|
|
1574
|
+
if (!Number.isFinite(cost) || cost < 0) {
|
|
1575
|
+
throw new TypeError("Evaluation cost calculator must return a finite, non-negative number");
|
|
1576
|
+
}
|
|
1577
|
+
return cost;
|
|
1578
|
+
}
|
|
1579
|
+
function validateSuiteOptions(options) {
|
|
1580
|
+
assertUnique(
|
|
1581
|
+
options.cases.map((testCase) => testCase.id),
|
|
1582
|
+
"Evaluation case id"
|
|
1583
|
+
);
|
|
1584
|
+
assertUnique(
|
|
1585
|
+
options.metrics.map((metric) => metric.name),
|
|
1586
|
+
"Evaluation metric name"
|
|
1587
|
+
);
|
|
1588
|
+
if (options.cost !== void 0 && options.cost.currency.trim().length === 0) {
|
|
1589
|
+
throw new TypeError("Evaluation cost currency must not be empty");
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
function assertUnique(values, label) {
|
|
1593
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1594
|
+
for (const value of values) {
|
|
1595
|
+
if (seen.has(value)) throw new TypeError(`${label} must be unique: ${value}`);
|
|
1596
|
+
seen.add(value);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// src/evals/cli.ts
|
|
1601
|
+
var EvalAssertionError = class extends Error {
|
|
1602
|
+
mismatches;
|
|
1603
|
+
constructor(message, mismatches) {
|
|
1604
|
+
super(`${message}
|
|
1605
|
+
${mismatches.map((mismatch) => `- ${mismatch}`).join("\n")}`);
|
|
1606
|
+
this.name = "EvalAssertionError";
|
|
1607
|
+
this.mismatches = mismatches;
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
function printEvalResult(result, options = {}) {
|
|
1611
|
+
const format = options.format ?? "pretty";
|
|
1612
|
+
if (format === "quiet") return;
|
|
1613
|
+
const write = options.output?.stdout ?? ((text) => process.stdout.write(text));
|
|
1614
|
+
write(`${format === "json" ? jsonResult(result) : prettyResult(result)}
|
|
1615
|
+
`);
|
|
1616
|
+
}
|
|
1617
|
+
function evalExitCode(result, expectations) {
|
|
1618
|
+
const hasExpectations = expectations?.totals !== void 0 || expectations?.outcomes !== void 0;
|
|
1619
|
+
const mismatches = hasExpectations ? expectationMismatches(result, expectations) : [];
|
|
1620
|
+
if (hasExpectations && mismatches.length === 0) return 0;
|
|
1621
|
+
if (hasExpectations && hasUnexpectedInvalid(result, expectations)) return 2;
|
|
1622
|
+
if (hasExpectations) return 1;
|
|
1623
|
+
if (requiredMetricCount(result, "invalid") > 0) return 2;
|
|
1624
|
+
return requiredMetricCount(result, "fail") > 0 ? 1 : 0;
|
|
1625
|
+
}
|
|
1626
|
+
function assertEvalTotals(result, expected) {
|
|
1627
|
+
const mismatches = totalMismatches(result, expected);
|
|
1628
|
+
if (mismatches.length > 0) {
|
|
1629
|
+
throw new EvalAssertionError("Evaluation totals did not match expectations.", mismatches);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
function assertEvalOutcomes(result, expected) {
|
|
1633
|
+
const mismatches = outcomeMismatches(result, expected);
|
|
1634
|
+
if (mismatches.length > 0) {
|
|
1635
|
+
throw new EvalAssertionError("Evaluation outcomes did not match expectations.", mismatches);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
async function runEvalCli(options) {
|
|
1639
|
+
const { format, exitCode, expectations, output, ...suiteOptions } = options;
|
|
1640
|
+
const result = await runEvalSuite(
|
|
1641
|
+
suiteOptions
|
|
1642
|
+
);
|
|
1643
|
+
printEvalResult(result, { format, output });
|
|
1644
|
+
const code = evalExitCode(result, expectations);
|
|
1645
|
+
const mismatches = expectationMismatches(result, expectations);
|
|
1646
|
+
if (mismatches.length > 0 && format !== "quiet") {
|
|
1647
|
+
const write = output?.stderr ?? ((text) => process.stderr.write(text));
|
|
1648
|
+
write(
|
|
1649
|
+
`Evaluation expectation mismatches:
|
|
1650
|
+
${mismatches.map((value) => `- ${value}`).join("\n")}
|
|
1651
|
+
`
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1654
|
+
const currentExitCode = typeof process.exitCode === "number" ? process.exitCode : Number(process.exitCode ?? 0);
|
|
1655
|
+
if (exitCode === true && code > currentExitCode) process.exitCode = code;
|
|
1656
|
+
return result;
|
|
1657
|
+
}
|
|
1658
|
+
function prettyResult(result) {
|
|
1659
|
+
const lines = [
|
|
1660
|
+
`${result.name} (${result.run.id})`,
|
|
1661
|
+
`Cases: ${totalsText(result.cases)}`,
|
|
1662
|
+
`Metrics: ${totalsText(result.metrics)}`
|
|
1663
|
+
];
|
|
1664
|
+
for (const caseResult of result.results) {
|
|
1665
|
+
lines.push(``, `[${caseResult.outcome.toUpperCase()}] ${caseResult.case.id}`);
|
|
1666
|
+
if (caseResult.output !== void 0) lines.push(` output: ${displayValue(caseResult.output)}`);
|
|
1667
|
+
if (caseResult.targetError !== void 0) {
|
|
1668
|
+
lines.push(` target error: ${errorText(caseResult.targetError)}`);
|
|
1669
|
+
}
|
|
1670
|
+
for (const metric of caseResult.metrics) {
|
|
1671
|
+
const parts = [` - ${metric.metricName}: ${metric.outcome.outcome}`];
|
|
1672
|
+
if (metric.outcome.score !== void 0)
|
|
1673
|
+
parts.push(`score=${displayValue(metric.outcome.score)}`);
|
|
1674
|
+
if (metric.threshold !== void 0) parts.push(`threshold=${metric.threshold}`);
|
|
1675
|
+
if (metric.direction !== void 0) parts.push(`direction=${metric.direction}`);
|
|
1676
|
+
if (!metric.required) parts.push("optional");
|
|
1677
|
+
lines.push(parts.join(" | "));
|
|
1678
|
+
const explanation = metric.outcome.comment ?? (metric.outcome.outcome === "invalid" ? metric.outcome.reason : void 0);
|
|
1679
|
+
if (explanation !== void 0) lines.push(` ${explanation}`);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
lines.push(
|
|
1683
|
+
``,
|
|
1684
|
+
`Usage: target=${result.usage.target.totalTokens} evaluation=${result.usage.evaluation.totalTokens} total=${result.usage.total.totalTokens} tokens`
|
|
1685
|
+
);
|
|
1686
|
+
if (result.cost !== void 0) {
|
|
1687
|
+
lines.push(
|
|
1688
|
+
`Cost: target=${result.cost.target} evaluation=${result.cost.evaluation} total=${result.cost.total} ${result.cost.currency}`
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
lines.push(`Duration: ${result.durationMs}ms`);
|
|
1692
|
+
return lines.join("\n");
|
|
1693
|
+
}
|
|
1694
|
+
function jsonResult(result) {
|
|
1695
|
+
return JSON.stringify(
|
|
1696
|
+
result,
|
|
1697
|
+
(_key, value) => {
|
|
1698
|
+
if (value instanceof Error) {
|
|
1699
|
+
return { name: value.name, message: value.message, stack: value.stack };
|
|
1700
|
+
}
|
|
1701
|
+
if (value instanceof RegExp) return String(value);
|
|
1702
|
+
return value;
|
|
1703
|
+
},
|
|
1704
|
+
2
|
|
1705
|
+
);
|
|
1706
|
+
}
|
|
1707
|
+
function expectationMismatches(result, expectations) {
|
|
1708
|
+
if (expectations === void 0) return [];
|
|
1709
|
+
return [
|
|
1710
|
+
...expectations.totals === void 0 ? [] : totalMismatches(result, expectations.totals),
|
|
1711
|
+
...expectations.outcomes === void 0 ? [] : outcomeMismatches(result, expectations.outcomes)
|
|
1712
|
+
];
|
|
1713
|
+
}
|
|
1714
|
+
function totalMismatches(result, expected) {
|
|
1715
|
+
const directMetrics = {
|
|
1716
|
+
...expected.total === void 0 ? {} : { total: expected.total },
|
|
1717
|
+
...expected.passed === void 0 ? {} : { passed: expected.passed },
|
|
1718
|
+
...expected.failed === void 0 ? {} : { failed: expected.failed },
|
|
1719
|
+
...expected.invalid === void 0 ? {} : { invalid: expected.invalid }
|
|
1720
|
+
};
|
|
1721
|
+
return [
|
|
1722
|
+
...totalsGroupMismatches("metrics", result.metrics, {
|
|
1723
|
+
...directMetrics,
|
|
1724
|
+
...expected.metrics
|
|
1725
|
+
}),
|
|
1726
|
+
...totalsGroupMismatches("cases", result.cases, expected.cases)
|
|
1727
|
+
];
|
|
1728
|
+
}
|
|
1729
|
+
function totalsGroupMismatches(label, actual, expected) {
|
|
1730
|
+
if (expected === void 0) return [];
|
|
1731
|
+
const mismatches = [];
|
|
1732
|
+
for (const key of ["total", "passed", "failed", "invalid"]) {
|
|
1733
|
+
if (expected[key] !== void 0 && actual[key] !== expected[key]) {
|
|
1734
|
+
mismatches.push(`${label}.${key}: expected ${expected[key]}, received ${actual[key]}`);
|
|
1448
1735
|
}
|
|
1449
1736
|
}
|
|
1450
|
-
return
|
|
1737
|
+
return mismatches;
|
|
1738
|
+
}
|
|
1739
|
+
function outcomeMismatches(result, expected) {
|
|
1740
|
+
const mismatches = [];
|
|
1741
|
+
const actualCases = new Map(result.results.map((caseResult) => [caseResult.case.id, caseResult]));
|
|
1742
|
+
for (const [caseId, metrics] of Object.entries(expected)) {
|
|
1743
|
+
const caseResult = actualCases.get(caseId);
|
|
1744
|
+
if (caseResult === void 0) {
|
|
1745
|
+
mismatches.push(`${caseId}: expected case was not present`);
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
for (const [metricName, expectedOutcome] of Object.entries(metrics)) {
|
|
1749
|
+
const metric = caseResult.metrics.find((candidate) => candidate.metricName === metricName);
|
|
1750
|
+
if (metric === void 0) {
|
|
1751
|
+
mismatches.push(`${caseId}.${metricName}: expected metric was not present`);
|
|
1752
|
+
} else if (metric.outcome.outcome !== expectedOutcome) {
|
|
1753
|
+
mismatches.push(
|
|
1754
|
+
`${caseId}.${metricName}: expected ${expectedOutcome}, received ${metric.outcome.outcome}`
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
for (const caseResult of result.results) {
|
|
1760
|
+
for (const metric of caseResult.metrics) {
|
|
1761
|
+
if (!metric.required) continue;
|
|
1762
|
+
const expectedOutcome = expected[caseResult.case.id]?.[metric.metricName] ?? "pass";
|
|
1763
|
+
if (metric.outcome.outcome !== expectedOutcome) {
|
|
1764
|
+
const message = `${caseResult.case.id}.${metric.metricName}: expected ${expectedOutcome}, received ${metric.outcome.outcome}`;
|
|
1765
|
+
if (!mismatches.includes(message)) mismatches.push(message);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
return mismatches;
|
|
1770
|
+
}
|
|
1771
|
+
function hasUnexpectedInvalid(result, expectations) {
|
|
1772
|
+
if (expectations?.outcomes !== void 0) {
|
|
1773
|
+
return result.results.some(
|
|
1774
|
+
(caseResult) => caseResult.metrics.some(
|
|
1775
|
+
(metric) => metric.required && metric.outcome.outcome === "invalid" && expectations.outcomes?.[caseResult.case.id]?.[metric.metricName] !== "invalid"
|
|
1776
|
+
)
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
const expectedInvalid = expectations?.totals?.metrics?.invalid ?? expectations?.totals?.invalid;
|
|
1780
|
+
return result.metrics.invalid > (expectedInvalid ?? 0);
|
|
1781
|
+
}
|
|
1782
|
+
function requiredMetricCount(result, status) {
|
|
1783
|
+
return result.results.reduce(
|
|
1784
|
+
(total, caseResult) => total + caseResult.metrics.filter((metric) => metric.required && metric.outcome.outcome === status).length,
|
|
1785
|
+
0
|
|
1786
|
+
);
|
|
1787
|
+
}
|
|
1788
|
+
function totalsText(totals) {
|
|
1789
|
+
return `${totals.total} total / ${totals.passed} pass / ${totals.failed} fail / ${totals.invalid} invalid`;
|
|
1790
|
+
}
|
|
1791
|
+
function displayValue(value) {
|
|
1792
|
+
if (typeof value === "string") return value;
|
|
1793
|
+
try {
|
|
1794
|
+
return JSON.stringify(value);
|
|
1795
|
+
} catch {
|
|
1796
|
+
return String(value);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
function errorText(error) {
|
|
1800
|
+
return error instanceof Error ? error.message : displayValue(error);
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
// src/evals/metric.ts
|
|
1804
|
+
function defineMetric(metric) {
|
|
1805
|
+
return metric;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// src/evals/metrics.ts
|
|
1809
|
+
import { z as z2 } from "zod";
|
|
1810
|
+
function exactMatch(options = {}) {
|
|
1811
|
+
return {
|
|
1812
|
+
name: options.name ?? "exact_match",
|
|
1813
|
+
required: options.required ?? true,
|
|
1814
|
+
dataType: "BOOLEAN",
|
|
1815
|
+
direction: "higher_is_better",
|
|
1816
|
+
threshold: 1,
|
|
1817
|
+
async evaluate(args) {
|
|
1818
|
+
const actual = await resolveActual(options.actual, args);
|
|
1819
|
+
const expected = await resolveExpected(options.expected, args);
|
|
1820
|
+
if (expected === void 0) {
|
|
1821
|
+
return EvalOutcome.invalid("No expected value provided for exact match.");
|
|
1822
|
+
}
|
|
1823
|
+
const passed = stableComparable(actual) === stableComparable(expected);
|
|
1824
|
+
return passed ? EvalOutcome.pass(true) : EvalOutcome.fail(false, { comment: `Expected ${formatValue(expected)}.` });
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
function contains(options = {}) {
|
|
1829
|
+
return {
|
|
1830
|
+
name: options.name ?? "contains",
|
|
1831
|
+
required: options.required ?? true,
|
|
1832
|
+
dataType: "BOOLEAN",
|
|
1833
|
+
direction: "higher_is_better",
|
|
1834
|
+
threshold: 1,
|
|
1835
|
+
async evaluate(args) {
|
|
1836
|
+
const actual = await resolveActualText(options.actual, args);
|
|
1837
|
+
const expected = await resolveExpected(options.expected, args);
|
|
1838
|
+
if (expected === void 0) {
|
|
1839
|
+
return EvalOutcome.invalid("No expected value provided for contains.");
|
|
1840
|
+
}
|
|
1841
|
+
if (typeof expected !== "string" && !(expected instanceof RegExp)) {
|
|
1842
|
+
return EvalOutcome.invalid("Contains expected value must be a string or RegExp.");
|
|
1843
|
+
}
|
|
1844
|
+
const passed = expected instanceof RegExp ? regexMatches(expected, actual) : actual.includes(expected);
|
|
1845
|
+
return passed ? EvalOutcome.pass(true) : EvalOutcome.fail(false, { comment: `Output did not contain ${String(expected)}.` });
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
function notContains(options = {}) {
|
|
1850
|
+
return {
|
|
1851
|
+
name: options.name ?? "not_contains",
|
|
1852
|
+
required: options.required ?? true,
|
|
1853
|
+
dataType: "BOOLEAN",
|
|
1854
|
+
direction: "higher_is_better",
|
|
1855
|
+
threshold: 1,
|
|
1856
|
+
async evaluate(args) {
|
|
1857
|
+
const actual = await resolveActualText(options.actual, args);
|
|
1858
|
+
const expected = await resolveExpected(options.expected, args);
|
|
1859
|
+
if (expected === void 0) {
|
|
1860
|
+
return EvalOutcome.invalid("No expected value provided for notContains.");
|
|
1861
|
+
}
|
|
1862
|
+
if (typeof expected !== "string" && !(expected instanceof RegExp)) {
|
|
1863
|
+
return EvalOutcome.invalid("notContains expected value must be a string or RegExp.");
|
|
1864
|
+
}
|
|
1865
|
+
const found = textExpectationMatches(expected, actual);
|
|
1866
|
+
return found ? EvalOutcome.fail(false, {
|
|
1867
|
+
comment: `Output contained forbidden value ${String(expected)}.`
|
|
1868
|
+
}) : EvalOutcome.pass(true);
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
function containsAll(options) {
|
|
1873
|
+
return containsListMetric("contains_all", "all", options);
|
|
1874
|
+
}
|
|
1875
|
+
function containsAny(options) {
|
|
1876
|
+
return containsListMetric("contains_any", "any", options);
|
|
1877
|
+
}
|
|
1878
|
+
function containsListMetric(defaultName, mode, options) {
|
|
1879
|
+
return {
|
|
1880
|
+
name: options.name ?? defaultName,
|
|
1881
|
+
required: options.required ?? true,
|
|
1882
|
+
dataType: "BOOLEAN",
|
|
1883
|
+
direction: "higher_is_better",
|
|
1884
|
+
threshold: 1,
|
|
1885
|
+
async evaluate(args) {
|
|
1886
|
+
const actual = await resolveActualText(options.actual, args);
|
|
1887
|
+
const expected = await resolveExpected(options.expected, args);
|
|
1888
|
+
if (!Array.isArray(expected) || expected.length === 0) {
|
|
1889
|
+
return EvalOutcome.invalid(`${defaultName} expected value must be a non-empty array.`);
|
|
1890
|
+
}
|
|
1891
|
+
if (expected.some((value) => typeof value !== "string" && !(value instanceof RegExp))) {
|
|
1892
|
+
return EvalOutcome.invalid(`${defaultName} expected values must be strings or RegExp.`);
|
|
1893
|
+
}
|
|
1894
|
+
const matches2 = expected.map((value) => textExpectationMatches(value, actual));
|
|
1895
|
+
const passed = mode === "all" ? matches2.every(Boolean) : matches2.some(Boolean);
|
|
1896
|
+
if (passed) return EvalOutcome.pass(true);
|
|
1897
|
+
const missing = expected.filter((_, index) => !matches2[index]).map(String);
|
|
1898
|
+
const comment = mode === "all" ? `Output was missing: ${missing.join(", ")}.` : `Output matched none of: ${expected.map(String).join(", ")}.`;
|
|
1899
|
+
return EvalOutcome.fail(false, { comment });
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
function matches(options) {
|
|
1904
|
+
return regexMetric("matches", false, options);
|
|
1905
|
+
}
|
|
1906
|
+
function doesNotMatch(options) {
|
|
1907
|
+
return regexMetric("does_not_match", true, options);
|
|
1908
|
+
}
|
|
1909
|
+
function regexMetric(defaultName, negate, options) {
|
|
1910
|
+
return {
|
|
1911
|
+
name: options.name ?? defaultName,
|
|
1912
|
+
required: options.required ?? true,
|
|
1913
|
+
dataType: "BOOLEAN",
|
|
1914
|
+
direction: "higher_is_better",
|
|
1915
|
+
threshold: 1,
|
|
1916
|
+
async evaluate(args) {
|
|
1917
|
+
const actual = await resolveActualText(options.actual, args);
|
|
1918
|
+
const expected = await resolveExpected(options.expected, args);
|
|
1919
|
+
if (!(expected instanceof RegExp)) {
|
|
1920
|
+
return EvalOutcome.invalid(`${defaultName} expected value must be a RegExp.`);
|
|
1921
|
+
}
|
|
1922
|
+
const matched = regexMatches(expected, actual);
|
|
1923
|
+
const passed = negate ? !matched : matched;
|
|
1924
|
+
return passed ? EvalOutcome.pass(true) : EvalOutcome.fail(false, {
|
|
1925
|
+
comment: negate ? `Output matched forbidden pattern ${String(expected)}.` : `Output did not match ${String(expected)}.`
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
function maxLength(options) {
|
|
1931
|
+
return {
|
|
1932
|
+
name: options.name ?? "max_length",
|
|
1933
|
+
required: options.required ?? true,
|
|
1934
|
+
dataType: "BOOLEAN",
|
|
1935
|
+
direction: "higher_is_better",
|
|
1936
|
+
threshold: 1,
|
|
1937
|
+
async evaluate(args) {
|
|
1938
|
+
const actual = await resolveActualText(options.actual, args);
|
|
1939
|
+
const max = await resolveOption(options.max, args);
|
|
1940
|
+
if (!Number.isInteger(max) || max < 0) {
|
|
1941
|
+
return EvalOutcome.invalid("maxLength max must be a non-negative integer.");
|
|
1942
|
+
}
|
|
1943
|
+
const length = Array.from(actual).length;
|
|
1944
|
+
return length <= max ? EvalOutcome.pass(true) : EvalOutcome.fail(false, { comment: `Output length ${length} exceeded maximum ${max}.` });
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
function requiredFields(options) {
|
|
1949
|
+
return {
|
|
1950
|
+
name: options.name ?? "required_fields",
|
|
1951
|
+
required: options.required ?? true,
|
|
1952
|
+
dataType: "BOOLEAN",
|
|
1953
|
+
direction: "higher_is_better",
|
|
1954
|
+
threshold: 1,
|
|
1955
|
+
async evaluate(args) {
|
|
1956
|
+
const actual = await resolveActual(options.actual, args);
|
|
1957
|
+
const expected = await resolveOption(options.expected, args);
|
|
1958
|
+
if (!Array.isArray(expected) || expected.length === 0 || expected.some((field) => typeof field !== "string" || field.length === 0)) {
|
|
1959
|
+
return EvalOutcome.invalid(
|
|
1960
|
+
"requiredFields expected value must be a non-empty string array."
|
|
1961
|
+
);
|
|
1962
|
+
}
|
|
1963
|
+
if (typeof actual !== "object" || actual === null || Array.isArray(actual)) {
|
|
1964
|
+
return EvalOutcome.invalid("requiredFields actual value must be an object.");
|
|
1965
|
+
}
|
|
1966
|
+
const missing = expected.filter((field) => !Object.hasOwn(actual, field));
|
|
1967
|
+
return missing.length === 0 ? EvalOutcome.pass(true) : EvalOutcome.fail(false, { comment: `Missing required fields: ${missing.join(", ")}.` });
|
|
1968
|
+
}
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
function regexMatches(pattern, text) {
|
|
1972
|
+
pattern.lastIndex = 0;
|
|
1973
|
+
const matched = pattern.test(text);
|
|
1974
|
+
pattern.lastIndex = 0;
|
|
1975
|
+
return matched;
|
|
1976
|
+
}
|
|
1977
|
+
function textExpectationMatches(expected, actual) {
|
|
1978
|
+
return expected instanceof RegExp ? regexMatches(expected, actual) : actual.includes(expected);
|
|
1979
|
+
}
|
|
1980
|
+
async function resolveOption(value, args) {
|
|
1981
|
+
return typeof value === "function" ? value(args) : value;
|
|
1982
|
+
}
|
|
1983
|
+
function semanticSimilarity(options) {
|
|
1984
|
+
return {
|
|
1985
|
+
name: options.name ?? "semantic_similarity",
|
|
1986
|
+
required: options.required ?? true,
|
|
1987
|
+
dataType: "NUMERIC",
|
|
1988
|
+
direction: "higher_is_better",
|
|
1989
|
+
threshold: options.threshold,
|
|
1990
|
+
async evaluate(args) {
|
|
1991
|
+
const actual = await resolveActualText(options.actual, args);
|
|
1992
|
+
const expected = await resolveExpected(options.expected, args);
|
|
1993
|
+
if (expected === void 0) {
|
|
1994
|
+
return EvalOutcome.invalid("No expected value provided for semantic similarity.");
|
|
1995
|
+
}
|
|
1996
|
+
if (typeof expected !== "string") {
|
|
1997
|
+
return EvalOutcome.invalid("Semantic similarity expected value must be a string.");
|
|
1998
|
+
}
|
|
1999
|
+
const [actualEmbedding, expectedEmbedding] = await Promise.all([
|
|
2000
|
+
embedText(options.model, actual),
|
|
2001
|
+
embedText(options.model, expected)
|
|
2002
|
+
]);
|
|
2003
|
+
const score = cosineSimilarity(actualEmbedding.vector, expectedEmbedding.vector);
|
|
2004
|
+
return score >= options.threshold ? EvalOutcome.pass(score) : EvalOutcome.fail(score, { comment: `Similarity below threshold ${options.threshold}.` });
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
function llmJudge(options) {
|
|
2009
|
+
const extractor = new ExtractorBuilder(options.model, options.schema).instructions(
|
|
2010
|
+
options.instructions ?? "Judge the eval case by the requested schema. Submit the judgment using the schema."
|
|
2011
|
+
).retries(options.retries ?? 0).build();
|
|
2012
|
+
return {
|
|
2013
|
+
name: options.name ?? "llm_judge",
|
|
2014
|
+
required: options.required ?? true,
|
|
2015
|
+
async evaluate(args) {
|
|
2016
|
+
try {
|
|
2017
|
+
const result = await extractor.extractWithUsage(
|
|
2018
|
+
await resolveJudgePrompt(options.prompt, args)
|
|
2019
|
+
);
|
|
2020
|
+
return options.passes(result.data) ? EvalOutcome.pass(result.data, { usage: result.usage }) : EvalOutcome.fail(result.data, { usage: result.usage });
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
function llmScore(options) {
|
|
2028
|
+
const criteria = Array.isArray(options.criteria) ? options.criteria.join("\n") : options.criteria;
|
|
2029
|
+
const extractor = new ExtractorBuilder(
|
|
2030
|
+
options.model,
|
|
2031
|
+
z2.object({
|
|
2032
|
+
score: z2.number(),
|
|
2033
|
+
feedback: z2.string()
|
|
2034
|
+
})
|
|
2035
|
+
).instructions(
|
|
2036
|
+
options.instructions ?? `Score the eval case against these criteria:
|
|
2037
|
+
${criteria}
|
|
2038
|
+
|
|
2039
|
+
Return a score between 0 and 1 and brief feedback.`
|
|
2040
|
+
).retries(options.retries ?? 0).build();
|
|
2041
|
+
return {
|
|
2042
|
+
name: options.name ?? "llm_score",
|
|
2043
|
+
required: options.required ?? true,
|
|
2044
|
+
dataType: "NUMERIC",
|
|
2045
|
+
projectScore: (score) => score.score,
|
|
2046
|
+
direction: "higher_is_better",
|
|
2047
|
+
threshold: options.threshold,
|
|
2048
|
+
async evaluate(args) {
|
|
2049
|
+
try {
|
|
2050
|
+
const result = await extractor.extractWithUsage(
|
|
2051
|
+
await resolveJudgePrompt(options.prompt, args)
|
|
2052
|
+
);
|
|
2053
|
+
const score = result.data;
|
|
2054
|
+
if (score.score < 0 || score.score > 1) {
|
|
2055
|
+
return EvalOutcome.invalid(`Score ${score.score} outside valid range [0, 1].`, {
|
|
2056
|
+
score,
|
|
2057
|
+
usage: result.usage
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
return score.score >= options.threshold ? EvalOutcome.pass(score, { comment: score.feedback, usage: result.usage }) : EvalOutcome.fail(score, { comment: score.feedback, usage: result.usage });
|
|
2061
|
+
} catch (error) {
|
|
2062
|
+
return EvalOutcome.invalid(errorMessage(error));
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
// src/evals/suite.ts
|
|
2069
|
+
function defineEvalCases(cases) {
|
|
2070
|
+
return cases;
|
|
2071
|
+
}
|
|
2072
|
+
function defineEvalSuite(options) {
|
|
2073
|
+
if (options !== void 0) return options;
|
|
2074
|
+
return {
|
|
2075
|
+
defineMetric(metric) {
|
|
2076
|
+
return metric;
|
|
2077
|
+
}
|
|
2078
|
+
};
|
|
1451
2079
|
}
|
|
1452
2080
|
export {
|
|
2081
|
+
EvalAssertionError,
|
|
1453
2082
|
EvalOutcome,
|
|
2083
|
+
abstention,
|
|
1454
2084
|
agentEvalTarget,
|
|
1455
2085
|
answerRelevancy,
|
|
2086
|
+
assertEvalOutcomes,
|
|
2087
|
+
assertEvalTotals,
|
|
1456
2088
|
contains,
|
|
2089
|
+
containsAll,
|
|
2090
|
+
containsAny,
|
|
1457
2091
|
defaultEvalTraceSelector,
|
|
2092
|
+
defineEvalCases,
|
|
2093
|
+
defineEvalSuite,
|
|
1458
2094
|
defineMetric,
|
|
2095
|
+
doesNotMatch,
|
|
2096
|
+
evalExitCode,
|
|
1459
2097
|
exactMatch,
|
|
1460
2098
|
faithfulness,
|
|
1461
2099
|
gEval,
|
|
@@ -1464,10 +2102,17 @@ export {
|
|
|
1464
2102
|
knowledgeRetention,
|
|
1465
2103
|
llmJudge,
|
|
1466
2104
|
llmScore,
|
|
2105
|
+
matches,
|
|
2106
|
+
maxLength,
|
|
2107
|
+
notContains,
|
|
2108
|
+
printEvalResult,
|
|
1467
2109
|
projectEvalOutcome,
|
|
1468
2110
|
promptAlignment,
|
|
2111
|
+
requiredFields,
|
|
1469
2112
|
resolveEvalTraceRef,
|
|
2113
|
+
runEvalCli,
|
|
1470
2114
|
runEvalSuite,
|
|
2115
|
+
selectPromptOutput,
|
|
1471
2116
|
semanticSimilarity,
|
|
1472
2117
|
summarization,
|
|
1473
2118
|
turnRelevancy
|