@anvia/core 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/agent/index.d.ts +11 -11
  2. package/dist/agent/index.js +9 -9
  3. package/dist/{agent-Q7FT1xmG.d.ts → agent-x3YTyv5i.d.ts} +8 -7
  4. package/dist/audio-generation/index.d.ts +1 -1
  5. package/dist/{chunk-PJDL5UT2.js → chunk-ADH7NNCS.js} +63 -1
  6. package/dist/chunk-ADH7NNCS.js.map +1 -0
  7. package/dist/{chunk-3IYTCGHS.js → chunk-BALBBVI3.js} +14 -7
  8. package/dist/{chunk-3IYTCGHS.js.map → chunk-BALBBVI3.js.map} +1 -1
  9. package/dist/{chunk-N3YV3WCO.js → chunk-DALYP4PX.js} +21 -4
  10. package/dist/chunk-DALYP4PX.js.map +1 -0
  11. package/dist/{chunk-6X2UTX3L.js → chunk-HYUYZEAJ.js} +2 -2
  12. package/dist/{chunk-SWUH4G2I.js → chunk-I6XOQ4LK.js} +2 -2
  13. package/dist/{chunk-WHAPIIEZ.js → chunk-JI5YZWNT.js} +5 -5
  14. package/dist/{chunk-F56AMXMQ.js → chunk-MC3CCKEB.js} +2 -2
  15. package/dist/{chunk-TGLNXVII.js → chunk-MRQLEK2B.js} +3 -3
  16. package/dist/{chunk-MTGNA4OS.js → chunk-Q25IWLBT.js} +2 -2
  17. package/dist/{chunk-ZNTIUOKL.js → chunk-UQX6EXWG.js} +2 -2
  18. package/dist/{chunk-ZSTUMG7G.js → chunk-YLMDBQO4.js} +5 -5
  19. package/dist/completion/index.d.ts +3 -3
  20. package/dist/completion/index.js +11 -5
  21. package/dist/{errors-Bf661V89.d.ts → errors-2moLDuuN.d.ts} +2 -2
  22. package/dist/evals/index.d.ts +124 -10
  23. package/dist/evals/index.js +1021 -29
  24. package/dist/evals/index.js.map +1 -1
  25. package/dist/extractor/index.d.ts +8 -8
  26. package/dist/extractor/index.js +10 -10
  27. package/dist/guardrails/index.d.ts +1 -1
  28. package/dist/hooks/index.d.ts +3 -3
  29. package/dist/image-generation/index.d.ts +1 -1
  30. package/dist/{index-XTUEMWhU.d.ts → index-BxSbmOMr.d.ts} +4 -2
  31. package/dist/index.d.ts +13 -13
  32. package/dist/index.js +18 -12
  33. package/dist/internal/agent.d.ts +8 -8
  34. package/dist/internal/agent.js +7 -7
  35. package/dist/{json-D9IvOgx_.d.ts → json-BVPssigK.d.ts} +1 -1
  36. package/dist/loaders/index.d.ts +1 -1
  37. package/dist/mcp/index.d.ts +4 -4
  38. package/dist/memory/index.d.ts +4 -4
  39. package/dist/memory/index.js +4 -4
  40. package/dist/{middleware-DfMc30in.d.ts → middleware-Dp3d_pxi.d.ts} +3 -3
  41. package/dist/observability/index.d.ts +3 -3
  42. package/dist/pipeline/index.d.ts +8 -8
  43. package/dist/request/index.d.ts +8 -8
  44. package/dist/request/index.js +6 -6
  45. package/dist/skills/index.d.ts +4 -4
  46. package/dist/skills/index.js +2 -2
  47. package/dist/{think-tool-CpCKo8Q8.d.ts → think-tool-DfCMuO8e.d.ts} +1 -1
  48. package/dist/tool/index.d.ts +5 -5
  49. package/dist/tool/index.js +2 -2
  50. package/dist/{tool-Dhwg__1L.d.ts → tool-Cohdv5GC.d.ts} +1 -1
  51. package/dist/transcription/index.d.ts +1 -1
  52. package/dist/{types-fBv8mhvP.d.ts → types-04LqWrmy.d.ts} +1 -1
  53. package/dist/{types-DBHzPsbx.d.ts → types-BrLbKnEz.d.ts} +1 -1
  54. package/dist/{types-BCA8p0sb.d.ts → types-D9JpeAPF.d.ts} +1 -1
  55. package/dist/{types-XjIh933x.d.ts → types-G12i56s2.d.ts} +26 -1
  56. package/dist/{types-BBEf34DP.d.ts → types-JJCxRnaZ.d.ts} +2 -2
  57. package/dist/{types-BrxxAtcJ.d.ts → types-O-abTIin.d.ts} +1 -1
  58. package/dist/ui/index.d.ts +2 -1
  59. package/dist/ui/index.js +2 -2
  60. package/dist/vector-store/index.d.ts +4 -4
  61. package/package.json +1 -1
  62. package/dist/chunk-N3YV3WCO.js.map +0 -1
  63. package/dist/chunk-PJDL5UT2.js.map +0 -1
  64. /package/dist/{chunk-6X2UTX3L.js.map → chunk-HYUYZEAJ.js.map} +0 -0
  65. /package/dist/{chunk-SWUH4G2I.js.map → chunk-I6XOQ4LK.js.map} +0 -0
  66. /package/dist/{chunk-WHAPIIEZ.js.map → chunk-JI5YZWNT.js.map} +0 -0
  67. /package/dist/{chunk-F56AMXMQ.js.map → chunk-MC3CCKEB.js.map} +0 -0
  68. /package/dist/{chunk-TGLNXVII.js.map → chunk-MRQLEK2B.js.map} +0 -0
  69. /package/dist/{chunk-MTGNA4OS.js.map → chunk-Q25IWLBT.js.map} +0 -0
  70. /package/dist/{chunk-ZNTIUOKL.js.map → chunk-UQX6EXWG.js.map} +0 -0
  71. /package/dist/{chunk-ZSTUMG7G.js.map → chunk-YLMDBQO4.js.map} +0 -0
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  ExtractorBuilder
3
- } from "../chunk-ZSTUMG7G.js";
4
- import "../chunk-WHAPIIEZ.js";
5
- import "../chunk-N3YV3WCO.js";
6
- import "../chunk-3IYTCGHS.js";
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-6X2UTX3L.js";
9
+ import "../chunk-HYUYZEAJ.js";
10
10
  import "../chunk-MELYDKWQ.js";
11
11
  import "../chunk-2ODTMRHP.js";
12
- import "../chunk-ZNTIUOKL.js";
13
- import "../chunk-TGLNXVII.js";
12
+ import "../chunk-UQX6EXWG.js";
13
+ import "../chunk-MRQLEK2B.js";
14
14
  import {
15
15
  cosineSimilarity,
16
16
  embedText
@@ -18,27 +18,15 @@ import {
18
18
  import {
19
19
  mapWithConcurrency
20
20
  } from "../chunk-OIMLU4SF.js";
21
- import "../chunk-MTGNA4OS.js";
22
- import "../chunk-F56AMXMQ.js";
23
- import "../chunk-PJDL5UT2.js";
21
+ import "../chunk-Q25IWLBT.js";
22
+ import "../chunk-MC3CCKEB.js";
23
+ import {
24
+ Usage
25
+ } from "../chunk-ADH7NNCS.js";
24
26
  import "../chunk-WQKHFADH.js";
25
27
  import "../chunk-CWUJUSOS.js";
26
28
 
27
- // src/evals/agent-target.ts
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
- z.object({
228
- score: z.number(),
229
- feedback: z.string()
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,6 +1134,81 @@ 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
1214
  const startedAt = Date.now();
@@ -276,6 +1233,7 @@ async function runEvalCase(options, testCase) {
276
1233
  } catch (error) {
277
1234
  targetError = error;
278
1235
  }
1236
+ const traceResult = await resolveTrace(options, testCase, output, targetError);
279
1237
  const metrics = [];
280
1238
  for (const metric of options.metrics) {
281
1239
  const outcome = targetError === void 0 ? await safeEvaluate(options.name, testCase, output, metric) : EvalOutcome.invalid(`Target failed: ${errorMessage(targetError)}`);
@@ -286,6 +1244,8 @@ async function runEvalCase(options, testCase) {
286
1244
  targetError,
287
1245
  metric,
288
1246
  outcome,
1247
+ trace: traceResult.trace,
1248
+ traceError: traceResult.error,
289
1249
  reporters: options.reporters ?? [],
290
1250
  failOnReporterError: options.failOnReporterError === true
291
1251
  });
@@ -303,6 +1263,20 @@ async function runEvalCase(options, testCase) {
303
1263
  }
304
1264
  return result;
305
1265
  }
1266
+ async function resolveTrace(options, testCase, output, targetError) {
1267
+ try {
1268
+ const selector = options.trace ?? defaultEvalTraceSelector;
1269
+ const trace = await selector({
1270
+ suiteName: options.name,
1271
+ case: testCase,
1272
+ output,
1273
+ targetError
1274
+ });
1275
+ return trace === void 0 ? {} : { trace };
1276
+ } catch (error) {
1277
+ return { error };
1278
+ }
1279
+ }
306
1280
  async function safeEvaluate(suiteName, testCase, output, metric) {
307
1281
  try {
308
1282
  return await metric.evaluate({ suiteName, case: testCase, output });
@@ -312,6 +1286,11 @@ async function safeEvaluate(suiteName, testCase, output, metric) {
312
1286
  }
313
1287
  async function reportOutcome(args) {
314
1288
  const errors = [];
1289
+ if (args.traceError !== void 0) {
1290
+ if (args.failOnReporterError) throw args.traceError;
1291
+ errors.push(args.traceError);
1292
+ return errors;
1293
+ }
315
1294
  for (const reporter of args.reporters) {
316
1295
  try {
317
1296
  await reporter.report({
@@ -319,6 +1298,7 @@ async function reportOutcome(args) {
319
1298
  case: args.testCase,
320
1299
  output: args.output,
321
1300
  targetError: args.targetError,
1301
+ trace: args.trace,
322
1302
  metric: args.metric,
323
1303
  outcome: args.outcome
324
1304
  });
@@ -347,12 +1327,24 @@ function countOutcomes(results) {
347
1327
  export {
348
1328
  EvalOutcome,
349
1329
  agentEvalTarget,
1330
+ answerRelevancy,
350
1331
  contains,
1332
+ defaultEvalTraceSelector,
351
1333
  defineMetric,
352
1334
  exactMatch,
1335
+ faithfulness,
1336
+ gEval,
1337
+ hallucination,
1338
+ jsonCorrectness,
1339
+ knowledgeRetention,
353
1340
  llmJudge,
354
1341
  llmScore,
1342
+ projectEvalOutcome,
1343
+ promptAlignment,
1344
+ resolveEvalTraceRef,
355
1345
  runEvalSuite,
356
- semanticSimilarity
1346
+ semanticSimilarity,
1347
+ summarization,
1348
+ turnRelevancy
357
1349
  };
358
1350
  //# sourceMappingURL=index.js.map