@cogitator-ai/evals 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +255 -54
  2. package/dist/assertions/error-rate.d.ts +8 -0
  3. package/dist/assertions/error-rate.d.ts.map +1 -0
  4. package/dist/assertions/error-rate.js +27 -0
  5. package/dist/assertions/error-rate.js.map +1 -0
  6. package/dist/assertions/index.d.ts +3 -0
  7. package/dist/assertions/index.d.ts.map +1 -1
  8. package/dist/assertions/index.js +1 -0
  9. package/dist/assertions/index.js.map +1 -1
  10. package/dist/assertions/regression.d.ts.map +1 -1
  11. package/dist/assertions/regression.js +3 -1
  12. package/dist/assertions/regression.js.map +1 -1
  13. package/dist/assertions/threshold.d.ts.map +1 -1
  14. package/dist/assertions/threshold.js +4 -1
  15. package/dist/assertions/threshold.js.map +1 -1
  16. package/dist/datasets/csv-loader.d.ts +5 -0
  17. package/dist/datasets/csv-loader.d.ts.map +1 -1
  18. package/dist/datasets/csv-loader.js +89 -7
  19. package/dist/datasets/csv-loader.js.map +1 -1
  20. package/dist/datasets/dataset.d.ts +11 -2
  21. package/dist/datasets/dataset.d.ts.map +1 -1
  22. package/dist/datasets/dataset.js +42 -13
  23. package/dist/datasets/dataset.js.map +1 -1
  24. package/dist/datasets/issues.d.ts +4 -0
  25. package/dist/datasets/issues.d.ts.map +1 -0
  26. package/dist/datasets/issues.js +7 -0
  27. package/dist/datasets/issues.js.map +1 -0
  28. package/dist/datasets/jsonl-loader.d.ts.map +1 -1
  29. package/dist/datasets/jsonl-loader.js +6 -1
  30. package/dist/datasets/jsonl-loader.js.map +1 -1
  31. package/dist/eval-builder.d.ts +3 -0
  32. package/dist/eval-builder.d.ts.map +1 -1
  33. package/dist/eval-builder.js +7 -0
  34. package/dist/eval-builder.js.map +1 -1
  35. package/dist/eval-comparison.d.ts +7 -0
  36. package/dist/eval-comparison.d.ts.map +1 -1
  37. package/dist/eval-comparison.js +42 -12
  38. package/dist/eval-comparison.js.map +1 -1
  39. package/dist/eval-suite.d.ts +55 -8
  40. package/dist/eval-suite.d.ts.map +1 -1
  41. package/dist/eval-suite.js +254 -53
  42. package/dist/eval-suite.js.map +1 -1
  43. package/dist/index.d.ts +14 -8
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +6 -2
  46. package/dist/index.js.map +1 -1
  47. package/dist/matchers.d.ts +68 -0
  48. package/dist/matchers.d.ts.map +1 -0
  49. package/dist/matchers.js +112 -0
  50. package/dist/matchers.js.map +1 -0
  51. package/dist/metrics/custom.d.ts +19 -7
  52. package/dist/metrics/custom.d.ts.map +1 -1
  53. package/dist/metrics/custom.js +5 -0
  54. package/dist/metrics/custom.js.map +1 -1
  55. package/dist/metrics/index.d.ts +8 -4
  56. package/dist/metrics/index.d.ts.map +1 -1
  57. package/dist/metrics/index.js +4 -2
  58. package/dist/metrics/index.js.map +1 -1
  59. package/dist/metrics/llm-judge.d.ts +20 -1
  60. package/dist/metrics/llm-judge.d.ts.map +1 -1
  61. package/dist/metrics/llm-judge.js +162 -20
  62. package/dist/metrics/llm-judge.js.map +1 -1
  63. package/dist/metrics/matching.d.ts +28 -0
  64. package/dist/metrics/matching.d.ts.map +1 -0
  65. package/dist/metrics/matching.js +124 -0
  66. package/dist/metrics/matching.js.map +1 -0
  67. package/dist/metrics/retrieval.d.ts +21 -0
  68. package/dist/metrics/retrieval.d.ts.map +1 -0
  69. package/dist/metrics/retrieval.js +108 -0
  70. package/dist/metrics/retrieval.js.map +1 -0
  71. package/dist/metrics/statistical.d.ts +28 -0
  72. package/dist/metrics/statistical.d.ts.map +1 -1
  73. package/dist/metrics/statistical.js +61 -0
  74. package/dist/metrics/statistical.js.map +1 -1
  75. package/dist/metrics/tool-calls.d.ts +43 -0
  76. package/dist/metrics/tool-calls.d.ts.map +1 -0
  77. package/dist/metrics/tool-calls.js +106 -0
  78. package/dist/metrics/tool-calls.js.map +1 -0
  79. package/dist/metrics/types.d.ts +53 -5
  80. package/dist/metrics/types.d.ts.map +1 -1
  81. package/dist/metrics/types.js.map +1 -1
  82. package/dist/reporters/ci.d.ts +4 -0
  83. package/dist/reporters/ci.d.ts.map +1 -1
  84. package/dist/reporters/ci.js +5 -1
  85. package/dist/reporters/ci.js.map +1 -1
  86. package/dist/reporters/console.d.ts.map +1 -1
  87. package/dist/reporters/console.js +11 -4
  88. package/dist/reporters/console.js.map +1 -1
  89. package/dist/reporters/csv.d.ts.map +1 -1
  90. package/dist/reporters/csv.js +5 -2
  91. package/dist/reporters/csv.js.map +1 -1
  92. package/dist/reporters/format.d.ts +19 -0
  93. package/dist/reporters/format.d.ts.map +1 -0
  94. package/dist/reporters/format.js +31 -0
  95. package/dist/reporters/format.js.map +1 -0
  96. package/dist/reporters/github.d.ts +11 -0
  97. package/dist/reporters/github.d.ts.map +1 -0
  98. package/dist/reporters/github.js +95 -0
  99. package/dist/reporters/github.js.map +1 -0
  100. package/dist/reporters/index.d.ts +44 -20
  101. package/dist/reporters/index.d.ts.map +1 -1
  102. package/dist/reporters/index.js +14 -3
  103. package/dist/reporters/index.js.map +1 -1
  104. package/dist/reporters/junit.d.ts +8 -0
  105. package/dist/reporters/junit.d.ts.map +1 -0
  106. package/dist/reporters/junit.js +107 -0
  107. package/dist/reporters/junit.js.map +1 -0
  108. package/dist/schema.d.ts +25 -0
  109. package/dist/schema.d.ts.map +1 -1
  110. package/dist/schema.js +29 -0
  111. package/dist/schema.js.map +1 -1
  112. package/dist/vitest.d.ts +35 -0
  113. package/dist/vitest.d.ts.map +1 -0
  114. package/dist/vitest.js +85 -0
  115. package/dist/vitest.js.map +1 -0
  116. package/package.json +17 -3
package/README.md CHANGED
@@ -10,20 +10,24 @@ pnpm add @cogitator-ai/evals
10
10
  # Optional dependencies
11
11
  pnpm add papaparse # CSV dataset loading
12
12
  pnpm add @cogitator-ai/core # agent targets and LLM-as-judge metrics
13
+ pnpm add -D vitest # describeEval from @cogitator-ai/evals/vitest
13
14
  ```
14
15
 
15
- Website docs: [Evals](https://cogitator.app/docs/evals), [Datasets](https://cogitator.app/docs/evals/datasets), [Metrics](https://cogitator.app/docs/evals/metrics), [Assertions](https://cogitator.app/docs/evals/assertions), [Comparison](https://cogitator.app/docs/evals/comparison), [Reporters](https://cogitator.app/docs/evals/reporters).
16
+ Website docs: [Evals](https://cogitator.app/docs/evals), [Datasets](https://cogitator.app/docs/evals/datasets), [Metrics](https://cogitator.app/docs/evals/metrics), [Assertions](https://cogitator.app/docs/evals/assertions), [Agents](https://cogitator.app/docs/evals/agents), [Comparison](https://cogitator.app/docs/evals/comparison), [Reporters](https://cogitator.app/docs/evals/reporters).
16
17
 
17
18
  ## Features
18
19
 
19
20
  - **EvalSuite** — Run datasets against agents or plain functions with configurable concurrency, timeouts, and retries
20
21
  - **7 Deterministic Metrics** - exactMatch, contains, containsAll, containsAny, similarity, regex, jsonSchema (Zod)
21
- - **5 LLM-as-Judge Metrics** — faithfulness, relevance, coherence, helpfulness, custom llmMetric
22
- - **3 Statistical Metrics** — latency, cost, tokenUsage with full percentile breakdowns
22
+ - **6 LLM-as-Judge Metrics** - faithfulness, relevance, coherence, helpfulness, contextRelevance, custom llmMetric
23
+ - **4 Statistical Metrics** - latency, cost, tokenUsage with full percentile breakdowns, and passAtK over trials
23
24
  - **Custom Metrics** — `metric()` factory for anything domain-specific
25
+ - **Agent Evaluation** - `toolCalls()` and `trajectory()` against expected tool calls, `retrievalRecall()`, `retrievalPrecision()`, `retrievalMRR()` and the `contextRelevance()` judge against expected passages, multi-turn cases on one conversation
26
+ - **Trials and pass@k** - run each case several times and score how often it passes with `passAtK()`
24
27
  - **Assertions** — threshold, noRegression, custom assertion with auto-detection of lower-is-better metrics
25
28
  - **A/B Testing** — EvalComparison with paired t-test and McNemar's test for statistical significance
26
- - **4 Reporters** — console (colored table), JSON, CSV, CI (exit code on failure)
29
+ - **6 Reporters** - console (colored table), JSON, CSV, JUnit XML, GitHub Actions annotations and job summary, CI (exit code on failure)
30
+ - **Vitest** - `describeEval` from `@cogitator-ai/evals/vitest` runs a suite as a test file, one test per case plus one for the assertions, and `evalMatchers` from `@cogitator-ai/evals/matchers` (Vitest 5) adds `toHaveCalledTool`, `toFollowTrajectory`, `toHaveRetrieved` and `toScoreAtLeast`
27
31
  - **Builder API** — Fluent `EvalBuilder` for composable eval pipelines
28
32
  - **Baseline Workflow** — Save baselines, compare against them, catch regressions in CI
29
33
  - **Zod Validation** — Type-safe configuration with runtime checks
@@ -62,7 +66,7 @@ result.report('console');
62
66
  result.saveBaseline('./baseline.json');
63
67
  ```
64
68
 
65
- A target is either `{ fn: (input, { signal, case, attempt }) => Promise<string> }` or `{ agent, cogitator }`, which runs the agent with `cogitator.run(agent, { input, context, signal })` and records its usage for the statistical metrics. Defaults: `concurrency` 5, `timeout` 30 000 ms (minimum 1 000), `retries` 0.
69
+ A target is either `{ fn: (input, { signal, case, attempt, turn, history }) => Promise<string> }` or `{ agent, cogitator, retrieval? }`, which runs the agent with `cogitator.run(agent, { input, context, signal, onToolResult })` and records its usage, tool calls, tool results and retrieved passages (see [Agent Evaluation](#agent-evaluation)). Defaults: `concurrency` 5, `timeout` 30 000 ms (minimum 1 000), `retries` 0, `trials` 1.
66
70
 
67
71
  A case that throws or exceeds `timeout` is retried up to `retries` times. A timed-out attempt is cancelled through its `signal`: an agent target's run is aborted, and a `fn` target should pass the signal on to its model call. The suite waits for the cancelled attempt to stop (at most another `timeout`) before it retries, so attempts of one case never overlap and `concurrency` holds. If every attempt fails, its result has an empty `output` and an `error` message describing the last failure.
68
72
 
@@ -82,25 +86,27 @@ const suite = new EvalSuite({
82
86
 
83
87
  `suite.run(options?: EvalRunOptions)` takes `{ maxCases?: number, signal?: AbortSignal }`: with `maxCases` only the first N cases of the dataset run, and the results, aggregates, assertions and `stats.total` cover just those cases. Aborting `signal` cancels the attempts in flight, starts no further case, and `run()` rejects with the signal's reason.
84
88
 
89
+ A case whose every attempt failed is not scored: its empty output costs no judge calls and counts as no 0, the aggregates cover the cases that answered (`aggregated.<metric>.count` says how many), and `maxErrorRate(rate)` fails a run where too many cases errored. An agent target's case result carries `truncated`, `iterationLimitReached` and `paused` when the run ended that way.
90
+
85
91
  A failure never drops a case. A metric that throws or returns a score that is not a finite number (such as NaN) scores 0 for that case with the reason in `score.error`, and an `onProgress` callback that throws is reported as a process warning.
86
92
 
87
93
  `result.stats` holds:
88
94
 
89
- | Field | Meaning |
90
- | -------------- | ------------------------------------------------------------ |
91
- | `total` | Cases run |
92
- | `errors` | Cases whose every attempt failed or timed out |
93
- | `metricErrors` | Metric scores that failed and count as 0 |
94
- | `duration` | Wall time of the run in ms |
95
- | `cost` | USD spent, `targetCost + judgeCost` |
96
- | `targetCost` | USD the target spent, over every attempt that reported usage |
97
- | `judgeCost` | USD the LLM judge spent |
95
+ | Field | Meaning |
96
+ | -------------- | ---------------------------------------------------------------------------------------------------------------------- |
97
+ | `total` | Cases run |
98
+ | `errors` | Cases whose every attempt failed or timed out (with several trials, a case counts as errored when every trial errored) |
99
+ | `metricErrors` | Metric scores that failed and count as 0, over every trial |
100
+ | `duration` | Wall time of the run in ms |
101
+ | `cost` | USD spent, `targetCost + judgeCost` |
102
+ | `targetCost` | USD the target spent, over every attempt and trial that reported usage |
103
+ | `judgeCost` | USD the LLM judge spent |
98
104
 
99
105
  ---
100
106
 
101
107
  ## Datasets
102
108
 
103
- Datasets are immutable collections of eval cases. Each case has an `input`, optional `expected`, optional `context`, and optional `metadata`.
109
+ Datasets are immutable collections of eval cases. Each case has an `input`, optional `expected`, optional `context`, and optional `metadata`. Agent cases may also have `turns` (further user turns run on the same conversation), `expectedToolCalls` (`{ name, arguments? }[]`) and `expectedPassages` (ids or texts the retrieval should surface).
104
110
 
105
111
  ### From inline data
106
112
 
@@ -134,7 +140,15 @@ Requires `papaparse` as an optional dependency.
134
140
  const dataset = await Dataset.fromCsv('./evals/qa.csv');
135
141
  ```
136
142
 
137
- CSV must have an `input` column. Optional columns: `expected`, `metadata.*`, `context.*`.
143
+ CSV must have an `input` column. Optional columns: `expected`, `metadata.*`, `context.*`, and `turns`, `expectedToolCalls` and `expectedPassages`, whose cells hold JSON arrays (CSV-quoted, an empty cell leaves the field out):
144
+
145
+ ```csv
146
+ input,turns,expectedToolCalls,expectedPassages
147
+ Book a table,"[""For two, please""]","[{""name"":""book_table"",""arguments"":{""guests"":2}}]",
148
+ How long do refunds take?,,"[{""name"":""rag_search""}]","[""refund is issued within 5 days""]"
149
+ ```
150
+
151
+ Both loaders name the line of the file a case comes from when the case schema refuses it (`Invalid case at line 3: input: ...`), the CSV loader counting quoted fields that span lines. The CSV loader splits on commas and throws on a malformed row, a quote never closed for one, with its line.
138
152
 
139
153
  ### Transformations
140
154
 
@@ -142,6 +156,7 @@ CSV must have an `input` column. Optional columns: `expected`, `metadata.*`, `co
142
156
  const filtered = dataset.filter((c) => c.expected !== undefined);
143
157
  const sampled = dataset.sample(50);
144
158
  const shuffled = dataset.shuffle();
159
+ const nightly = dataset.sample(50, { seed: 'nightly' }); // the same cases on every run
145
160
  ```
146
161
 
147
162
  All transformations return new `Dataset` instances — the original is never mutated.
@@ -208,13 +223,14 @@ const metrics = [
208
223
 
209
224
  Metrics scored by an LLM judge (0.0 to 1.0). They require a `judge` config on the suite and a Cogitator to run the judge agent: `judge.cogitator`, or the `cogitator` of an agent target. Without one, the suite throws when it is created.
210
225
 
211
- | Metric | Evaluates |
212
- | -------------- | --------------------------------------- |
213
- | `faithfulness` | Factual accuracy relative to input |
214
- | `relevance` | How on-topic the response is |
215
- | `coherence` | Logical structure and readability |
216
- | `helpfulness` | Practical usefulness to the user |
217
- | `llmMetric` | Custom prompt — you define the criteria |
226
+ | Metric | Evaluates |
227
+ | ------------------ | -------------------------------------------- |
228
+ | `faithfulness` | Factual accuracy relative to input |
229
+ | `relevance` | How on-topic the response is |
230
+ | `coherence` | Logical structure and readability |
231
+ | `helpfulness` | Practical usefulness to the user |
232
+ | `contextRelevance` | Whether the retrieved passages fit the input |
233
+ | `llmMetric` | Custom prompt, you define the criteria |
218
234
 
219
235
  ```typescript
220
236
  import { faithfulness, relevance, llmMetric } from '@cogitator-ai/evals';
@@ -240,7 +256,9 @@ The judge reads the case's `context` too, so `faithfulness()` can check a respon
240
256
  Dataset.from([{ input: 'Write the lede', context: { dossier: dossierText } }]);
241
257
  ```
242
258
 
243
- The `judge` option is a `JudgeConfigInput`: `{ model, temperature?, maxTokens?, cogitator? }`, with `temperature` defaulting to `0`.
259
+ When the run retrieved passages (see [Retrieval](#retrieval)), `faithfulness()` checks the response against them instead of `context`. `contextRelevance()` judges the input against the retrieved passages alone, or against `context` when nothing was retrieved, and scores 0 without a judge call when the case has neither.
260
+
261
+ The `judge` option is a `JudgeConfigInput`: `{ model, temperature?, maxTokens?, cogitator?, cache?, concurrency? }`, with `temperature` defaulting to `0`. `cache: true` reuses a verdict for the same model, settings and prompt in memory (a `JudgeCache` with `get` and `set` keeps them across runs), and `concurrency` caps the judge calls running at once.
244
262
 
245
263
  The judge answers with `{ "score", "reasoning" }` JSON. A verdict in a code fence or with the score as a numeric string is read as well, and an answer without JSON falls back to its last labelled score (`Score: 0.4`, `score is 4/5`), never to a stray number in the prose. When no verdict can be read, or the judge call fails, the metric scores 0 with the reason in `score.error`. The judge's usage is reported on each score and counted in `stats.judgeCost`.
246
264
 
@@ -261,7 +279,7 @@ const suite = new EvalSuite({
261
279
 
262
280
  ### Custom
263
281
 
264
- Build domain-specific metrics with the `metric()` factory.
282
+ Build domain-specific metrics with the `metric()` factory. `evaluate` gets `input`, `output`, `expected` and `context`, the run's `toolCalls`, `toolResults`, `retrieved` and `turns`, and the whole case result as `result`.
265
283
 
266
284
  ```typescript
267
285
  import { metric } from '@cogitator-ai/evals';
@@ -285,6 +303,108 @@ Scores are automatically clamped to [0, 1]. A score that is not a finite number
285
303
 
286
304
  ---
287
305
 
306
+ ## Agent Evaluation
307
+
308
+ An agent target records what the agent did, not only what it answered. Each case result carries `toolCalls` (every call of the run), `toolResults` (`{ callId, name, result?, error? }`, the raw values the run's `onToolResult` reported) and `retrieved` (`{ id?, content, score?, source? }` passages the tool results surfaced), summed over every turn of the case.
309
+
310
+ ### Tool calls and trajectory
311
+
312
+ ```typescript
313
+ import { EvalSuite, Dataset, toolCalls, trajectory } from '@cogitator-ai/evals';
314
+
315
+ const dataset = Dataset.from([
316
+ {
317
+ input: 'What is the weather in Paris?',
318
+ expectedToolCalls: [{ name: 'get_weather', arguments: { city: 'Paris' } }],
319
+ },
320
+ ]);
321
+
322
+ const suite = new EvalSuite({
323
+ dataset,
324
+ target: { agent, cogitator },
325
+ metrics: [toolCalls(), trajectory({ order: 'in-order' })],
326
+ });
327
+ ```
328
+
329
+ `toolCalls({ arguments?, score? })` pairs the actual calls with the expected ones as multisets, each expected call taking at most one actual call. `arguments: 'subset'` (default) wants the expected arguments in the call with equal values, other arguments allowed, `'exact'` wants equal arguments and `'ignore'` compares names only. An expected call without `arguments` matches on its name. `score` is `'f1'` (default), `'recall'` or `'precision'`, and `details` lists the missing and unexpected calls.
330
+
331
+ `trajectory({ order? })` compares the sequence of tool names: `'exact'` (default) scores 1 for the same sequence and 0 otherwise, `'in-order'` scores the longest common subsequence over the longer sequence, and `'any-order'` the names both share over the longer sequence.
332
+
333
+ Both score 0 with `details: 'no expected tool calls provided'` on a case without `expectedToolCalls`, as `exactMatch()` does without `expected`. A case expecting no calls (`expectedToolCalls: []`) scores 1 when the agent made none.
334
+
335
+ ### Retrieval
336
+
337
+ ```typescript
338
+ import {
339
+ retrievalRecall,
340
+ retrievalPrecision,
341
+ retrievalMRR,
342
+ contextRelevance,
343
+ } from '@cogitator-ai/evals';
344
+ import { createSearchTool } from '@cogitator-ai/rag';
345
+
346
+ const agent = new Agent({ name: 'kb', model, tools: [tool(createSearchTool(pipeline))] });
347
+
348
+ const suite = new EvalSuite({
349
+ dataset: Dataset.from([
350
+ { input: 'How long do refunds take?', expectedPassages: ['refund is issued within 5 days'] },
351
+ ]),
352
+ target: { agent, cogitator },
353
+ metrics: [retrievalRecall(), retrievalPrecision(), retrievalMRR(), contextRelevance()],
354
+ judge: { model: 'google/gemini-3.5-flash-lite' },
355
+ });
356
+ ```
357
+
358
+ The target's `retrieval` reads the passages out of each successful tool result. The default, `ragSearchPassages`, reads the `rag_search` tool of `@cogitator-ai/rag` (`{ results: [{ content, chunkId, score, source, ... }] }`, the `id` of a result or else its `chunkId` as the passage id) and any other result of that shape. Pass your own for other tools:
359
+
360
+ ```typescript
361
+ target: {
362
+ agent,
363
+ cogitator,
364
+ retrieval: ({ name, result }) =>
365
+ name === 'search_docs' ? (result as Doc[]).map((d) => ({ id: d.slug, content: d.body })) : undefined,
366
+ },
367
+ ```
368
+
369
+ A retrieved passage matches an expected one by an equal `id`, by an equal text once case and whitespace are normalized, or when one normalized text contains the other and the contained one has at least three words, so a short id or a single word never matches every passage that happens to hold it. `retrievalRecall()` scores the share of expected passages retrieved, `retrievalPrecision()` the share of retrieved passages expected, and `retrievalMRR()` the reciprocal rank of the first match (passages rank in the order they came). They score 0 on a case without `expectedPassages`, and a case expecting none scores 1 when nothing was retrieved.
370
+
371
+ ### Multi-turn cases
372
+
373
+ ```typescript
374
+ const cogitator = new Cogitator({ llm, memory: { adapter: 'memory' } });
375
+
376
+ Dataset.from([
377
+ {
378
+ input: 'My name is Ada.',
379
+ turns: ['What is my name?'],
380
+ expected: 'Ada',
381
+ },
382
+ ]);
383
+ ```
384
+
385
+ An agent case with `turns` runs `input`, then each turn, on one thread of its own (`threadId` `eval-<uuid>`, a fresh one for every attempt and trial). The Cogitator's memory carries the conversation from turn to turn, so it needs a memory adapter. The case `output` is the last answer, `toolCalls`, `toolResults`, `retrieved` and `usage` cover every turn, and `turns` lists each `{ input, output, toolCalls? }`. A turn that fails fails the case with its error, `turns` then ending with the failed one, and a cancelled or timed-out case starts no further turn. LLM judges read a multi-turn case as the conversation, each turn's input and answer, with the last user message marked as the one the response answers. A `fn` target is called once per turn, with `context.turn` (0 for `input`) and `context.history` (the earlier `{ input, output }` turns).
386
+
387
+ ### Trials and pass@k
388
+
389
+ ```typescript
390
+ import { passAtK, exactMatch, threshold } from '@cogitator-ai/evals';
391
+
392
+ const suite = new EvalSuite({
393
+ dataset,
394
+ target: { agent, cogitator },
395
+ metrics: [exactMatch()],
396
+ statisticalMetrics: [passAtK('exactMatch', { k: 1 }), passAtK('exactMatch', { k: 5 })],
397
+ assertions: [threshold('passAtK(exactMatch,5)', 0.9)],
398
+ trials: 5,
399
+ });
400
+ ```
401
+
402
+ `trials` (a positive integer, 1 by default) runs each case that many times. Trials share the suite's concurrency, timeout, retries, abort signal and `maxCases`, and each is scored on its own. The case result is the first trial that answered (the first one when every trial errored), so it carries an error only when all trials failed, with every trial in `trials` (each with its `scores`), and the aggregate of a metric takes the mean of a case's scored trials as that case's score. `stats.errors` counts a case as errored when every trial errored, while costs and `metricErrors` cover every trial. `latency()`, `cost()` and `tokenUsage()` see every trial as one value each, `passAtK()` sees the cases with their trials. `onProgress` fires once per case, after its last trial. `EvalComparison` takes `trials` too, comparing each case's mean over its trials.
403
+
404
+ `passAtK(metric, { k, threshold = 1 })`, named `passAtK(<metric>,<k>)`, estimates per case the chance that at least one of `k` trials scores `threshold` or more, with the unbiased estimator `1 - C(n - c, k) / C(n, k)` (`n` trials, `c` passing), and averages it over the cases with at least one scored trial. An errored trial counts as a failure. The suite throws when it is created if `k` exceeds `trials` or the metric is not among its `metrics`.
405
+
406
+ ---
407
+
288
408
  ## Assertions
289
409
 
290
410
  Assertions check aggregated metrics after a suite run and produce pass/fail results.
@@ -306,6 +426,16 @@ const assertions = [
306
426
 
307
427
  Latency, cost and tokenUsage metrics (and names ending in `Duration` or `Latency`) are lower-is-better: the assertion passes when the value is at or below the threshold. `noRegression` uses the same rule.
308
428
 
429
+ ### maxErrorRate
430
+
431
+ ```typescript
432
+ import { maxErrorRate } from '@cogitator-ai/evals';
433
+
434
+ const assertions = [threshold('exactMatch', 0.9), maxErrorRate(0.05)];
435
+ ```
436
+
437
+ Passes while at most `rate` (0 to 1) of the cases errored. Errored cases are not scored, so pair metric thresholds with it.
438
+
309
439
  ### noRegression
310
440
 
311
441
  Compares current results against a saved baseline file.
@@ -357,6 +487,7 @@ const comparison = new EvalComparison({
357
487
  },
358
488
  metrics: [exactMatch(), contains()],
359
489
  concurrency: 5,
490
+ trials: 1,
360
491
  });
361
492
 
362
493
  const result = await comparison.run();
@@ -377,22 +508,66 @@ Access full suite results via `result.baseline` and `result.challenger`. `compar
377
508
 
378
509
  Call `result.report()` after a suite run to output results.
379
510
 
380
- | Reporter | Output |
381
- | --------- | ----------------------------------------------- |
382
- | `console` | Colored table with metrics, assertions, summary |
383
- | `json` | Writes `eval-report.json` (configurable path) |
384
- | `csv` | Writes `eval-report.csv` (configurable path) |
385
- | `ci` | Compact output, `process.exit(1)` on failure |
511
+ | Reporter | Output |
512
+ | --------- | -------------------------------------------------------------------- |
513
+ | `console` | Colored table with metrics, assertions, summary |
514
+ | `json` | Writes `eval-report.json` (configurable path) |
515
+ | `csv` | Writes `eval-report.csv` (configurable path) |
516
+ | `junit` | Writes `eval-report.xml`, a JUnit report (configurable path) |
517
+ | `github` | GitHub Actions annotations, plus a job summary in Actions |
518
+ | `ci` | Compact verdict, sets `process.exitCode = 1` when an assertion fails |
386
519
 
387
520
  ```typescript
388
521
  const result = await suite.run();
389
522
 
390
523
  result.report('console');
391
524
  result.report('json', { path: './reports/eval.json' });
392
- result.report(['console', 'json', 'csv']);
393
- result.report('ci');
525
+ result.report(['console', 'json', 'csv', 'junit'], { path: './reports/eval' });
526
+ result.report(['github', 'ci']);
527
+ ```
528
+
529
+ The `path` is a base path: each file reporter adds its own extension (`.json`, `.csv`, `.xml`). `ci` runs last and does not end the process, so output flushes and later hooks still run before Node exits with code 1.
530
+
531
+ - **junit** writes one `<testsuite name="eval">` with a `<testcase>` per case (a `<failure>` when it errored, its scores in `<system-out>`) and a `<testcase>` per assertion (a `<failure>` when it did not pass). Everything is XML-escaped, and characters XML 1.0 does not allow are replaced.
532
+ - **github** prints `::error` for each failed assertion and errored case and `::warning` for each failed metric score. When `GITHUB_STEP_SUMMARY` is set it also appends a Markdown summary with the totals, the aggregated metrics (mean, p50, p95, cases scored) and the assertions.
533
+
534
+ Agent run flags (`truncated`, `iterationLimitReached`, `paused`) show up per case in the json, csv and junit reports, and each aggregated metric's `count` (cases it was scored on) in the console, json and github output. The json report keeps each case's `toolCalls`, `toolResults`, `retrieved`, `turns` and `trials`, and the console and github reports show the trials per case.
535
+
536
+ ### Vitest
537
+
538
+ ```typescript
539
+ // eval/qa.eval.test.ts
540
+ import { afterAll } from 'vitest';
541
+ import { describeEval } from '@cogitator-ai/evals/vitest';
542
+
543
+ const qa = describeEval('qa agent', { suite, run: { maxCases: 50 }, timeout: 5 * 60_000 });
544
+
545
+ afterAll(() => qa.result().report('junit', { path: './reports/qa' }));
546
+ ```
547
+
548
+ `evalMatchers` adds assertions on results to Vitest's `expect`, typed through a module augmentation of `Assertion` and `AsymmetricMatchersContaining` (Vitest 5):
549
+
550
+ ```typescript
551
+ import { expect, it } from 'vitest';
552
+ import { evalMatchers } from '@cogitator-ai/evals/matchers';
553
+
554
+ expect.extend(evalMatchers);
555
+
556
+ it('books a table', async () => {
557
+ const result = await suite.run();
558
+ const [booking] = result.results;
559
+ expect(booking).toHaveCalledTool('book_table', { guests: 2 });
560
+ expect(booking).toFollowTrajectory(['find_restaurant', 'book_table'], { order: 'in-order' });
561
+ expect(booking).toHaveRetrieved('doc:menu');
562
+ expect(booking).toScoreAtLeast('toolCalls', 1);
563
+ expect(result).toScoreAtLeast('trajectory', 0.8);
564
+ });
394
565
  ```
395
566
 
567
+ `toHaveCalledTool(name, args?)` checks a case result's calls, `args` as a subset of a call's arguments. `toFollowTrajectory(names, { order? })` wants exactly those calls in that order by default, the calls in that order with others between them for `'in-order'`, and each of them in any order for `'any-order'`. `toHaveRetrieved(idOrText)` matches passages as the retrieval metrics do. `toScoreAtLeast(metric, min)` reads a case result's score or a suite result's aggregated mean, and throws when the metric is missing.
568
+
569
+ `describeEval(name, { suite, run?, timeout? })` registers a `describe` block. The suite runs once in `beforeAll` (`timeout` defaults to 10 minutes, after which the run is cancelled), each case of `suite.cases(run?.maxCases)` gets a test that fails with the case error, and an `assertions` test lists every failed assertion. When the run itself fails, every test fails with that cause. `result()` returns the `EvalSuiteResult` once the run has finished.
570
+
396
571
  ---
397
572
 
398
573
  ## Builder API
@@ -421,6 +596,7 @@ const suite = new EvalBuilder()
421
596
  .withConcurrency(10)
422
597
  .withTimeout(60_000)
423
598
  .withRetries(2)
599
+ .withTrials(3)
424
600
  .onProgress(({ completed, total }) => {
425
601
  console.log(`${completed}/${total}`);
426
602
  })
@@ -484,38 +660,63 @@ result.report('ci');
484
660
 
485
661
  ### Metrics
486
662
 
487
- | Export | Type | Description |
488
- | -------------- | ------------- | ------------------------------------- |
489
- | `exactMatch` | Deterministic | Exact string match |
490
- | `contains` | Deterministic | Substring match |
491
- | `containsAll` | Deterministic | Fraction of keywords found |
492
- | `containsAny` | Deterministic | Any configured keyword found |
493
- | `similarity` | Deterministic | Levenshtein distance similarity score |
494
- | `regex` | Deterministic | Regex pattern match |
495
- | `jsonSchema` | Deterministic | Zod schema validation |
496
- | `faithfulness` | LLM Judge | Factual accuracy |
497
- | `relevance` | LLM Judge | Topical relevance |
498
- | `coherence` | LLM Judge | Logical structure |
499
- | `helpfulness` | LLM Judge | Practical usefulness |
500
- | `llmMetric` | LLM Judge | Custom judge prompt |
501
- | `latency` | Statistical | Response time percentiles |
502
- | `cost` | Statistical | Token cost aggregation |
503
- | `tokenUsage` | Statistical | Input/output token counts |
504
- | `metric` | Custom | Factory for domain-specific metrics |
663
+ | Export | Type | Description |
664
+ | -------------------- | ------------- | ------------------------------------------------------------------ |
665
+ | `exactMatch` | Deterministic | Exact string match |
666
+ | `contains` | Deterministic | Substring match |
667
+ | `containsAll` | Deterministic | Fraction of keywords found |
668
+ | `containsAny` | Deterministic | Any configured keyword found |
669
+ | `similarity` | Deterministic | Levenshtein distance similarity score |
670
+ | `regex` | Deterministic | Regex pattern match |
671
+ | `jsonSchema` | Deterministic | Zod schema validation |
672
+ | `faithfulness` | LLM Judge | Factual accuracy |
673
+ | `relevance` | LLM Judge | Topical relevance |
674
+ | `coherence` | LLM Judge | Logical structure |
675
+ | `helpfulness` | LLM Judge | Practical usefulness |
676
+ | `contextRelevance` | LLM Judge | Relevance of the retrieved passages to the input |
677
+ | `llmMetric` | LLM Judge | Custom judge prompt |
678
+ | `toolCalls` | Agent | Tool calls against `expectedToolCalls`, F1, recall or precision |
679
+ | `trajectory` | Agent | Sequence of tool names, exact, in-order or any-order |
680
+ | `retrievalRecall` | Agent | Share of `expectedPassages` retrieved |
681
+ | `retrievalPrecision` | Agent | Share of retrieved passages expected |
682
+ | `retrievalMRR` | Agent | Reciprocal rank of the first expected passage |
683
+ | `latency` | Statistical | Response time percentiles |
684
+ | `cost` | Statistical | Token cost aggregation |
685
+ | `tokenUsage` | Statistical | Input/output token counts |
686
+ | `passAtK` | Statistical | pass@k of a metric over the trials |
687
+ | `metric` | Custom | Factory for domain-specific metrics |
688
+ | `ragSearchPassages` | Helper | The default `retrieval` of agent targets, for `rag_search` results |
505
689
 
506
690
  ### Assertions
507
691
 
508
692
  | Export | Description |
509
693
  | -------------- | ---------------------------------------------- |
510
694
  | `threshold` | Enforce min/max on metric mean |
695
+ | `maxErrorRate` | Fail a run where too many cases errored |
511
696
  | `noRegression` | Compare against saved baseline |
512
697
  | `assertion` | Custom assertion with arbitrary check function |
513
698
 
514
699
  ### Reporters
515
700
 
516
- | Export | Description |
517
- | -------- | -------------------------------------- |
518
- | `report` | Dispatch to one or more reporter types |
701
+ | Export | Description |
702
+ | ---------------------------------- | ---------------------------------------- |
703
+ | `report` | Dispatch to one or more reporter types |
704
+ | `describeEval` (`/vitest` entry) | Run a suite as a Vitest describe block |
705
+ | `evalMatchers` (`/matchers` entry) | Matchers for `expect.extend()`, Vitest 5 |
706
+
707
+ `report` takes `console`, `json`, `csv`, `junit`, `github` and `ci`. `describeEval` takes `DescribeEvalOptions` and returns a `DescribeEvalHandle`.
708
+
709
+ ### Types
710
+
711
+ | Export | Description |
712
+ | ------------------ | ------------------------------------------------------------------------ |
713
+ | `JudgeConfigInput` | The `judge` option: model, settings, `cogitator`, `cache`, `concurrency` |
714
+ | `JudgeCache` | Where verdicts are kept across runs: `get(key)` and `set(key, verdict)` |
715
+ | `AggregatedMetric` | Aggregate of a metric, with `count` for the cases it covers |
716
+ | `MetricComparison` | A metric of a comparison, with `n` for the cases it was compared on |
717
+ | `ExpectedToolCall` | A tool call a case expects: `name` and optional `arguments` |
718
+ | `Passage` | A retrieved passage: `id?`, `content`, `score?`, `source?` |
719
+ | `EvalTurnResult` | A turn of a multi-turn case: `input`, `output`, `toolCalls?`, `error?` |
519
720
 
520
721
  ### Statistics
521
722
 
@@ -0,0 +1,8 @@
1
+ import type { AssertionFn } from './index.js';
2
+ /**
3
+ * Passes while at most `rate` (0 to 1) of the cases errored, every attempt failed or timed out.
4
+ * Errored cases are not scored, so a metric's mean says nothing about them: pair metric
5
+ * thresholds with this to catch a run where the target mostly failed.
6
+ */
7
+ export declare function maxErrorRate(rate: number): AssertionFn;
8
+ //# sourceMappingURL=error-rate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-rate.d.ts","sourceRoot":"","sources":["../../src/assertions/error-rate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAoBtD"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Passes while at most `rate` (0 to 1) of the cases errored, every attempt failed or timed out.
3
+ * Errored cases are not scored, so a metric's mean says nothing about them: pair metric
4
+ * thresholds with this to catch a run where the target mostly failed.
5
+ */
6
+ export function maxErrorRate(rate) {
7
+ if (!Number.isFinite(rate) || rate < 0 || rate > 1) {
8
+ throw new Error(`maxErrorRate takes a rate from 0 to 1, got ${rate}`);
9
+ }
10
+ const name = `maxErrorRate(${rate})`;
11
+ const percent = (value) => `${Math.round(value * 1000) / 10}%`;
12
+ return (_aggregated, stats) => {
13
+ const errors = stats.errors ?? 0;
14
+ const actual = stats.total === 0 ? 0 : errors / stats.total;
15
+ const passed = actual <= rate;
16
+ return {
17
+ name,
18
+ passed,
19
+ message: passed
20
+ ? `${errors} of ${stats.total} cases errored, at most ${percent(rate)} allowed`
21
+ : `${errors} of ${stats.total} cases errored (${percent(actual)}), expected at most ${percent(rate)}`,
22
+ actual,
23
+ expected: rate,
24
+ };
25
+ };
26
+ }
27
+ //# sourceMappingURL=error-rate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-rate.js","sourceRoot":"","sources":["../../src/assertions/error-rate.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,8CAA8C,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,IAAI,GAAG,gBAAgB,IAAI,GAAG,CAAC;IACrC,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC;IACvE,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,IAAI,IAAI,CAAC;QAC9B,OAAO;YACL,IAAI;YACJ,MAAM;YACN,OAAO,EAAE,MAAM;gBACb,CAAC,CAAC,GAAG,MAAM,OAAO,KAAK,CAAC,KAAK,2BAA2B,OAAO,CAAC,IAAI,CAAC,UAAU;gBAC/E,CAAC,CAAC,GAAG,MAAM,OAAO,KAAK,CAAC,KAAK,mBAAmB,OAAO,CAAC,MAAM,CAAC,uBAAuB,OAAO,CAAC,IAAI,CAAC,EAAE;YACvG,MAAM;YACN,QAAQ,EAAE,IAAI;SACf,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -8,6 +8,8 @@ export interface AggregatedMetric {
8
8
  p50: number;
9
9
  p95: number;
10
10
  p99: number;
11
+ /** Cases the metric was scored on: errored cases are not scored */
12
+ count?: number;
11
13
  /** Extra figures a statistical metric reports, such as the total cost */
12
14
  metadata?: Record<string, unknown>;
13
15
  }
@@ -39,4 +41,5 @@ export type AssertionFn = (aggregated: Record<string, AggregatedMetric>, stats:
39
41
  export { threshold } from './threshold.js';
40
42
  export { noRegression } from './regression.js';
41
43
  export { assertion } from './custom.js';
44
+ export { maxErrorRate } from './error-rate.js';
42
45
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assertions/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,sCAAsC;AACtC,MAAM,WAAW,SAAS;IACxB,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,CACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC5C,KAAK,EAAE,SAAS,KACb,eAAe,CAAC;AAErB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assertions/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,sCAAsC;AACtC,MAAM,WAAW,SAAS;IACxB,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,CACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC5C,KAAK,EAAE,SAAS,KACb,eAAe,CAAC;AAErB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { threshold } from './threshold.js';
2
2
  export { noRegression } from './regression.js';
3
3
  export { assertion } from './custom.js';
4
+ export { maxErrorRate } from './error-rate.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/assertions/index.ts"],"names":[],"mappings":"AA6CA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/assertions/index.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"regression.d.ts","sourceRoot":"","sources":["../../src/assertions/regression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAuF7F"}
1
+ {"version":3,"file":"regression.d.ts","sourceRoot":"","sources":["../../src/assertions/regression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CA0F7F"}
@@ -38,7 +38,9 @@ export function noRegression(baselinePath, opts) {
38
38
  return {
39
39
  name: 'noRegression',
40
40
  passed: false,
41
- message: `'${metric}' = ${actual} is not a finite number (baseline ${baselineValue})`,
41
+ message: agg.count === 0
42
+ ? `'${metric}' has no scored cases, every case errored (baseline ${baselineValue})`
43
+ : `'${metric}' = ${actual} is not a finite number (baseline ${baselineValue})`,
42
44
  actual,
43
45
  expected: baselineValue,
44
46
  };
@@ -1 +1 @@
1
- {"version":3,"file":"regression.js","sourceRoot":"","sources":["../../src/assertions/regression.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"regression.js","sourceRoot":"","sources":["../../src/assertions/regression.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"threshold.d.ts","sourceRoot":"","sources":["../../src/assertions/threshold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAoB,MAAM,SAAS,CAAC;AAoB7D,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAoCxE"}
1
+ {"version":3,"file":"threshold.d.ts","sourceRoot":"","sources":["../../src/assertions/threshold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAoB,MAAM,SAAS,CAAC;AAoB7D,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAuCxE"}
@@ -23,10 +23,13 @@ export function threshold(metricName, value) {
23
23
  };
24
24
  }
25
25
  if (!Number.isFinite(actual)) {
26
+ const unscored = aggregated[metricName.split('.')[0]]?.count === 0;
26
27
  return {
27
28
  name: `threshold(${metricName})`,
28
29
  passed: false,
29
- message: `${metricName} = ${actual} is not a finite number`,
30
+ message: unscored
31
+ ? `${metricName} has no scored cases: every case errored`
32
+ : `${metricName} = ${actual} is not a finite number`,
30
33
  actual,
31
34
  expected: value,
32
35
  };
@@ -1 +1 @@
1
- {"version":3,"file":"threshold.js","sourceRoot":"","sources":["../../src/assertions/threshold.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,SAAS,OAAO,CACd,UAA4C,EAC5C,IAAY;IAEZ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAE/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAEnE,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAA2B,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAE/D,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,UAAkB,EAAE,KAAa;IACzD,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE;QAC5B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAEjE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;gBACL,IAAI,EAAE,aAAa,UAAU,GAAG;gBAChC,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,WAAW,UAAU,mCAAmC;aAClE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,aAAa,UAAU,GAAG;gBAChC,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,GAAG,UAAU,MAAM,MAAM,yBAAyB;gBAC3D,MAAM;gBACN,QAAQ,EAAE,KAAK;aAChB,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;QAC/D,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAE5C,OAAO;YACL,IAAI,EAAE,aAAa,UAAU,GAAG;YAChC,MAAM;YACN,OAAO,EAAE,MAAM;gBACb,CAAC,CAAC,GAAG,UAAU,MAAM,MAAM,IAAI,SAAS,IAAI,KAAK,EAAE;gBACnD,CAAC,CAAC,GAAG,UAAU,MAAM,MAAM,cAAc,SAAS,IAAI,KAAK,EAAE;YAC/D,MAAM;YACN,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"threshold.js","sourceRoot":"","sources":["../../src/assertions/threshold.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,SAAS,OAAO,CACd,UAA4C,EAC5C,IAAY;IAEZ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAE/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAEnE,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAA2B,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAE/D,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,UAAkB,EAAE,KAAa;IACzD,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE;QAC5B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAEjE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;gBACL,IAAI,EAAE,aAAa,UAAU,GAAG;gBAChC,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,WAAW,UAAU,mCAAmC;aAClE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;YACpE,OAAO;gBACL,IAAI,EAAE,aAAa,UAAU,GAAG;gBAChC,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,QAAQ;oBACf,CAAC,CAAC,GAAG,UAAU,0CAA0C;oBACzD,CAAC,CAAC,GAAG,UAAU,MAAM,MAAM,yBAAyB;gBACtD,MAAM;gBACN,QAAQ,EAAE,KAAK;aAChB,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;QAC/D,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAE5C,OAAO;YACL,IAAI,EAAE,aAAa,UAAU,GAAG;YAChC,MAAM;YACN,OAAO,EAAE,MAAM;gBACb,CAAC,CAAC,GAAG,UAAU,MAAM,MAAM,IAAI,SAAS,IAAI,KAAK,EAAE;gBACnD,CAAC,CAAC,GAAG,UAAU,MAAM,MAAM,cAAc,SAAS,IAAI,KAAK,EAAE;YAC/D,MAAM;YACN,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -1,3 +1,8 @@
1
1
  import type { EvalCase } from '../schema.js';
2
+ /**
3
+ * Cases from a CSV file with an `input` column, an optional `expected` one, `metadata.<key>` and
4
+ * `context.<key>` columns, and `turns`, `expectedToolCalls` and `expectedPassages` columns whose
5
+ * cells hold JSON arrays (an empty cell leaves the field out)
6
+ */
2
7
  export declare function loadCsv(path: string): Promise<EvalCase[]>;
3
8
  //# sourceMappingURL=csv-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"csv-loader.d.ts","sourceRoot":"","sources":["../../src/datasets/csv-loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CA6C/D"}
1
+ {"version":3,"file":"csv-loader.d.ts","sourceRoot":"","sources":["../../src/datasets/csv-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAK1C;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAwF/D"}