@axlsdk/eval 0.17.9 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -19
- package/dist/{chunk-NETD4AUB.js → chunk-66EYSYQS.js} +251 -164
- package/dist/chunk-66EYSYQS.js.map +1 -0
- package/dist/cli.cjs +541 -208
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +305 -47
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +322 -176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +254 -2
- package/dist/index.d.ts +254 -2
- package/dist/index.js +80 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-NETD4AUB.js.map +0 -1
package/README.md
CHANGED
|
@@ -101,6 +101,8 @@ const ds = dataset({
|
|
|
101
101
|
|
|
102
102
|
You can also load from a file: `dataset({ name: 'large', schema, file: './data.json' })`.
|
|
103
103
|
|
|
104
|
+
> **Annotation keys must be in the schema.** Zod strips keys it doesn't declare, so an annotation field missing from the `annotations` schema is dropped before it reaches your scorers — and a scorer reading `annotations?.thatField` then silently sees `undefined` and becomes a no-op. By default `dataset()` warns once per dataset listing any dropped keys (`annotation key(s) dropped by schema: ...`). Set `onExtraAnnotationKeys: 'error'` to fail instead (recommended in CI), or `'ignore'` to silence. This is the only guard for `file:` datasets, which aren't type-checked.
|
|
105
|
+
|
|
104
106
|
## Scorers
|
|
105
107
|
|
|
106
108
|
Scorers rate each output on a 0-1 scale. You can mix deterministic and LLM scorers in the same eval.
|
|
@@ -132,6 +134,48 @@ const lengthScore = scorer({
|
|
|
132
134
|
|
|
133
135
|
Scorers that return a non-finite value (`NaN` / `Infinity`) or a score outside `[0, 1]` are recorded as `null` with an entry in `item.scorerErrors` — they don't abort the run.
|
|
134
136
|
|
|
137
|
+
### Conditional scorers (`applies`)
|
|
138
|
+
|
|
139
|
+
By default every scorer runs against every item. When a scorer only applies to a subset — a refusal judge for refusal-expected items, a constraint judge for constrained items, an asymmetry scorer for asymmetric exercises — declare its scope with an `applies` predicate. It mirrors the `score` signature (`output, input, annotations?`) and returns `true` to run or `false` to skip:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { scorer } from '@axlsdk/eval';
|
|
143
|
+
|
|
144
|
+
// Deterministic: only score items the dataset flagged as "constrained"
|
|
145
|
+
const constraintAdherence = scorer({
|
|
146
|
+
name: 'constraint-adherence',
|
|
147
|
+
description: 'Output respects the stated constraint',
|
|
148
|
+
applies: (_output, _input, annotations) => annotations?.constrained === true,
|
|
149
|
+
score: (output, _input, annotations) =>
|
|
150
|
+
String(output).includes(String(annotations?.constraint)) ? 1 : 0,
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
When `applies` returns `false`, the scorer is **skipped** for that item:
|
|
155
|
+
|
|
156
|
+
- The scorer body never runs — for an `llmScorer`, **no provider call is made**, saving real cost and rate-limit budget (a skipped judge can't 429 you).
|
|
157
|
+
- The item counts as **neither `scored` nor `failed`** — it's excluded from the `mean` *and* from the failure-rate denominator (`failOnScorerErrorRate` and `compare --max-scorer-error-rate`), so a conditional scorer stays honest to both the mean and the trust gate.
|
|
158
|
+
- The skip is visible: `item.scoreDetails[name].skipped === true`, and the run summary carries a per-scorer `skipped` count (alongside `scored` / `failed`). Studio's Eval Runner shows an "N/A" chip.
|
|
159
|
+
|
|
160
|
+
Applicability usually keys off `input` / `annotations` (a flag or exercise type), but `output` is available too (e.g. "only score outputs that actually refused"). The predicate runs **before** the scorer body, so an inapplicable `llmScorer` never makes its provider call:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { llmScorer } from '@axlsdk/eval';
|
|
164
|
+
|
|
165
|
+
// LLM judge: only invoke the (expensive) judge for items where a refusal was expected
|
|
166
|
+
const refusalQuality = llmScorer({
|
|
167
|
+
name: 'refusal-quality',
|
|
168
|
+
description: 'Did the model refuse appropriately and explain why?',
|
|
169
|
+
model: 'openai:gpt-4o',
|
|
170
|
+
system: 'Rate whether the output is a well-justified refusal.',
|
|
171
|
+
applies: (_output, _input, annotations) => annotations?.expectRefusal === true,
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
A predicate that **throws** is treated as a scorer failure (it's a bug), not a skip — the item lands in the `failed` bucket with an entry in `item.scorerErrors`, never silently swallowed.
|
|
176
|
+
|
|
177
|
+
> **Deprecated: the `NaN`-skip workaround.** Before `applies`, the only way to express "not applicable" was to return a `NaN` / out-of-range sentinel for skipped items. The failure-rate gate (correctly) treats a non-finite score as a real failure, so a deterministic conditional scorer that returned `NaN` for inapplicable items showed a ~90% failure rate and permanently tripped the zero-tolerance deterministic gate. Use `applies` instead. The gate's strictness about `NaN` is intentional and unchanged.
|
|
178
|
+
|
|
135
179
|
### LLM scorers
|
|
136
180
|
|
|
137
181
|
Use an LLM as a judge. The scorer constructs a prompt from the input, output, and annotations, calls the LLM, and validates the response:
|
|
@@ -185,6 +229,8 @@ const qualityJudge = llmScorer({ name: 'quality', model: 'openai:gpt-4o', ... })
|
|
|
185
229
|
const safetyJudge = llmScorer({ name: 'safety', model: 'anthropic:claude-sonnet-4-5-20250514', ... });
|
|
186
230
|
```
|
|
187
231
|
|
|
232
|
+
LLM scorers also accept the conditional `applies` predicate described in [Conditional scorers](#conditional-scorers-applies) — the most valuable place to use it, since a skipped judge makes no provider call at all.
|
|
233
|
+
|
|
188
234
|
#### Tuning the judge
|
|
189
235
|
|
|
190
236
|
For reasoning-capable judge models (gpt-5.x, Anthropic Opus/Sonnet 4.5+, Gemini 3.x), `effort` is the highest-leverage knob — judges with reasoning enabled are materially more consistent. The full `ChatOptions` surface is forwarded:
|
|
@@ -223,10 +269,49 @@ npx axl-eval ./evals/ # run all *.eval.* files in
|
|
|
223
269
|
npx axl-eval ./evals/ --output ./results/v1.json # save results to JSON
|
|
224
270
|
npx axl-eval ./evals/ --config ./axl.config.ts # use a specific runtime config
|
|
225
271
|
npx axl-eval ./evals/ --conditions development # add Node.js import conditions (monorepo source exports)
|
|
272
|
+
npx axl-eval ./evals/qa.eval.ts --concurrency 10 # override item concurrency for this run
|
|
273
|
+
npx axl-eval ./evals/qa.eval.ts --scorers accuracy # run only named scorer(s) (single file)
|
|
226
274
|
```
|
|
227
275
|
|
|
228
276
|
The CLI resolves a runtime automatically: `--config <path>` > auto-detect `axl.config.*` > bare `new AxlRuntime()` (providers from env vars). Use `--conditions` when your eval file imports from monorepo packages that use conditional exports (e.g., `"development"` condition for source TypeScript instead of compiled dist).
|
|
229
277
|
|
|
278
|
+
**`--concurrency <n>` / `AXL_EVAL_CONCURRENCY`** override item concurrency per-invocation (precedence: flag > env > `defineEval` value > default 5). Concurrency is pure scheduling — it never changes results. Note the per-eval `scorerConcurrency` (default 5) is independent, so the worst-case number of simultaneous scorer calls is `concurrency × scorerConcurrency`; lower `--concurrency` if a rate-limited judge model needs a tighter ceiling.
|
|
279
|
+
|
|
280
|
+
**`--scorers <a,b>`** runs only the named scorers (single eval file only) for a fast iteration loop. The result is stamped `metadata.scorerFiltered: true` so `axl-eval compare` warns — and, under `--fail-on-regression`, refuses — to gate on a subset run mistaken for a full baseline.
|
|
281
|
+
|
|
282
|
+
#### Scorer failure rate — two complementary gates
|
|
283
|
+
|
|
284
|
+
Concurrent scorers (`concurrency × scorerConcurrency`, up to 25 in-flight by default) can overrun a rate-limited judge model. When a judge call exhausts the provider's retry backoff it throws, the runner records that item's score as `null`, and the mean is computed over the **survivors** — so a thinned sample can still look green. The runner now always reports per-scorer `scored`/`failed` counts (visible in the table as `(18/20 scored, 2 failed)` and in Studio as an amber badge), and two opt-in gates turn that signal into a non-zero exit:
|
|
285
|
+
|
|
286
|
+
| Gate | Where | When it fires |
|
|
287
|
+
|------|-------|---------------|
|
|
288
|
+
| `failOnScorerErrorRate` (config field) | **Source-side** — at run/produce time | `runEval` flags `summary.degraded` (and the CLI exits non-zero) when a scorer exceeds tolerance, *before* the thinned result is saved. `runEval` never throws — it returns the result with the flag set, preserving multi-run partials and auto-save. |
|
|
289
|
+
| `--max-scorer-error-rate <0..1>` (compare flag) | **Gate-side** — at consume/gate time | `axl-eval compare` refuses (exit 1) to certify a baseline/candidate whose failure rate is over the limit. Always *warns* when either side rests on a thinned sample, even without the flag. |
|
|
290
|
+
|
|
291
|
+
Both are **type-aware**: deterministic scorers tolerate **zero** failures (a deterministic scorer that throws is a bug, not noise); LLM judges use the configured rate. They're complementary — set `failOnScorerErrorRate` in the eval file to fail fast at run time, and pass `--max-scorer-error-rate` in CI to refuse to gate on an artifact that was already thinned (e.g. an imported result).
|
|
292
|
+
|
|
293
|
+
**Skipped items don't count against either gate.** The failure rate is `failed / (scored + failed)` — items a scorer's `applies` predicate skipped land in neither bucket, so they're excluded from the denominator. This is the supported way to scope a conditional scorer (a refusal judge that only runs on refusal-expected items, say) to its applicable subset without polluting the trust signal. **Do not** return `NaN` for inapplicable items: the gate (correctly) treats a non-finite score as a real failure, so a `NaN`-skipping deterministic scorer trips the zero-tolerance deterministic gate on every skipped item. Use [`applies`](#conditional-scorers-applies) instead.
|
|
294
|
+
|
|
295
|
+
Because skips are excluded rather than failed, two failure modes are surfaced as **advisories** (stderr, non-fatal) so a conditional scorer can't silently mislead:
|
|
296
|
+
|
|
297
|
+
- **Fully N/A scorer** — if a scorer ends up applicable to *zero* items (an over-strict predicate, a dataset missing the flagged items, or a predicate that forgot to `return`), its mean is a meaningless empty-sample `0` and the failure-rate gate can't assess it. `axl-eval` prints a `NOTE` (the run still exits 0 — skips are legitimately conditional).
|
|
298
|
+
- **Asymmetric skips in `compare`** — if the baseline and candidate applied a scorer to *different* item subsets (different N/A counts, or the same count of *different* items — detected as fewer items scored on both sides than either scored alone), their per-side means cover different subsets, so a delta may be a sample mismatch rather than a real change. `axl-eval compare` prints a `NOTE` alongside the usual thinned-sample warning. (Studio's compare view shows the same as a "paired n" note.)
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
// eval file — fail this run if the judge errored on >10% of the items it ran against
|
|
302
|
+
export default { workflow: 'qa', dataset: ds, scorers: [judge], failOnScorerErrorRate: 0.1 };
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npx axl-eval compare base.json cand.json --fail-on-regression --max-scorer-error-rate 0.05
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Building a custom CI gate? Both decisions are exported as pure functions — `evaluateScorerErrorRateGate` and `evaluateScorerTolerance` (see the [API reference](../../docs/api-reference.md)).
|
|
310
|
+
|
|
311
|
+
#### Total-workflow-wipeout guard
|
|
312
|
+
|
|
313
|
+
Separate from the scorer signal above: if **every** item errored in the *workflow* (0 succeeded), the eval produced no scorable output, so `axl-eval` always exits non-zero with `FAILED: … all N item(s) errored in the workflow` — a fully-broken eval (bad provider URI, an exception in every run) can never go green in CI. This is non-configurable (a 0%-success eval is unambiguously broken) and distinct from `failOnScorerErrorRate` (which is about a flaky *scorer* and deliberately ignores a run with no scored items). Partial workflow-failure rates stay visible (`Failures: N/M` in the table) but non-gating.
|
|
314
|
+
|
|
230
315
|
### Programmatic
|
|
231
316
|
|
|
232
317
|
**`runtime.eval()`** — when you have a workflow registered on the runtime. The `workflow` field must match the registered name:
|
|
@@ -319,8 +404,16 @@ Each eval run returns an `EvalResult` with per-item scores and aggregate statist
|
|
|
319
404
|
|
|
320
405
|
Each item has two ways to access scores:
|
|
321
406
|
|
|
322
|
-
- **`item.scores`** — quick numeric lookup: `Record<string, number | null>`. Use this for simple checks and aggregation. `null` means the scorer failed (see `item.scorerErrors`).
|
|
323
|
-
- **`item.scoreDetails`** — full context: `Record<string, ScorerDetail>`. Each detail has the numeric score plus `metadata` (e.g., LLM reasoning), per-scorer `duration`, and `
|
|
407
|
+
- **`item.scores`** — quick numeric lookup: `Record<string, number | null>`. Use this for simple checks and aggregation. `null` means the scorer failed (see `item.scorerErrors`) **or** was skipped by its `applies` predicate — disambiguate with `scoreDetails[name].skipped`.
|
|
408
|
+
- **`item.scoreDetails`** — full context: `Record<string, ScorerDetail>`. Each detail has the numeric score plus `metadata` (e.g., LLM reasoning), per-scorer `duration`, `cost`, and a `skipped` flag. Use this when you need to understand *why* a score is what it is.
|
|
409
|
+
|
|
410
|
+
A `ScorerDetail` distinguishes three terminal states for a scorer on a given item:
|
|
411
|
+
|
|
412
|
+
| State | `score` | `skipped` | `duration` |
|
|
413
|
+
|-------|---------|-----------|------------|
|
|
414
|
+
| Scored | `number` | absent | set |
|
|
415
|
+
| Ran-and-failed (threw / out-of-range) | `null` | absent | set |
|
|
416
|
+
| Skipped by `applies` | `null` | `true` | absent |
|
|
324
417
|
|
|
325
418
|
Summary statistics (mean, p50, p95, etc.) exclude `null` scores. If all scores for a scorer are `null`, the CLI shows `--` instead of misleading `0.00`.
|
|
326
419
|
|
|
@@ -329,6 +422,9 @@ const results = await runtime.eval({ ... });
|
|
|
329
422
|
|
|
330
423
|
// ── Aggregate stats ──────────────────────────────────
|
|
331
424
|
console.log(results.summary.scorers['quality'].mean); // 0.85
|
|
425
|
+
console.log(results.summary.scorers['quality'].scored); // 48 (items that produced a numeric score)
|
|
426
|
+
console.log(results.summary.scorers['quality'].failed); // 2 (scorer ran and threw / out-of-range)
|
|
427
|
+
console.log(results.summary.scorers['quality'].skipped); // 0 (items the applies predicate skipped)
|
|
332
428
|
console.log(results.summary.count); // 50 items
|
|
333
429
|
console.log(results.summary.failures); // 2 workflow errors
|
|
334
430
|
console.log(results.summary.timing); // { mean, min, max, p50, p95 } in ms
|
|
@@ -356,14 +452,19 @@ for (const item of results.items) {
|
|
|
356
452
|
// Rich per-scorer detail — reasoning, timing, cost
|
|
357
453
|
const detail = item.scoreDetails?.['quality'];
|
|
358
454
|
if (detail) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
455
|
+
if (detail.skipped) {
|
|
456
|
+
// This scorer's `applies` predicate returned false — not run, not counted
|
|
457
|
+
console.log('Scorer skipped (not applicable to this item)');
|
|
458
|
+
} else {
|
|
459
|
+
console.log(detail.score); // 0.85
|
|
460
|
+
console.log(detail.metadata?.reasoning); // "The answer is relevant..."
|
|
461
|
+
console.log(detail.duration); // scorer execution ms
|
|
462
|
+
console.log(detail.cost); // scorer LLM cost
|
|
463
|
+
}
|
|
363
464
|
}
|
|
364
465
|
|
|
365
|
-
// Error handling
|
|
366
|
-
if (item.scores['quality'] === null) {
|
|
466
|
+
// Error handling — a null score is either a failure OR a skip
|
|
467
|
+
if (item.scores['quality'] === null && !item.scoreDetails?.['quality']?.skipped) {
|
|
367
468
|
console.log('Scorer failed:', item.scorerErrors); // ["Scorer "quality" threw: ..."]
|
|
368
469
|
}
|
|
369
470
|
|
|
@@ -430,6 +531,7 @@ Compare two runs to detect regressions and improvements. Runs must use the same
|
|
|
430
531
|
```bash
|
|
431
532
|
npx axl-eval compare ./results/v1.json ./results/v2.json
|
|
432
533
|
npx axl-eval compare v1.json v2.json --fail-on-regression # exit 1 if significant regressions
|
|
534
|
+
npx axl-eval compare v1.json v2.json --max-scorer-error-rate 0.05 # exit 1 if a scorer failed on >5% of the items it ran against (deterministic scorers: zero tolerance)
|
|
433
535
|
```
|
|
434
536
|
|
|
435
537
|
```
|
|
@@ -569,18 +671,21 @@ return { output, cost: ctx.totalCost };
|
|
|
569
671
|
|
|
570
672
|
### Common patterns
|
|
571
673
|
|
|
572
|
-
**Concurrency** — process items in parallel (default: 5):
|
|
674
|
+
**Concurrency** — process items in parallel (default: 5), and fan out each item's scorers (default: 5):
|
|
573
675
|
|
|
574
676
|
```typescript
|
|
575
677
|
export default defineEval({
|
|
576
678
|
workflow: 'qa-eval',
|
|
577
679
|
dataset: ds,
|
|
578
|
-
scorers: [qualityJudge],
|
|
579
|
-
concurrency: 10,
|
|
680
|
+
scorers: [qualityJudge, toneJudge, safetyJudge],
|
|
681
|
+
concurrency: 10, // run 10 items in parallel
|
|
682
|
+
scorerConcurrency: 3, // run up to 3 of an item's scorers at once
|
|
580
683
|
budget: '$5.00', // stop if total cost exceeds $5
|
|
581
684
|
});
|
|
582
685
|
```
|
|
583
686
|
|
|
687
|
+
`scorerConcurrency` parallelizes the per-item judge phase — the dominant cost for evals with several `llmScorer`s. The worst-case number of simultaneous scorer calls is `concurrency × scorerConcurrency`, so a rate-limited judge model may need a lower value (set `scorerConcurrency: 1` for the old serial behavior). Because the per-item `budget` check runs once before an item's scorers, scorer-cost overshoot is bounded by `concurrency × scorerConcurrency × max-scorer-cost`.
|
|
688
|
+
|
|
584
689
|
**Per-item budget** — cap cost for a single workflow execution:
|
|
585
690
|
|
|
586
691
|
```typescript
|
|
@@ -620,7 +725,9 @@ npx axl-eval rescore ./results/v1.json ./evals/qa.eval.ts
|
|
|
620
725
|
npx axl-eval rescore ./results/v1.json ./evals/qa.eval.ts --output ./results/v1-rescored.json
|
|
621
726
|
```
|
|
622
727
|
|
|
623
|
-
The rescored result gets a new `id` and has `metadata.rescored: true` and `metadata.originalId` pointing to the source run. `runGroupId` and `
|
|
728
|
+
The rescored result gets a new `id` and has `metadata.rescored: true` and `metadata.originalId` pointing to the source run. `runGroupId`, `runIndex`, and any `scorerFiltered`/`scorersRun` stamp are stripped from inherited metadata (rescored results are independent evaluations, and a rescore always runs the eval file's full scorer set). Only scorer cost is tracked (workflow cost is zero).
|
|
729
|
+
|
|
730
|
+
`--concurrency`/`AXL_EVAL_CONCURRENCY` are honored by `rescore` too (item-level; `scorerConcurrency` keeps its default 5). `--scorers` is **not** supported with `rescore` — it's a run-command-only filter; rescore re-runs whatever scorers the eval file exports (pass a subset there instead).
|
|
624
731
|
|
|
625
732
|
Programmatically:
|
|
626
733
|
|
|
@@ -628,7 +735,8 @@ Programmatically:
|
|
|
628
735
|
import { rescore } from '@axlsdk/eval';
|
|
629
736
|
|
|
630
737
|
const rescored = await rescore(originalResult, [updatedScorer, newScorer], runtime, {
|
|
631
|
-
concurrency: 10,
|
|
738
|
+
concurrency: 10, // items rescored in parallel
|
|
739
|
+
scorerConcurrency: 3, // scorers per item run in parallel (default 5)
|
|
632
740
|
});
|
|
633
741
|
|
|
634
742
|
console.log(rescored.metadata.rescored); // true
|
|
@@ -700,8 +808,8 @@ const comparison = evalCompare(baselineRuns, candidateRuns);
|
|
|
700
808
|
| Function | Description |
|
|
701
809
|
|----------|-------------|
|
|
702
810
|
| `dataset(config)` | Create a dataset from inline items or a JSON file |
|
|
703
|
-
| `scorer(config)` | Create a deterministic scorer |
|
|
704
|
-
| `llmScorer(config)` | Create an LLM-as-judge scorer |
|
|
811
|
+
| `scorer(config)` | Create a deterministic scorer (optional `applies` predicate for conditional scoring) |
|
|
812
|
+
| `llmScorer(config)` | Create an LLM-as-judge scorer (optional `applies` predicate — a skipped judge makes no provider call) |
|
|
705
813
|
| `defineEval(config)` | Wrap an eval config for CLI discovery |
|
|
706
814
|
| `runEval(config, executeFn, runtime, options?)` | Run an eval programmatically. `options: RunEvalOptions` accepts `onProgress` / `signal` / `captureTraces` |
|
|
707
815
|
| `evalCompare(baseline, candidate, options?)` | Compare eval results with bootstrap CI |
|
|
@@ -714,17 +822,17 @@ const comparison = evalCompare(baselineRuns, candidateRuns);
|
|
|
714
822
|
|
|
715
823
|
| Type | Description |
|
|
716
824
|
|------|-------------|
|
|
717
|
-
| `EvalConfig` | Eval definition (workflow, dataset, scorers, concurrency, budget) |
|
|
825
|
+
| `EvalConfig` | Eval definition (workflow, dataset, scorers, concurrency, scorerConcurrency, budget) |
|
|
718
826
|
| `EvalResult` | Full eval output (items, summary, cost, duration) |
|
|
719
827
|
| `EvalItem` | Per-item result (input, output, scores, scoreDetails, metadata, traces?) |
|
|
720
|
-
| `EvalSummary` | Aggregate statistics (count, failures, scorer stats
|
|
828
|
+
| `EvalSummary` | Aggregate statistics (count, failures, per-scorer stats incl. `scored`/`failed`/`skipped`, timing) |
|
|
721
829
|
| `EvalComparison` | Comparison output (scorer deltas, CI, pRegression/pImprovement, n, per-side `runCount` / `partial?`, regressions, improvements). Both sides truncate to `min(baseline.length, candidate.length)` so means align with the paired bootstrap CI's sample |
|
|
722
830
|
| `EvalComparisonPartial` | `{ completed, attempted }` set on a comparison side when the pooled run count is less than the original batch's planned count |
|
|
723
831
|
| `EvalCompareOptions` | Options for `evalCompare()` (`thresholds`) |
|
|
724
832
|
| `EvalRegression` / `EvalImprovement` | Per-item change record (itemIndex, scorer, scores, delta) |
|
|
725
|
-
| `ScorerDetail` | Per-scorer detail (score, metadata, duration, cost) |
|
|
833
|
+
| `ScorerDetail` | Per-scorer detail (score, metadata, duration, cost, `skipped?`) |
|
|
726
834
|
| `ScorerResult` | Scorer return type (`{ score, metadata?, cost? }`) |
|
|
727
|
-
| `RescoreOptions` | Options for `rescore()` (`concurrency`) |
|
|
835
|
+
| `RescoreOptions` | Options for `rescore()` (`concurrency`, `scorerConcurrency`, `signal`) |
|
|
728
836
|
| `MultiRunSummary` | Aggregated multi-run output (per-scorer mean/std/min/max) |
|
|
729
837
|
| `BootstrapCIResult` | CI result (`{ lower, upper, mean, pRegression, pImprovement }`) |
|
|
730
838
|
| `RunEvalOptions` | Options for `runEval()` (`onProgress`, `signal`, `captureTraces`) |
|