@axlsdk/eval 0.13.6 → 0.13.7

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 CHANGED
@@ -275,6 +275,8 @@ console.log(results.summary.count); // 50 items
275
275
  console.log(results.summary.failures); // 2 workflow errors
276
276
  console.log(results.summary.timing); // { mean, min, max, p50, p95 } in ms
277
277
  console.log(results.totalCost); // 0.42 (workflow + scorer LLM costs)
278
+ console.log(results.metadata.models); // ["openai:gpt-4o"] (sorted by usage)
279
+ console.log(results.metadata.modelCounts); // { "openai:gpt-4o": 48, "openai:gpt-4o-mini": 2 } (total LLM calls per model)
278
280
 
279
281
  // ── Per-item inspection ──────────────────────────────
280
282
  for (const item of results.items) {
@@ -285,6 +287,11 @@ for (const item of results.items) {
285
287
  console.log(item.cost); // workflow LLM cost
286
288
  console.log(item.scorerCost); // total scorer cost for this item
287
289
 
290
+ // Execution metadata (models, tokens, agent calls — captured by AxlRuntime)
291
+ console.log(item.metadata?.models); // ["openai:gpt-4o"]
292
+ console.log(item.metadata?.tokens); // { input: 150, output: 320, reasoning: 0 }
293
+ console.log(item.metadata?.agentCalls); // 1
294
+
288
295
  // Quick score access
289
296
  console.log(item.scores['quality']); // 0.85 or null
290
297
 
@@ -310,16 +317,16 @@ Compare two runs to detect regressions and improvements. Runs must use the same
310
317
 
311
318
  ```bash
312
319
  npx axl-eval compare ./results/v1.json ./results/v2.json
313
- npx axl-eval compare v1.json v2.json --fail-on-regression # exit 1 if worse
320
+ npx axl-eval compare v1.json v2.json --fail-on-regression # exit 1 if significant regressions
314
321
  ```
315
322
 
316
323
  ```
317
324
  Compare: baseline (3f8a2b1c) -> candidate (9d4e7f6a)
318
325
 
319
- Scorer Baseline Candidate Delta Change
320
- ──────────────────────────────────────────────────
321
- quality 0.750 0.850 +0.100 +13.3%
322
- safety 0.900 0.900 +0.000 +0.0%
326
+ Scorer Baseline Candidate Delta Change CI 95% Sig
327
+ ────────────────────────────────────────────────────────────────────────
328
+ quality 0.750 0.850 +0.100 +13.3% [+0.0312, +0.1688] *
329
+ safety 0.900 0.900 +0.000 +0.0% [-0.0250, +0.0250]
323
330
 
324
331
  Timing: baseline 2.10s -> candidate 4.30s (+104.8%)
325
332
  Cost: baseline $0.45 -> candidate $0.31 (-31.1%)
@@ -338,11 +345,18 @@ const comparison = evalCompare(v1Results, v2Results);
338
345
  console.log(comparison.scorers.quality.delta); // +0.1
339
346
  console.log(comparison.scorers.quality.deltaPercent); // +13.3
340
347
 
348
+ // Statistical significance
349
+ console.log(comparison.scorers.quality.ci); // { lower: 0.0312, upper: 0.1688 }
350
+ console.log(comparison.scorers.quality.significant); // true
351
+ console.log(comparison.scorers.quality.pRegression); // 0.02 (2% chance of regression)
352
+ console.log(comparison.scorers.quality.pImprovement); // 0.98 (98% chance of improvement)
353
+ console.log(comparison.scorers.quality.n); // 50 (paired sample count)
354
+
341
355
  // Timing and cost tradeoffs
342
356
  console.log(comparison.timing?.deltaPercent); // +104.8 (slower)
343
357
  console.log(comparison.cost?.deltaPercent); // -31.1 (cheaper)
344
358
 
345
- // Per-item regressions/improvements (delta > 0.1 threshold)
359
+ // Per-item regressions/improvements
346
360
  for (const r of comparison.regressions) {
347
361
  console.log(`Item ${r.itemIndex}: ${r.scorer} dropped ${r.baselineScore} → ${r.candidateScore}`);
348
362
  }
@@ -350,6 +364,53 @@ for (const r of comparison.regressions) {
350
364
  console.log(comparison.summary); // human-readable one-liner
351
365
  ```
352
366
 
367
+ `evalCompare()` also accepts arrays for multi-run comparison — see [Multi-Run](#multi-run).
368
+
369
+ ### Configurable Thresholds
370
+
371
+ By default, thresholds auto-calibrate from scorer type metadata embedded in eval results: **0** for deterministic scorers, **0.05** for LLM scorers (which have natural variance). Results without `scorerTypes` metadata fall back to **0.1**.
372
+
373
+ Override with `--threshold` on the CLI:
374
+
375
+ ```bash
376
+ # Global threshold for all scorers
377
+ npx axl-eval compare v1.json v2.json --threshold 0.05
378
+
379
+ # Per-scorer thresholds
380
+ npx axl-eval compare v1.json v2.json --threshold accuracy=0,tone=0.1
381
+ ```
382
+
383
+ Programmatically via `EvalCompareOptions`:
384
+
385
+ ```typescript
386
+ import { evalCompare } from '@axlsdk/eval';
387
+ import type { EvalCompareOptions } from '@axlsdk/eval';
388
+
389
+ // Global threshold
390
+ evalCompare(baseline, candidate, { thresholds: 0.05 });
391
+
392
+ // Per-scorer map
393
+ evalCompare(baseline, candidate, { thresholds: { accuracy: 0, tone: 0.1 } });
394
+ ```
395
+
396
+ ### Statistical Significance
397
+
398
+ `evalCompare()` computes a 95% bootstrap confidence interval on paired per-item score differences. A scorer change is marked `significant` when:
399
+
400
+ 1. The CI excludes zero (the effect is unlikely due to chance), **and**
401
+ 2. The absolute delta exceeds the threshold (the effect is practically meaningful).
402
+
403
+ `--fail-on-regression` uses significance when available — it only exits with code 1 if at least one scorer has a significant negative delta. Without enough paired data for CI (fewer than 2 items), it falls back to threshold-only comparison.
404
+
405
+ The underlying `pairedBootstrapCI()` function is exported for direct use:
406
+
407
+ ```typescript
408
+ import { pairedBootstrapCI } from '@axlsdk/eval';
409
+
410
+ const ci = pairedBootstrapCI(differences, { nResamples: 1000, alpha: 0.05, seed: 42 });
411
+ console.log(ci); // { lower: -0.02, upper: 0.15, mean: 0.065, pRegression: 0.12, pImprovement: 0.88 }
412
+ ```
413
+
353
414
  ## Eval Files in Detail
354
415
 
355
416
  ### Execution function
@@ -436,6 +497,109 @@ const ctx = runtime.createContext({
436
497
  });
437
498
  ```
438
499
 
500
+ ## Rescore Mode
501
+
502
+ Re-run scorers on saved outputs without re-executing the workflow. Useful when iterating on scorer logic — avoids burning LLM cost on generation.
503
+
504
+ ```bash
505
+ npx axl-eval rescore ./results/v1.json ./evals/qa.eval.ts
506
+ npx axl-eval rescore ./results/v1.json ./evals/qa.eval.ts --output ./results/v1-rescored.json
507
+ ```
508
+
509
+ The rescored result gets a new `id` and has `metadata.rescored: true` and `metadata.originalId` pointing to the source run. `runGroupId` and `runIndex` are stripped from inherited metadata (rescored results are independent evaluations). Only scorer cost is tracked (workflow cost is zero).
510
+
511
+ Programmatically:
512
+
513
+ ```typescript
514
+ import { rescore } from '@axlsdk/eval';
515
+
516
+ const rescored = await rescore(originalResult, [updatedScorer, newScorer], runtime, {
517
+ concurrency: 10,
518
+ });
519
+
520
+ console.log(rescored.metadata.rescored); // true
521
+ console.log(rescored.metadata.originalId); // original result ID
522
+ console.log(rescored.totalCost); // scorer cost only
523
+ ```
524
+
525
+ ## Multi-Run
526
+
527
+ Run the same eval multiple times to measure variance across runs. The CLI aggregates per-scorer means into mean ± std:
528
+
529
+ ```bash
530
+ npx axl-eval ./evals/qa.eval.ts --runs 5
531
+ npx axl-eval ./evals/qa.eval.ts --runs 5 --output ./results/qa-5runs.json
532
+ ```
533
+
534
+ ```
535
+ Eval: qa-eval x qa-basics — 5 runs
536
+ Scorer Mean ± Std Min Max
537
+ ──────────────────────────────────────────────
538
+ not-empty 1.000 ± 0.000 1.000 1.000
539
+ relevance 0.870 ± 0.024 0.840 0.900
540
+
541
+ Total Cost: $0.05 | Total Duration: 16.2s
542
+ ```
543
+
544
+ The output JSON contains all individual run results (as an array). Each run has `metadata.runGroupId` and `metadata.runIndex`.
545
+
546
+ Programmatically, use `aggregateRuns()`:
547
+
548
+ ```typescript
549
+ import { aggregateRuns } from '@axlsdk/eval';
550
+ import type { MultiRunSummary } from '@axlsdk/eval';
551
+
552
+ const summary: MultiRunSummary = aggregateRuns(runs);
553
+ console.log(summary.scorers.relevance.mean); // 0.87
554
+ console.log(summary.scorers.relevance.std); // 0.024
555
+ console.log(summary.runCount); // 5
556
+ ```
557
+
558
+ ### Multi-run comparison
559
+
560
+ `evalCompare()` accepts arrays of `EvalResult` for both baseline and candidate, pooling paired differences across runs for more robust CI estimates:
561
+
562
+ ```typescript
563
+ import { evalCompare } from '@axlsdk/eval';
564
+
565
+ const comparison = evalCompare(baselineRuns, candidateRuns);
566
+ // CI is computed from all paired (baseline[r].items[i], candidate[r].items[i]) differences
567
+ ```
568
+
569
+ ## API Reference
570
+
571
+ ### Functions
572
+
573
+ | Function | Description |
574
+ |----------|-------------|
575
+ | `dataset(config)` | Create a dataset from inline items or a JSON file |
576
+ | `scorer(config)` | Create a deterministic scorer |
577
+ | `llmScorer(config)` | Create an LLM-as-judge scorer |
578
+ | `defineEval(config)` | Wrap an eval config for CLI discovery |
579
+ | `runEval(config, executeFn, runtime)` | Run an eval programmatically |
580
+ | `evalCompare(baseline, candidate, options?)` | Compare eval results with bootstrap CI |
581
+ | `rescore(result, scorers, runtime, options?)` | Re-run scorers on saved outputs |
582
+ | `aggregateRuns(runs)` | Aggregate multiple runs into mean ± std |
583
+ | `pairedBootstrapCI(differences, options?)` | Compute bootstrap confidence interval |
584
+ | `normalizeScorerResult(value)` | Convert `number \| ScorerResult` to `ScorerResult` |
585
+
586
+ ### Types
587
+
588
+ | Type | Description |
589
+ |------|-------------|
590
+ | `EvalConfig` | Eval definition (workflow, dataset, scorers, concurrency, budget) |
591
+ | `EvalResult` | Full eval output (items, summary, cost, duration) |
592
+ | `EvalItem` | Per-item result (input, output, scores, scoreDetails, metadata) |
593
+ | `EvalSummary` | Aggregate statistics (count, failures, scorer stats, timing) |
594
+ | `EvalComparison` | Comparison output (scorer deltas, CI, pRegression/pImprovement, n, regressions, improvements) |
595
+ | `EvalCompareOptions` | Options for `evalCompare()` (`thresholds`) |
596
+ | `EvalRegression` / `EvalImprovement` | Per-item change record (itemIndex, scorer, scores, delta) |
597
+ | `ScorerDetail` | Per-scorer detail (score, metadata, duration, cost) |
598
+ | `ScorerResult` | Scorer return type (`{ score, metadata?, cost? }`) |
599
+ | `RescoreOptions` | Options for `rescore()` (`concurrency`) |
600
+ | `MultiRunSummary` | Aggregated multi-run output (per-scorer mean/std/min/max) |
601
+ | `BootstrapCIResult` | CI result (`{ lower, upper, mean, pRegression, pImprovement }`) |
602
+
439
603
  ## License
440
604
 
441
605
  [Apache 2.0](../../LICENSE)