@smthrs/evals 0.0.0-stage → 1.0.0-rc.13
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/CHANGELOG.md +142 -0
- package/LICENSE +21 -0
- package/README.md +66 -2
- package/dist/cjs/Baseline.d.ts +120 -0
- package/dist/cjs/Baseline.d.ts.map +1 -0
- package/dist/cjs/Baseline.js +160 -0
- package/dist/cjs/Baseline.js.map +7 -0
- package/dist/cjs/CaseExecutor.d.ts +85 -0
- package/dist/cjs/CaseExecutor.d.ts.map +1 -0
- package/dist/cjs/CaseExecutor.js +59 -0
- package/dist/cjs/CaseExecutor.js.map +7 -0
- package/dist/cjs/EvalError.d.ts +64 -0
- package/dist/cjs/EvalError.d.ts.map +1 -0
- package/dist/cjs/EvalError.js +54 -0
- package/dist/cjs/EvalError.js.map +7 -0
- package/dist/cjs/Gate.d.ts +55 -0
- package/dist/cjs/Gate.d.ts.map +1 -0
- package/dist/cjs/Gate.js +88 -0
- package/dist/cjs/Gate.js.map +7 -0
- package/dist/cjs/Regression.d.ts +116 -0
- package/dist/cjs/Regression.d.ts.map +1 -0
- package/dist/cjs/Regression.js +192 -0
- package/dist/cjs/Regression.js.map +7 -0
- package/dist/cjs/Report.d.ts +114 -0
- package/dist/cjs/Report.d.ts.map +1 -0
- package/dist/cjs/Report.js +161 -0
- package/dist/cjs/Report.js.map +7 -0
- package/dist/cjs/Runner.d.ts +229 -0
- package/dist/cjs/Runner.d.ts.map +1 -0
- package/dist/cjs/Runner.js +453 -0
- package/dist/cjs/Runner.js.map +7 -0
- package/dist/cjs/Suite.d.ts +139 -0
- package/dist/cjs/Suite.d.ts.map +1 -0
- package/dist/cjs/Suite.js +221 -0
- package/dist/cjs/Suite.js.map +7 -0
- package/dist/cjs/Trials.d.ts +93 -0
- package/dist/cjs/Trials.d.ts.map +1 -0
- package/dist/cjs/Trials.js +82 -0
- package/dist/cjs/Trials.js.map +7 -0
- package/dist/cjs/index.d.ts +76 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +51 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/CanonicalJson.d.ts +44 -0
- package/dist/cjs/internal/CanonicalJson.d.ts.map +1 -0
- package/dist/cjs/internal/CanonicalJson.js +114 -0
- package/dist/cjs/internal/CanonicalJson.js.map +7 -0
- package/dist/cjs/internal/controlCharacters.d.ts +44 -0
- package/dist/cjs/internal/controlCharacters.d.ts.map +1 -0
- package/dist/cjs/internal/controlCharacters.js +42 -0
- package/dist/cjs/internal/controlCharacters.js.map +7 -0
- package/dist/cjs/internal/scorerLabel.d.ts +33 -0
- package/dist/cjs/internal/scorerLabel.d.ts.map +1 -0
- package/dist/cjs/internal/scorerLabel.js +28 -0
- package/dist/cjs/internal/scorerLabel.js.map +7 -0
- package/dist/cjs/internal/tupleKey.d.ts +20 -0
- package/dist/cjs/internal/tupleKey.d.ts.map +1 -0
- package/dist/cjs/internal/tupleKey.js +25 -0
- package/dist/cjs/internal/tupleKey.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/Baseline.d.ts +120 -0
- package/dist/esm/Baseline.d.ts.map +1 -0
- package/dist/esm/Baseline.js +208 -0
- package/dist/esm/Baseline.js.map +1 -0
- package/dist/esm/CaseExecutor.d.ts +85 -0
- package/dist/esm/CaseExecutor.d.ts.map +1 -0
- package/dist/esm/CaseExecutor.js +60 -0
- package/dist/esm/CaseExecutor.js.map +1 -0
- package/dist/esm/EvalError.d.ts +64 -0
- package/dist/esm/EvalError.d.ts.map +1 -0
- package/dist/esm/EvalError.js +65 -0
- package/dist/esm/EvalError.js.map +1 -0
- package/dist/esm/Gate.d.ts +55 -0
- package/dist/esm/Gate.d.ts.map +1 -0
- package/dist/esm/Gate.js +90 -0
- package/dist/esm/Gate.js.map +1 -0
- package/dist/esm/Regression.d.ts +116 -0
- package/dist/esm/Regression.d.ts.map +1 -0
- package/dist/esm/Regression.js +200 -0
- package/dist/esm/Regression.js.map +1 -0
- package/dist/esm/Report.d.ts +114 -0
- package/dist/esm/Report.d.ts.map +1 -0
- package/dist/esm/Report.js +188 -0
- package/dist/esm/Report.js.map +1 -0
- package/dist/esm/Runner.d.ts +229 -0
- package/dist/esm/Runner.d.ts.map +1 -0
- package/dist/esm/Runner.js +455 -0
- package/dist/esm/Runner.js.map +1 -0
- package/dist/esm/Suite.d.ts +139 -0
- package/dist/esm/Suite.d.ts.map +1 -0
- package/dist/esm/Suite.js +255 -0
- package/dist/esm/Suite.js.map +1 -0
- package/dist/esm/Trials.d.ts +93 -0
- package/dist/esm/Trials.d.ts.map +1 -0
- package/dist/esm/Trials.js +117 -0
- package/dist/esm/Trials.js.map +1 -0
- package/dist/esm/index.d.ts +76 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +76 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/CanonicalJson.d.ts +44 -0
- package/dist/esm/internal/CanonicalJson.d.ts.map +1 -0
- package/dist/esm/internal/CanonicalJson.js +202 -0
- package/dist/esm/internal/CanonicalJson.js.map +1 -0
- package/dist/esm/internal/controlCharacters.d.ts +44 -0
- package/dist/esm/internal/controlCharacters.d.ts.map +1 -0
- package/dist/esm/internal/controlCharacters.js +65 -0
- package/dist/esm/internal/controlCharacters.js.map +1 -0
- package/dist/esm/internal/scorerLabel.d.ts +33 -0
- package/dist/esm/internal/scorerLabel.d.ts.map +1 -0
- package/dist/esm/internal/scorerLabel.js +33 -0
- package/dist/esm/internal/scorerLabel.js.map +1 -0
- package/dist/esm/internal/tupleKey.d.ts +20 -0
- package/dist/esm/internal/tupleKey.d.ts.map +1 -0
- package/dist/esm/internal/tupleKey.js +20 -0
- package/dist/esm/internal/tupleKey.js.map +1 -0
- package/docs/README.md +128 -0
- package/docs/api.md +626 -0
- package/docs/concepts/determinism.md +64 -0
- package/docs/concepts/fixed-suites.md +71 -0
- package/docs/concepts/step-keys.md +60 -0
- package/docs/guides/author-a-suite.md +115 -0
- package/docs/guides/gate-a-run-in-ci.md +129 -0
- package/docs/guides/maintain-a-baseline.md +91 -0
- package/docs/guides/run-a-suite.md +150 -0
- package/docs/installation.md +32 -0
- package/docs/quickstart.md +238 -0
- package/docs/troubleshooting.md +153 -0
- package/package.json +198 -3
- package/src/Baseline.ts +266 -0
- package/src/CaseExecutor.ts +104 -0
- package/src/EvalError.ts +74 -0
- package/src/Gate.ts +128 -0
- package/src/Regression.ts +321 -0
- package/src/Report.ts +268 -0
- package/src/Runner.ts +789 -0
- package/src/Suite.ts +358 -0
- package/src/Trials.ts +155 -0
- package/src/index.ts +76 -0
- package/src/internal/CanonicalJson.ts +234 -0
- package/src/internal/controlCharacters.ts +66 -0
- package/src/internal/scorerLabel.ts +35 -0
- package/src/internal/tupleKey.ts +20 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON and Markdown regression reports.
|
|
3
|
+
*
|
|
4
|
+
* The JSON report is the machine-readable artifact; the Markdown report is what
|
|
5
|
+
* an operator reads in a CI log, so it names every case the run could not
|
|
6
|
+
* decide rather than only counting them.
|
|
7
|
+
*
|
|
8
|
+
* @since 0.1.0
|
|
9
|
+
*/
|
|
10
|
+
import type { Baseline } from "./Baseline.ts";
|
|
11
|
+
import type { MissingObservation, Nondeterminism, Regression, Report as RegressionReport } from "./Regression.ts";
|
|
12
|
+
import type { CaseResult, RunResult } from "./Runner.ts";
|
|
13
|
+
/**
|
|
14
|
+
* Current serialized report format version.
|
|
15
|
+
*
|
|
16
|
+
* It versions {@link Data}, the wire shape, independently of the nested
|
|
17
|
+
* `Baseline.version`: the comparison can change shape without the committed
|
|
18
|
+
* baseline artifact changing at all.
|
|
19
|
+
*
|
|
20
|
+
* @category serialization
|
|
21
|
+
* @since 0.1.0
|
|
22
|
+
*/
|
|
23
|
+
export declare const version: 1;
|
|
24
|
+
/** An index into {@link Data.run}'s `observations` table. */
|
|
25
|
+
type ObservationRef = number;
|
|
26
|
+
/**
|
|
27
|
+
* The serialized shape of a regression report.
|
|
28
|
+
*
|
|
29
|
+
* `run.observations` is the observation table: every observation the report
|
|
30
|
+
* mentions appears in it exactly once, and everything else refers to one by
|
|
31
|
+
* index. `run.cases[i].observations` is a list of indexes, and a regression or
|
|
32
|
+
* nondeterminism entry's `actual` is a single index. An in-memory
|
|
33
|
+
* `Regression.Report` carries the same observation object in up to four
|
|
34
|
+
* places, so encoding it directly repeated every reason and every `meta` that
|
|
35
|
+
* many times.
|
|
36
|
+
*
|
|
37
|
+
* `samples` and `inconclusive` are absent by construction: both are filters of
|
|
38
|
+
* `run.observations` by `kind`, so a reader recomputes them rather than
|
|
39
|
+
* downloading a third copy.
|
|
40
|
+
*
|
|
41
|
+
* @category serialization
|
|
42
|
+
* @since 0.1.0
|
|
43
|
+
*/
|
|
44
|
+
export interface Data {
|
|
45
|
+
readonly reportVersion: typeof version;
|
|
46
|
+
readonly suite: string;
|
|
47
|
+
readonly baseline: Baseline;
|
|
48
|
+
readonly run: Omit<RunResult, "cases"> & {
|
|
49
|
+
readonly cases: ReadonlyArray<Omit<CaseResult, "observations"> & {
|
|
50
|
+
readonly observations: ReadonlyArray<ObservationRef>;
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
53
|
+
readonly regressions: ReadonlyArray<Omit<Regression, "actual"> & {
|
|
54
|
+
readonly actual: ObservationRef;
|
|
55
|
+
}>;
|
|
56
|
+
readonly nondeterminism: ReadonlyArray<Omit<Nondeterminism, "actual"> & {
|
|
57
|
+
readonly actual: ObservationRef;
|
|
58
|
+
}>;
|
|
59
|
+
readonly missing: ReadonlyArray<MissingObservation>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Projects a comparison into its serialized form.
|
|
63
|
+
*
|
|
64
|
+
* The observation table starts as `run.observations` and grows to hold any
|
|
65
|
+
* observation reached only through a case or a comparison entry, so a
|
|
66
|
+
* hand-built report whose case lists are not flattened still serializes every
|
|
67
|
+
* reference. Observations are pooled by identity, not by value: two distinct
|
|
68
|
+
* objects that happen to be equal stay two rows, because collapsing them would
|
|
69
|
+
* claim one grading where there were two.
|
|
70
|
+
*
|
|
71
|
+
* @category serialization
|
|
72
|
+
* @since 0.1.0
|
|
73
|
+
*/
|
|
74
|
+
export declare const data: (report: RegressionReport) => Data;
|
|
75
|
+
/**
|
|
76
|
+
* Serializes a regression report as stable, sorted-key JSON.
|
|
77
|
+
*
|
|
78
|
+
* The wire shape is {@link Data}, stamped with {@link version}: each
|
|
79
|
+
* observation is encoded once and referred to by index everywhere else. The
|
|
80
|
+
* report embeds each case's raw `execution.output`, which comes from an
|
|
81
|
+
* arbitrary target flow, so the encoding is total rather than trusting: keys
|
|
82
|
+
* are sorted by code unit, embedded strings are capped, total traversal is
|
|
83
|
+
* bounded, and anything JSON cannot express becomes a named marker
|
|
84
|
+
* (`[circular]`, `[NaN]`, `[function]`, `[budget exceeded]`) instead of a
|
|
85
|
+
* `RangeError` or a silent `null`. Two identical runs therefore produce
|
|
86
|
+
* byte-identical JSON.
|
|
87
|
+
*
|
|
88
|
+
* Nothing redacts the embedded output. A suite whose cases carry secrets must
|
|
89
|
+
* not print this report where the log is readable.
|
|
90
|
+
*
|
|
91
|
+
* @category serialization
|
|
92
|
+
* @since 0.1.0
|
|
93
|
+
*/
|
|
94
|
+
export declare const json: (report: RegressionReport) => string;
|
|
95
|
+
/**
|
|
96
|
+
* Renders a concise stable Markdown regression report.
|
|
97
|
+
*
|
|
98
|
+
* Every count in the summary that is not zero has a section naming its rows:
|
|
99
|
+
* the regressions and the nondeterminism a gate reads as red, and the case
|
|
100
|
+
* failures, missing observations, and inconclusive observations that leave a
|
|
101
|
+
* gate undecided. A report that only counted them named nothing an operator
|
|
102
|
+
* could act on, which is exactly the run that needs debugging.
|
|
103
|
+
*
|
|
104
|
+
* Every cell and the suite heading value replace C0 controls and DEL with
|
|
105
|
+
* spaces and escape backslashes, pipes, inline GFM syntax, raw HTML, entities,
|
|
106
|
+
* and URL/email autolinks. Values are capped at 240 escaped UTF-16 code units,
|
|
107
|
+
* plus an ellipsis when truncated, without splitting escapes or code points.
|
|
108
|
+
*
|
|
109
|
+
* @category rendering
|
|
110
|
+
* @since 0.1.0
|
|
111
|
+
*/
|
|
112
|
+
export declare const markdown: (report: RegressionReport) => string;
|
|
113
|
+
export {};
|
|
114
|
+
//# sourceMappingURL=Report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Report.d.ts","sourceRoot":"","sources":["../../src/Report.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAI7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,IAAI,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACjH,OAAO,KAAK,EAAE,UAAU,EAAe,SAAS,EAAE,MAAM,aAAa,CAAA;AAErE;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,EAAG,CAAU,CAAA;AAEjC,6DAA6D;AAC7D,KAAK,cAAc,GAAG,MAAM,CAAA;AAE5B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,aAAa,EAAE,OAAO,OAAO,CAAA;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,GAAG,EACR,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,GACxB;QACA,QAAQ,CAAC,KAAK,EAAE,aAAa,CACzB,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,GAChC;YAAE,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;SAAE,CAC3D,CAAA;KACF,CAAA;IACH,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG;QAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAC,CAAA;IACrG,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,GAAG;QAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAC,CAAA;IAC5G,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;CACpD;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,IAAI,GAAI,QAAQ,gBAAgB,KAAG,IAsB/C,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,IAAI,GAAI,QAAQ,gBAAgB,KAAG,MAC2C,CAAA;AAkD3F;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ,gBAAgB,KAAG,MA4EnD,CAAA"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON and Markdown regression reports.
|
|
3
|
+
*
|
|
4
|
+
* The JSON report is the machine-readable artifact; the Markdown report is what
|
|
5
|
+
* an operator reads in a CI log, so it names every case the run could not
|
|
6
|
+
* decide rather than only counting them.
|
|
7
|
+
*
|
|
8
|
+
* @since 0.1.0
|
|
9
|
+
*/
|
|
10
|
+
import { CanonicalJson } from "./internal/CanonicalJson.js";
|
|
11
|
+
import { isControlCodePoint } from "./internal/controlCharacters.js";
|
|
12
|
+
import { scorerLabel } from "./internal/scorerLabel.js";
|
|
13
|
+
/**
|
|
14
|
+
* Current serialized report format version.
|
|
15
|
+
*
|
|
16
|
+
* It versions {@link Data}, the wire shape, independently of the nested
|
|
17
|
+
* `Baseline.version`: the comparison can change shape without the committed
|
|
18
|
+
* baseline artifact changing at all.
|
|
19
|
+
*
|
|
20
|
+
* @category serialization
|
|
21
|
+
* @since 0.1.0
|
|
22
|
+
*/
|
|
23
|
+
export const version = 1;
|
|
24
|
+
/**
|
|
25
|
+
* Projects a comparison into its serialized form.
|
|
26
|
+
*
|
|
27
|
+
* The observation table starts as `run.observations` and grows to hold any
|
|
28
|
+
* observation reached only through a case or a comparison entry, so a
|
|
29
|
+
* hand-built report whose case lists are not flattened still serializes every
|
|
30
|
+
* reference. Observations are pooled by identity, not by value: two distinct
|
|
31
|
+
* objects that happen to be equal stay two rows, because collapsing them would
|
|
32
|
+
* claim one grading where there were two.
|
|
33
|
+
*
|
|
34
|
+
* @category serialization
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
*/
|
|
37
|
+
export const data = (report) => {
|
|
38
|
+
const positions = new Map();
|
|
39
|
+
const observations = [];
|
|
40
|
+
const refer = (observation) => {
|
|
41
|
+
const known = positions.get(observation);
|
|
42
|
+
if (known !== undefined)
|
|
43
|
+
return known;
|
|
44
|
+
const position = observations.length;
|
|
45
|
+
observations.push(observation);
|
|
46
|
+
positions.set(observation, position);
|
|
47
|
+
return position;
|
|
48
|
+
};
|
|
49
|
+
for (const observation of report.run.observations)
|
|
50
|
+
refer(observation);
|
|
51
|
+
const cases = report.run.cases.map((result) => ({ ...result, observations: result.observations.map(refer) }));
|
|
52
|
+
return {
|
|
53
|
+
reportVersion: version,
|
|
54
|
+
suite: report.suite,
|
|
55
|
+
baseline: report.baseline,
|
|
56
|
+
run: { ...report.run, cases, observations },
|
|
57
|
+
regressions: report.regressions.map((entry) => ({ ...entry, actual: refer(entry.actual) })),
|
|
58
|
+
nondeterminism: report.nondeterminism.map((entry) => ({ ...entry, actual: refer(entry.actual) })),
|
|
59
|
+
missing: report.missing
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Serializes a regression report as stable, sorted-key JSON.
|
|
64
|
+
*
|
|
65
|
+
* The wire shape is {@link Data}, stamped with {@link version}: each
|
|
66
|
+
* observation is encoded once and referred to by index everywhere else. The
|
|
67
|
+
* report embeds each case's raw `execution.output`, which comes from an
|
|
68
|
+
* arbitrary target flow, so the encoding is total rather than trusting: keys
|
|
69
|
+
* are sorted by code unit, embedded strings are capped, total traversal is
|
|
70
|
+
* bounded, and anything JSON cannot express becomes a named marker
|
|
71
|
+
* (`[circular]`, `[NaN]`, `[function]`, `[budget exceeded]`) instead of a
|
|
72
|
+
* `RangeError` or a silent `null`. Two identical runs therefore produce
|
|
73
|
+
* byte-identical JSON.
|
|
74
|
+
*
|
|
75
|
+
* Nothing redacts the embedded output. A suite whose cases carry secrets must
|
|
76
|
+
* not print this report where the log is readable.
|
|
77
|
+
*
|
|
78
|
+
* @category serialization
|
|
79
|
+
* @since 0.1.0
|
|
80
|
+
*/
|
|
81
|
+
export const json = (report) => CanonicalJson.stringify(data(report), { maxStringLength: CanonicalJson.maxStringLength });
|
|
82
|
+
/** Maximum escaped UTF-16 code units per value, before a truncation ellipsis. */
|
|
83
|
+
const maxCellLength = 240;
|
|
84
|
+
const cell = (value) => {
|
|
85
|
+
let text = "";
|
|
86
|
+
if (value !== undefined && value !== null) {
|
|
87
|
+
if (typeof value === "string")
|
|
88
|
+
text = value;
|
|
89
|
+
else {
|
|
90
|
+
try {
|
|
91
|
+
text = String(value);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
text = "[unreadable]";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
let escaped = "";
|
|
99
|
+
for (const character of text) {
|
|
100
|
+
const code = character.codePointAt(0);
|
|
101
|
+
// Escape each input character once, including backslashes before pipes.
|
|
102
|
+
// Ampersands prevent entities; dots, colons, and @ prevent GFM autolinks.
|
|
103
|
+
const token = isControlCodePoint(code)
|
|
104
|
+
? " "
|
|
105
|
+
: /[\\|`*_[\]<>!#~&.:@]/u.test(character)
|
|
106
|
+
? `\\${character}`
|
|
107
|
+
: character;
|
|
108
|
+
if (escaped.length + token.length > maxCellLength)
|
|
109
|
+
return `${escaped}…`;
|
|
110
|
+
escaped += token;
|
|
111
|
+
}
|
|
112
|
+
return escaped;
|
|
113
|
+
};
|
|
114
|
+
const scorerCell = (scorer, scorerName) => cell(scorerLabel({ scorer, scorerName }));
|
|
115
|
+
const section = (heading, header, alignment, rows) => rows.length === 0 ? [] : [
|
|
116
|
+
`## ${heading}`,
|
|
117
|
+
"",
|
|
118
|
+
`| ${header.join(" | ")} |`,
|
|
119
|
+
`| ${alignment.join(" | ")} |`,
|
|
120
|
+
...rows.map((row) => `| ${row.join(" | ")} |`),
|
|
121
|
+
""
|
|
122
|
+
];
|
|
123
|
+
/**
|
|
124
|
+
* Renders a concise stable Markdown regression report.
|
|
125
|
+
*
|
|
126
|
+
* Every count in the summary that is not zero has a section naming its rows:
|
|
127
|
+
* the regressions and the nondeterminism a gate reads as red, and the case
|
|
128
|
+
* failures, missing observations, and inconclusive observations that leave a
|
|
129
|
+
* gate undecided. A report that only counted them named nothing an operator
|
|
130
|
+
* could act on, which is exactly the run that needs debugging.
|
|
131
|
+
*
|
|
132
|
+
* Every cell and the suite heading value replace C0 controls and DEL with
|
|
133
|
+
* spaces and escape backslashes, pipes, inline GFM syntax, raw HTML, entities,
|
|
134
|
+
* and URL/email autolinks. Values are capped at 240 escaped UTF-16 code units,
|
|
135
|
+
* plus an ellipsis when truncated, without splitting escapes or code points.
|
|
136
|
+
*
|
|
137
|
+
* @category rendering
|
|
138
|
+
* @since 0.1.0
|
|
139
|
+
*/
|
|
140
|
+
export const markdown = (report) => {
|
|
141
|
+
const lines = [
|
|
142
|
+
`# Evaluation report: ${cell(report.suite)}`,
|
|
143
|
+
"",
|
|
144
|
+
`- Regressions: ${report.regressions.length}`,
|
|
145
|
+
`- Nondeterminism: ${report.nondeterminism.length}`,
|
|
146
|
+
`- Missing observations: ${report.missing.length}`,
|
|
147
|
+
`- Inconclusive observations: ${report.inconclusive.length}`,
|
|
148
|
+
`- Failed cases: ${report.run.cases.filter((result) => result.error !== undefined).length}`,
|
|
149
|
+
...(report.run.trials === undefined ? [] : [
|
|
150
|
+
`- pass@1: ${report.run.trials.passAt1.toFixed(6)}`,
|
|
151
|
+
`- pass@${report.run.k ?? 1}: ${report.run.trials.passAtK.toFixed(6)}`,
|
|
152
|
+
`- pass^${report.run.k ?? 1}: ${report.run.trials.passHatK.toFixed(6)}`,
|
|
153
|
+
`- stderr: ${(Object.values(report.run.trials.perCase).reduce((sum, item) => sum + item.stderr, 0) / report.run.trials.cases)
|
|
154
|
+
.toFixed(6)}`
|
|
155
|
+
]),
|
|
156
|
+
"",
|
|
157
|
+
...section("Regressions", ["Case", "Scorer", "Baseline", "Actual", "Drop"], ["---", "---", "---:", "---:", "---:"], report.regressions.map((item) => [
|
|
158
|
+
cell(item.case),
|
|
159
|
+
scorerCell(item.scorer, item.actual.scorerName),
|
|
160
|
+
item.baseline.score.toFixed(6),
|
|
161
|
+
item.actual.score.toFixed(6),
|
|
162
|
+
item.drop.toFixed(6)
|
|
163
|
+
])),
|
|
164
|
+
...section("Nondeterminism", ["Case", "Scorer", "Step key", "Baseline", "Actual", "Delta"], ["---", "---", "---", "---:", "---:", "---:"], report.nondeterminism.map((item) => [
|
|
165
|
+
cell(item.case),
|
|
166
|
+
scorerCell(item.scorer, item.actual.scorerName),
|
|
167
|
+
cell(item.actual.stepKey),
|
|
168
|
+
item.baseline.score.toFixed(6),
|
|
169
|
+
item.actual.score.toFixed(6),
|
|
170
|
+
item.delta.toFixed(6)
|
|
171
|
+
])),
|
|
172
|
+
...section("Case failures", ["Case", "Code", "Message"], ["---", "---", "---"], report.run.cases.flatMap((result) => result.error === undefined ? [] : [[cell(result.case), cell(result.error.code), cell(result.error.message)]])),
|
|
173
|
+
...section("Missing observations", ["Side", "Case", "Scorer", "Step key"], ["---", "---", "---", "---"], report.missing.map((item) => [
|
|
174
|
+
cell(item.side),
|
|
175
|
+
cell(item.case),
|
|
176
|
+
scorerCell(item.scorer, item.scorerName),
|
|
177
|
+
cell(item.stepKey)
|
|
178
|
+
])),
|
|
179
|
+
...section("Inconclusive", ["Case", "Scorer", "Step key", "Reason"], ["---", "---", "---", "---"], report.inconclusive.map((item) => [
|
|
180
|
+
cell(item.case),
|
|
181
|
+
scorerCell(item.scorer, item.scorerName),
|
|
182
|
+
cell(item.stepKey),
|
|
183
|
+
cell(item.reason)
|
|
184
|
+
]))
|
|
185
|
+
];
|
|
186
|
+
return `${lines.join("\n").trim()}\n`;
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=Report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Report.js","sourceRoot":"","sources":["../../src/Report.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Canonical JSON and Markdown regression reports.\n *\n * The JSON report is the machine-readable artifact; the Markdown report is what\n * an operator reads in a CI log, so it names every case the run could not\n * decide rather than only counting them.\n *\n * @since 0.1.0\n */\n\nimport type { Baseline } from \"./Baseline.ts\"\nimport { CanonicalJson } from \"./internal/CanonicalJson.ts\"\nimport { isControlCodePoint } from \"./internal/controlCharacters.ts\"\nimport { scorerLabel } from \"./internal/scorerLabel.ts\"\nimport type { MissingObservation, Nondeterminism, Regression, Report as RegressionReport } from \"./Regression.ts\"\nimport type { CaseResult, Observation, RunResult } from \"./Runner.ts\"\n\n/**\n * Current serialized report format version.\n *\n * It versions {@link Data}, the wire shape, independently of the nested\n * `Baseline.version`: the comparison can change shape without the committed\n * baseline artifact changing at all.\n *\n * @category serialization\n * @since 0.1.0\n */\nexport const version = 1 as const\n\n/** An index into {@link Data.run}'s `observations` table. */\ntype ObservationRef = number\n\n/**\n * The serialized shape of a regression report.\n *\n * `run.observations` is the observation table: every observation the report\n * mentions appears in it exactly once, and everything else refers to one by\n * index. `run.cases[i].observations` is a list of indexes, and a regression or\n * nondeterminism entry's `actual` is a single index. An in-memory\n * `Regression.Report` carries the same observation object in up to four\n * places, so encoding it directly repeated every reason and every `meta` that\n * many times.\n *\n * `samples` and `inconclusive` are absent by construction: both are filters of\n * `run.observations` by `kind`, so a reader recomputes them rather than\n * downloading a third copy.\n *\n * @category serialization\n * @since 0.1.0\n */\nexport interface Data {\n readonly reportVersion: typeof version\n readonly suite: string\n readonly baseline: Baseline\n readonly run:\n & Omit<RunResult, \"cases\">\n & {\n readonly cases: ReadonlyArray<\n & Omit<CaseResult, \"observations\">\n & { readonly observations: ReadonlyArray<ObservationRef> }\n >\n }\n readonly regressions: ReadonlyArray<Omit<Regression, \"actual\"> & { readonly actual: ObservationRef }>\n readonly nondeterminism: ReadonlyArray<Omit<Nondeterminism, \"actual\"> & { readonly actual: ObservationRef }>\n readonly missing: ReadonlyArray<MissingObservation>\n}\n\n/**\n * Projects a comparison into its serialized form.\n *\n * The observation table starts as `run.observations` and grows to hold any\n * observation reached only through a case or a comparison entry, so a\n * hand-built report whose case lists are not flattened still serializes every\n * reference. Observations are pooled by identity, not by value: two distinct\n * objects that happen to be equal stay two rows, because collapsing them would\n * claim one grading where there were two.\n *\n * @category serialization\n * @since 0.1.0\n */\nexport const data = (report: RegressionReport): Data => {\n const positions = new Map<Observation, ObservationRef>()\n const observations: Array<Observation> = []\n const refer = (observation: Observation): ObservationRef => {\n const known = positions.get(observation)\n if (known !== undefined) return known\n const position = observations.length\n observations.push(observation)\n positions.set(observation, position)\n return position\n }\n for (const observation of report.run.observations) refer(observation)\n const cases = report.run.cases.map((result) => ({ ...result, observations: result.observations.map(refer) }))\n return {\n reportVersion: version,\n suite: report.suite,\n baseline: report.baseline,\n run: { ...report.run, cases, observations },\n regressions: report.regressions.map((entry) => ({ ...entry, actual: refer(entry.actual) })),\n nondeterminism: report.nondeterminism.map((entry) => ({ ...entry, actual: refer(entry.actual) })),\n missing: report.missing\n }\n}\n\n/**\n * Serializes a regression report as stable, sorted-key JSON.\n *\n * The wire shape is {@link Data}, stamped with {@link version}: each\n * observation is encoded once and referred to by index everywhere else. The\n * report embeds each case's raw `execution.output`, which comes from an\n * arbitrary target flow, so the encoding is total rather than trusting: keys\n * are sorted by code unit, embedded strings are capped, total traversal is\n * bounded, and anything JSON cannot express becomes a named marker\n * (`[circular]`, `[NaN]`, `[function]`, `[budget exceeded]`) instead of a\n * `RangeError` or a silent `null`. Two identical runs therefore produce\n * byte-identical JSON.\n *\n * Nothing redacts the embedded output. A suite whose cases carry secrets must\n * not print this report where the log is readable.\n *\n * @category serialization\n * @since 0.1.0\n */\nexport const json = (report: RegressionReport): string =>\n CanonicalJson.stringify(data(report), { maxStringLength: CanonicalJson.maxStringLength })\n\n/** Maximum escaped UTF-16 code units per value, before a truncation ellipsis. */\nconst maxCellLength = 240\n\nconst cell = (value: unknown): string => {\n let text = \"\"\n if (value !== undefined && value !== null) {\n if (typeof value === \"string\") text = value\n else {\n try {\n text = String(value)\n } catch {\n text = \"[unreadable]\"\n }\n }\n }\n let escaped = \"\"\n for (const character of text) {\n const code = character.codePointAt(0)!\n // Escape each input character once, including backslashes before pipes.\n // Ampersands prevent entities; dots, colons, and @ prevent GFM autolinks.\n const token = isControlCodePoint(code)\n ? \" \"\n : /[\\\\|`*_[\\]<>!#~&.:@]/u.test(character)\n ? `\\\\${character}`\n : character\n if (escaped.length + token.length > maxCellLength) return `${escaped}…`\n escaped += token\n }\n return escaped\n}\n\nconst scorerCell = (scorer: string, scorerName: string | undefined): string => cell(scorerLabel({ scorer, scorerName }))\n\nconst section = (\n heading: string,\n header: ReadonlyArray<string>,\n alignment: ReadonlyArray<string>,\n rows: ReadonlyArray<ReadonlyArray<string>>\n): ReadonlyArray<string> =>\n rows.length === 0 ? [] : [\n `## ${heading}`,\n \"\",\n `| ${header.join(\" | \")} |`,\n `| ${alignment.join(\" | \")} |`,\n ...rows.map((row) => `| ${row.join(\" | \")} |`),\n \"\"\n ]\n\n/**\n * Renders a concise stable Markdown regression report.\n *\n * Every count in the summary that is not zero has a section naming its rows:\n * the regressions and the nondeterminism a gate reads as red, and the case\n * failures, missing observations, and inconclusive observations that leave a\n * gate undecided. A report that only counted them named nothing an operator\n * could act on, which is exactly the run that needs debugging.\n *\n * Every cell and the suite heading value replace C0 controls and DEL with\n * spaces and escape backslashes, pipes, inline GFM syntax, raw HTML, entities,\n * and URL/email autolinks. Values are capped at 240 escaped UTF-16 code units,\n * plus an ellipsis when truncated, without splitting escapes or code points.\n *\n * @category rendering\n * @since 0.1.0\n */\nexport const markdown = (report: RegressionReport): string => {\n const lines = [\n `# Evaluation report: ${cell(report.suite)}`,\n \"\",\n `- Regressions: ${report.regressions.length}`,\n `- Nondeterminism: ${report.nondeterminism.length}`,\n `- Missing observations: ${report.missing.length}`,\n `- Inconclusive observations: ${report.inconclusive.length}`,\n `- Failed cases: ${report.run.cases.filter((result) => result.error !== undefined).length}`,\n ...(report.run.trials === undefined ? [] : [\n `- pass@1: ${report.run.trials.passAt1.toFixed(6)}`,\n `- pass@${report.run.k ?? 1}: ${report.run.trials.passAtK.toFixed(6)}`,\n `- pass^${report.run.k ?? 1}: ${report.run.trials.passHatK.toFixed(6)}`,\n `- stderr: ${\n (Object.values(report.run.trials.perCase).reduce((sum, item) => sum + item.stderr, 0) / report.run.trials.cases)\n .toFixed(6)\n }`\n ]),\n \"\",\n ...section(\n \"Regressions\",\n [\"Case\", \"Scorer\", \"Baseline\", \"Actual\", \"Drop\"],\n [\"---\", \"---\", \"---:\", \"---:\", \"---:\"],\n report.regressions.map((item) => [\n cell(item.case),\n scorerCell(item.scorer, item.actual.scorerName),\n item.baseline.score.toFixed(6),\n item.actual.score.toFixed(6),\n item.drop.toFixed(6)\n ])\n ),\n ...section(\n \"Nondeterminism\",\n [\"Case\", \"Scorer\", \"Step key\", \"Baseline\", \"Actual\", \"Delta\"],\n [\"---\", \"---\", \"---\", \"---:\", \"---:\", \"---:\"],\n report.nondeterminism.map((item) => [\n cell(item.case),\n scorerCell(item.scorer, item.actual.scorerName),\n cell(item.actual.stepKey),\n item.baseline.score.toFixed(6),\n item.actual.score.toFixed(6),\n item.delta.toFixed(6)\n ])\n ),\n ...section(\n \"Case failures\",\n [\"Case\", \"Code\", \"Message\"],\n [\"---\", \"---\", \"---\"],\n report.run.cases.flatMap((result) =>\n result.error === undefined ? [] : [[cell(result.case), cell(result.error.code), cell(result.error.message)]]\n )\n ),\n ...section(\n \"Missing observations\",\n [\"Side\", \"Case\", \"Scorer\", \"Step key\"],\n [\"---\", \"---\", \"---\", \"---\"],\n report.missing.map((item) => [\n cell(item.side),\n cell(item.case),\n scorerCell(item.scorer, item.scorerName),\n cell(item.stepKey)\n ])\n ),\n ...section(\n \"Inconclusive\",\n [\"Case\", \"Scorer\", \"Step key\", \"Reason\"],\n [\"---\", \"---\", \"---\", \"---\"],\n report.inconclusive.map((item) => [\n cell(item.case),\n scorerCell(item.scorer, item.scorerName),\n cell(item.stepKey),\n cell(item.reason)\n ])\n )\n ]\n return `${lines.join(\"\\n\").trim()}\\n`\n}\n"]}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic fixed-suite execution and bound scorer evaluation.
|
|
3
|
+
*
|
|
4
|
+
* A run executes every case through the injected {@link CaseExecutor}, then
|
|
5
|
+
* grades each execution with the scorers bound to the flow it executed. The
|
|
6
|
+
* timestamp and the run identity come from the caller, so two runs of the same
|
|
7
|
+
* suite over the same inputs produce byte-identical observations.
|
|
8
|
+
*
|
|
9
|
+
* @since 0.1.0
|
|
10
|
+
*/
|
|
11
|
+
import * as ScorerRunner from "@smthrs/scorers/Runner";
|
|
12
|
+
import type * as ScoreStore from "@smthrs/scorers/ScoreStore";
|
|
13
|
+
import * as Context from "effect/Context";
|
|
14
|
+
import * as Effect from "effect/Effect";
|
|
15
|
+
import * as Layer from "effect/Layer";
|
|
16
|
+
import { CaseExecutor, type Execution } from "./CaseExecutor.ts";
|
|
17
|
+
import { EvalError } from "./EvalError.ts";
|
|
18
|
+
import type { Binding, Suite } from "./Suite.ts";
|
|
19
|
+
import * as Trials from "./Trials.ts";
|
|
20
|
+
/**
|
|
21
|
+
* One score observation emitted by a suite run.
|
|
22
|
+
*
|
|
23
|
+
* `scorer` is the scorer key, a digest of the scorer's own declaration, which
|
|
24
|
+
* is what a baseline matches on. `scorerName` is the human name from the same
|
|
25
|
+
* declaration, carried alongside so a report can be read without grepping for
|
|
26
|
+
* the digest. `at` is the run's timestamp, not the scorer's: a run's
|
|
27
|
+
* observations all carry one instant so a baseline stays reproducible.
|
|
28
|
+
*
|
|
29
|
+
* @category models
|
|
30
|
+
* @since 0.1.0
|
|
31
|
+
*/
|
|
32
|
+
export type Observation = {
|
|
33
|
+
readonly case: string;
|
|
34
|
+
readonly scorer: string;
|
|
35
|
+
readonly scorerName?: string | undefined;
|
|
36
|
+
readonly stepKey: string;
|
|
37
|
+
readonly at: string;
|
|
38
|
+
} & ({
|
|
39
|
+
readonly kind: "score";
|
|
40
|
+
readonly score: number;
|
|
41
|
+
readonly reason?: string | undefined;
|
|
42
|
+
readonly meta?: unknown;
|
|
43
|
+
} | {
|
|
44
|
+
readonly kind: "inconclusive";
|
|
45
|
+
readonly reason: string;
|
|
46
|
+
readonly meta?: unknown;
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* A request sent to the scorers batch runner.
|
|
50
|
+
*
|
|
51
|
+
* @category models
|
|
52
|
+
* @since 0.1.0
|
|
53
|
+
*/
|
|
54
|
+
export interface ScoreRequest {
|
|
55
|
+
readonly case: string;
|
|
56
|
+
readonly stepKey: string;
|
|
57
|
+
readonly binding: Binding;
|
|
58
|
+
readonly input: {
|
|
59
|
+
readonly input: unknown;
|
|
60
|
+
readonly output: unknown;
|
|
61
|
+
readonly groundTruth?: unknown;
|
|
62
|
+
readonly context?: unknown;
|
|
63
|
+
readonly latencyMs?: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A blocking scorer job, matching the `Runner` module of `@smthrs/scorers`.
|
|
68
|
+
*
|
|
69
|
+
* @category models
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
*/
|
|
72
|
+
export type ScoreJob = ScorerRunner.Job;
|
|
73
|
+
/**
|
|
74
|
+
* Structural adapter for `@smthrs/scorers`' blocking batch runner.
|
|
75
|
+
*
|
|
76
|
+
* A runner that implements `runBatchCorrelated` tags every observation with
|
|
77
|
+
* its job identity and may return results in any order. A runner that implements
|
|
78
|
+
* only `runBatch` is correlated positionally: it must return exactly one
|
|
79
|
+
* observation per job, in order, and each observation must echo its job's
|
|
80
|
+
* `targetStepKey` and `scorerKey`. A run refuses the order-only protocol before
|
|
81
|
+
* scoring when two jobs share that pair, because their results could not be
|
|
82
|
+
* attributed to a case.
|
|
83
|
+
*
|
|
84
|
+
* A run fails with `scorer_protocol` when either protocol is broken.
|
|
85
|
+
* `@smthrs/scorers`' `Runner.Service` implements `runBatchCorrelated`, so its
|
|
86
|
+
* service value can be used directly and `ambiguous_score_job` never applies
|
|
87
|
+
* to it.
|
|
88
|
+
*
|
|
89
|
+
* @category services
|
|
90
|
+
* @since 0.1.0
|
|
91
|
+
*/
|
|
92
|
+
export interface ScoreBatchRunner {
|
|
93
|
+
readonly runBatch: (jobs: ReadonlyArray<ScoreJob>, options?: {
|
|
94
|
+
readonly concurrency?: number | undefined;
|
|
95
|
+
}) => Effect.Effect<ReadonlyArray<ScoreObservation>, unknown>;
|
|
96
|
+
readonly runBatchCorrelated?: ((jobs: ReadonlyArray<ScoreJob>, options?: {
|
|
97
|
+
readonly concurrency?: number | undefined;
|
|
98
|
+
}) => Effect.Effect<ReadonlyArray<BatchResult>, unknown>) | undefined;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A score result aligned with a {@link ScoreRequest}.
|
|
102
|
+
*
|
|
103
|
+
* @category models
|
|
104
|
+
* @since 0.1.0
|
|
105
|
+
*/
|
|
106
|
+
export type ScoreObservation = ScoreStore.Observation;
|
|
107
|
+
/**
|
|
108
|
+
* A batch result tagged with the identity of the job that produced it.
|
|
109
|
+
*
|
|
110
|
+
* @category models
|
|
111
|
+
* @since 0.1.0
|
|
112
|
+
*/
|
|
113
|
+
export interface BatchResult {
|
|
114
|
+
readonly identity: string;
|
|
115
|
+
readonly observation: ScoreObservation;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Per-case result retained by the deterministic runner.
|
|
119
|
+
*
|
|
120
|
+
* @category models
|
|
121
|
+
* @since 0.1.0
|
|
122
|
+
*/
|
|
123
|
+
export interface CaseResult {
|
|
124
|
+
readonly case: string;
|
|
125
|
+
readonly execution?: Execution | undefined;
|
|
126
|
+
readonly error?: EvalError | undefined;
|
|
127
|
+
readonly observations: ReadonlyArray<Observation>;
|
|
128
|
+
readonly trials?: Trials.Summary | undefined;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Stable result of a suite run.
|
|
132
|
+
*
|
|
133
|
+
* @category models
|
|
134
|
+
* @since 0.1.0
|
|
135
|
+
*/
|
|
136
|
+
export interface RunResult {
|
|
137
|
+
readonly runId: string;
|
|
138
|
+
readonly suite: string;
|
|
139
|
+
readonly cases: ReadonlyArray<CaseResult>;
|
|
140
|
+
readonly observations: ReadonlyArray<Observation>;
|
|
141
|
+
readonly trials?: Trials.Aggregate | undefined;
|
|
142
|
+
readonly k?: number | undefined;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Options for a deterministic suite run.
|
|
146
|
+
*
|
|
147
|
+
* @category models
|
|
148
|
+
* @since 0.1.0
|
|
149
|
+
*/
|
|
150
|
+
export interface RunOptions {
|
|
151
|
+
readonly scorer?: ScoreBatchRunner | undefined;
|
|
152
|
+
readonly runId: string;
|
|
153
|
+
readonly sampleId?: string | undefined;
|
|
154
|
+
readonly trials?: number | undefined;
|
|
155
|
+
readonly k?: number | undefined;
|
|
156
|
+
readonly at: string;
|
|
157
|
+
}
|
|
158
|
+
declare const Runner_base: Context.ServiceClass<Runner, "flows/evals/Runner", ScoreBatchRunner>;
|
|
159
|
+
/**
|
|
160
|
+
* Injectable batch-runner service used when a caller wants a reusable adapter.
|
|
161
|
+
*
|
|
162
|
+
* {@link run} does not require it: a run scores with `options.scorer` when one
|
|
163
|
+
* is passed, otherwise with this service when one is provided, otherwise
|
|
164
|
+
* in process through {@link makeInline}.
|
|
165
|
+
*
|
|
166
|
+
* @category services
|
|
167
|
+
* @since 0.1.0
|
|
168
|
+
*/
|
|
169
|
+
export declare class Runner extends Runner_base {
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Builds the in-process batch runner a run scores with by default.
|
|
173
|
+
*
|
|
174
|
+
* Each job's scorer runs in the current process, its result is checked by
|
|
175
|
+
* `@smthrs/scorers`' own `Scorer.validate`, and a scorer that fails becomes an
|
|
176
|
+
* inconclusive observation naming its cause rather than failing the run. It
|
|
177
|
+
* honours the {@link ScoreBatchRunner} protocol, so it is also the reference
|
|
178
|
+
* a custom adapter can be compared against.
|
|
179
|
+
*
|
|
180
|
+
* @category constructors
|
|
181
|
+
* @since 0.1.0
|
|
182
|
+
*/
|
|
183
|
+
export declare const makeInline: () => ScoreBatchRunner;
|
|
184
|
+
/**
|
|
185
|
+
* Provides the in-process batch runner built by {@link makeInline}.
|
|
186
|
+
*
|
|
187
|
+
* @category layers
|
|
188
|
+
* @since 0.1.0
|
|
189
|
+
*/
|
|
190
|
+
export declare const layerInline: Layer.Layer<Runner>;
|
|
191
|
+
/**
|
|
192
|
+
* Runs a fixed suite with bounded execution and declaration-order results.
|
|
193
|
+
*
|
|
194
|
+
* Cases run through the provided {@link CaseExecutor} at the suite's
|
|
195
|
+
* concurrency and are returned in declaration order. Each executor receives
|
|
196
|
+
* its own mutable case copy. Each scorer receives independent copies of the
|
|
197
|
+
* original case input, expected value, and binding data. Every execution is then
|
|
198
|
+
* graded by the bindings whose `appliesTo` is the flow the execution reports as
|
|
199
|
+
* its target, matched by reference identity, so a binding attached to another
|
|
200
|
+
* flow contributes no observations.
|
|
201
|
+
*
|
|
202
|
+
* Scoring goes through `options.scorer` when the caller passes one, the
|
|
203
|
+
* {@link Runner} service when one is provided, and {@link makeInline}
|
|
204
|
+
* otherwise. A case whose target failed keeps its typed error, locates itself
|
|
205
|
+
* with the executor's own `path` or with `cases['<name>']`, and produces no
|
|
206
|
+
* observations; a scorer that failed produces an inconclusive observation. The
|
|
207
|
+
* run itself fails only with `invalid_run_options` for a non-canonical run
|
|
208
|
+
* identity or timestamp, `invalid_suite` for an unusable sampling policy,
|
|
209
|
+
* `ambiguous_score_job` when an order-only runner cannot distinguish two jobs,
|
|
210
|
+
* or `scorer_protocol` for a batch runner that broke the
|
|
211
|
+
* {@link ScoreBatchRunner} contract.
|
|
212
|
+
*
|
|
213
|
+
* @category constructors
|
|
214
|
+
* @since 0.1.0
|
|
215
|
+
*/
|
|
216
|
+
export declare const run: (suite: Suite, options: RunOptions) => Effect.Effect<RunResult, EvalError, CaseExecutor>;
|
|
217
|
+
/**
|
|
218
|
+
* Provides a batch runner that is never available.
|
|
219
|
+
*
|
|
220
|
+
* Every bound score under this layer becomes an inconclusive observation, which
|
|
221
|
+
* a gate grades as an undecidable run rather than a red. Provide it to state
|
|
222
|
+
* that a suite must not score, and {@link layerInline} to score in process.
|
|
223
|
+
*
|
|
224
|
+
* @category layers
|
|
225
|
+
* @since 0.1.0
|
|
226
|
+
*/
|
|
227
|
+
export declare const layerNoop: Layer.Layer<Runner>;
|
|
228
|
+
export {};
|
|
229
|
+
//# sourceMappingURL=Runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Runner.d.ts","sourceRoot":"","sources":["../../src/Runner.ts"],"names":[],"mappings":"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"}
|