@smthrs/evals 0.0.0-stage → 1.0.0-rc.3
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 +62 -2
- package/dist/cjs/Baseline.d.ts +118 -0
- package/dist/cjs/Baseline.d.ts.map +1 -0
- package/dist/cjs/Baseline.js +158 -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 +191 -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 +430 -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 +118 -0
- package/dist/esm/Baseline.d.ts.map +1 -0
- package/dist/esm/Baseline.js +203 -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 +195 -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 +430 -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 +116 -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 +130 -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 +46 -0
- package/docs/quickstart.md +238 -0
- package/docs/troubleshooting.md +148 -0
- package/package.json +198 -3
- package/src/Baseline.ts +259 -0
- package/src/CaseExecutor.ts +104 -0
- package/src/EvalError.ts +74 -0
- package/src/Gate.ts +128 -0
- package/src/Regression.ts +314 -0
- package/src/Report.ts +268 -0
- package/src/Runner.ts +759 -0
- package/src/Suite.ts +358 -0
- package/src/Trials.ts +154 -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,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var CaseExecutor_exports = {};
|
|
30
|
+
__export(CaseExecutor_exports, {
|
|
31
|
+
CaseExecutor: () => CaseExecutor,
|
|
32
|
+
layerNoop: () => layerNoop,
|
|
33
|
+
make: () => make,
|
|
34
|
+
makeNoop: () => makeNoop
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(CaseExecutor_exports);
|
|
37
|
+
var Context = __toESM(require("effect/Context"), 1);
|
|
38
|
+
var Effect = __toESM(require("effect/Effect"), 1);
|
|
39
|
+
var Layer = __toESM(require("effect/Layer"), 1);
|
|
40
|
+
var import_EvalError = require("./EvalError.js");
|
|
41
|
+
class CaseExecutor extends Context.Service()("flows/evals/CaseExecutor") {
|
|
42
|
+
}
|
|
43
|
+
const make = (run) => {
|
|
44
|
+
if (typeof run !== "function") {
|
|
45
|
+
throw new TypeError("CaseExecutor.make needs a callback");
|
|
46
|
+
}
|
|
47
|
+
return CaseExecutor.of({ run });
|
|
48
|
+
};
|
|
49
|
+
const makeNoop = () => CaseExecutor.of({
|
|
50
|
+
run: (suiteCase) => Effect.fail(
|
|
51
|
+
new import_EvalError.EvalError({
|
|
52
|
+
code: "executor",
|
|
53
|
+
message: `No executor is available for case '${suiteCase.name}'`,
|
|
54
|
+
path: `cases['${suiteCase.name}']`
|
|
55
|
+
})
|
|
56
|
+
)
|
|
57
|
+
});
|
|
58
|
+
const layerNoop = Layer.succeed(CaseExecutor)(makeNoop());
|
|
59
|
+
//# sourceMappingURL=CaseExecutor.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/CaseExecutor.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Injectable target-flow execution boundary.\n *\n * A suite says what to run; this service says how. Keeping it injectable is\n * what lets the same suite run against a real flow, a scripted host, or nothing\n * at all, and it is the only place a target's failure is converted into a typed\n * evaluation failure.\n *\n * @since 0.1.0\n */\n\nimport type { Flow } from \"@smthrs/core\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport { EvalError } from \"./EvalError.ts\"\nimport type { Case } from \"./Suite.ts\"\n\n/**\n * The result of executing one target-flow case.\n *\n * `target` is the flow value the case actually executed. A run matches it\n * against each binding's `appliesTo` by reference identity, so it has to be the\n * declared flow itself rather than a copy of it.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Execution {\n readonly output: unknown\n readonly stepKey: string\n readonly latencyMs: number\n readonly target: Pick<Flow.Any, \"name\">\n}\n\n/**\n * The one callback a case executor is.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Run = (suiteCase: Case) => Effect.Effect<Execution, EvalError>\n\n/**\n * Runtime shape for an injectable target-flow executor.\n *\n * @category services\n * @since 0.1.0\n */\nexport interface Service {\n readonly run: Run\n}\n\n/**\n * Injectable execution boundary for a target flow.\n *\n * @category services\n * @since 0.1.0\n */\nexport class CaseExecutor extends Context.Service<CaseExecutor, Service>()(\"flows/evals/CaseExecutor\") {}\n\n/**\n * Builds an executor from its one callback.\n *\n * Throws a `TypeError` when there is no callback. An executor that silently\n * degraded to {@link makeNoop} turned one wiring mistake into a whole suite of\n * cases failing with `executor`, which reads as a broken target rather than a\n * missing one.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = (run: Run): Service => {\n if (typeof run !== \"function\") {\n throw new TypeError(\"CaseExecutor.make needs a callback\")\n }\n return CaseExecutor.of({ run })\n}\n\n/**\n * Builds an executor that fails every case with a typed executor error.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makeNoop = (): Service =>\n CaseExecutor.of({\n run: (suiteCase) =>\n Effect.fail(\n new EvalError({\n code: \"executor\",\n message: `No executor is available for case '${suiteCase.name}'`,\n path: `cases['${suiteCase.name}']`\n })\n )\n })\n\n/**\n * Provides the unavailable executor.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerNoop: Layer.Layer<CaseExecutor> = Layer.succeed(CaseExecutor)(makeNoop())\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYA,cAAyB;AACzB,aAAwB;AACxB,YAAuB;AACvB,uBAA0B;AA4CnB,MAAM,qBAAqB,QAAQ,QAA+B,EAAE,0BAA0B,EAAE;AAAC;AAajG,MAAM,OAAO,CAAC,QAAsB;AACzC,MAAI,OAAO,QAAQ,YAAY;AAC7B,UAAM,IAAI,UAAU,oCAAoC;AAAA,EAC1D;AACA,SAAO,aAAa,GAAG,EAAE,IAAI,CAAC;AAChC;AAQO,MAAM,WAAW,MACtB,aAAa,GAAG;AAAA,EACd,KAAK,CAAC,cACJ,OAAO;AAAA,IACL,IAAI,2BAAU;AAAA,MACZ,MAAM;AAAA,MACN,SAAS,sCAAsC,UAAU,IAAI;AAAA,MAC7D,MAAM,UAAU,UAAU,IAAI;AAAA,IAChC,CAAC;AAAA,EACH;AACJ,CAAC;AAQI,MAAM,YAAuC,MAAM,QAAQ,YAAY,EAAE,SAAS,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The typed failures evaluation raises, with the stable codes a gate or a
|
|
3
|
+
* report branches on.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*/
|
|
7
|
+
import * as Schema from "effect/Schema";
|
|
8
|
+
/**
|
|
9
|
+
* Stable evaluation failure codes.
|
|
10
|
+
*
|
|
11
|
+
* Each code names one thing a caller can do about it, so a gate that branches
|
|
12
|
+
* on the code routes the failure to the right owner:
|
|
13
|
+
*
|
|
14
|
+
* - `invalid_suite`: the suite declaration is wrong. Fix the suite.
|
|
15
|
+
* - `invalid_run_options`: `Runner.run`'s own options are wrong, not the suite.
|
|
16
|
+
* - `invalid_baseline`: the committed baseline is unreadable, belongs to
|
|
17
|
+
* another suite, or holds a record the schema rejects. Regenerate it.
|
|
18
|
+
* - `invalid_tolerance`: the caller passed a tolerance that is not a finite
|
|
19
|
+
* non-negative number. Fix the call, not the baseline.
|
|
20
|
+
* - `invalid_trials`: the caller passed run, pass, or `k` counts to `Trials`
|
|
21
|
+
* outside their bounds. Fix the call.
|
|
22
|
+
* - `executor`: the target flow failed for a case.
|
|
23
|
+
* - `ambiguous_score_job`: two score jobs shared a step key and a scorer, so
|
|
24
|
+
* an order-only batch runner's results cannot be attributed to a case. Give
|
|
25
|
+
* each case its own step key, or use a runner that implements
|
|
26
|
+
* `runBatchCorrelated`.
|
|
27
|
+
* - `scorer_protocol`: a batch runner broke the `runBatch` contract by returning
|
|
28
|
+
* the wrong number of observations, or observations that identify jobs other
|
|
29
|
+
* than the ones it was given. Nothing it returned can be trusted.
|
|
30
|
+
* - `scorer_unavailable`: no batch runner was available to score with.
|
|
31
|
+
*
|
|
32
|
+
* @category models
|
|
33
|
+
* @since 0.1.0
|
|
34
|
+
*/
|
|
35
|
+
export declare const EvalErrorCode: Schema.Literals<readonly ["invalid_suite", "invalid_run_options", "invalid_baseline", "invalid_tolerance", "invalid_trials", "executor", "ambiguous_score_job", "scorer_protocol", "scorer_unavailable"]>;
|
|
36
|
+
/**
|
|
37
|
+
* Stable evaluation failure code.
|
|
38
|
+
*
|
|
39
|
+
* @category models
|
|
40
|
+
* @since 0.1.0
|
|
41
|
+
*/
|
|
42
|
+
export type EvalErrorCode = typeof EvalErrorCode.Type;
|
|
43
|
+
declare const EvalError_base: Schema.Class<EvalError, Schema.TaggedStruct<"flows/evals/EvalError", {
|
|
44
|
+
readonly code: Schema.Literals<readonly ["invalid_suite", "invalid_run_options", "invalid_baseline", "invalid_tolerance", "invalid_trials", "executor", "ambiguous_score_job", "scorer_protocol", "scorer_unavailable"]>;
|
|
45
|
+
readonly message: Schema.String;
|
|
46
|
+
readonly path: Schema.optional<Schema.String>;
|
|
47
|
+
readonly cause: Schema.optional<Schema.Defect>;
|
|
48
|
+
}>, import("effect/Cause").YieldableError>;
|
|
49
|
+
/**
|
|
50
|
+
* A typed failure raised while loading or executing an evaluation.
|
|
51
|
+
*
|
|
52
|
+
* `code` is the stable branch point, `message` is the sentence a CI log shows,
|
|
53
|
+
* and `path` locates the offending value inside the input the caller supplied
|
|
54
|
+
* (`records[3].score`, `cases[1].input`, `options.at`). `cause` retains the
|
|
55
|
+
* original failure; it is never rendered on its own, so a message that matters
|
|
56
|
+
* to an operator has to say so itself.
|
|
57
|
+
*
|
|
58
|
+
* @category errors
|
|
59
|
+
* @since 0.1.0
|
|
60
|
+
*/
|
|
61
|
+
export declare class EvalError extends EvalError_base {
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=EvalError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EvalError.d.ts","sourceRoot":"","sources":["../../src/EvalError.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,aAAa,2MAUxB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;;;;;;;AAErD;;;;;;;;;;;GAWG;AACH,qBAAa,SAAU,SAAQ,cAK7B;CAAG"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var EvalError_exports = {};
|
|
30
|
+
__export(EvalError_exports, {
|
|
31
|
+
EvalError: () => EvalError,
|
|
32
|
+
EvalErrorCode: () => EvalErrorCode
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(EvalError_exports);
|
|
35
|
+
var Schema = __toESM(require("effect/Schema"), 1);
|
|
36
|
+
const EvalErrorCode = Schema.Literals([
|
|
37
|
+
"invalid_suite",
|
|
38
|
+
"invalid_run_options",
|
|
39
|
+
"invalid_baseline",
|
|
40
|
+
"invalid_tolerance",
|
|
41
|
+
"invalid_trials",
|
|
42
|
+
"executor",
|
|
43
|
+
"ambiguous_score_job",
|
|
44
|
+
"scorer_protocol",
|
|
45
|
+
"scorer_unavailable"
|
|
46
|
+
]);
|
|
47
|
+
class EvalError extends Schema.TaggedError()("flows/evals/EvalError", {
|
|
48
|
+
code: EvalErrorCode,
|
|
49
|
+
message: Schema.String,
|
|
50
|
+
path: Schema.optional(Schema.String),
|
|
51
|
+
cause: Schema.optional(Schema.Defect())
|
|
52
|
+
}) {
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=EvalError.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/EvalError.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The typed failures evaluation raises, with the stable codes a gate or a\n * report branches on.\n *\n * @since 0.1.0\n */\n\nimport * as Schema from \"effect/Schema\"\n\n/**\n * Stable evaluation failure codes.\n *\n * Each code names one thing a caller can do about it, so a gate that branches\n * on the code routes the failure to the right owner:\n *\n * - `invalid_suite`: the suite declaration is wrong. Fix the suite.\n * - `invalid_run_options`: `Runner.run`'s own options are wrong, not the suite.\n * - `invalid_baseline`: the committed baseline is unreadable, belongs to\n * another suite, or holds a record the schema rejects. Regenerate it.\n * - `invalid_tolerance`: the caller passed a tolerance that is not a finite\n * non-negative number. Fix the call, not the baseline.\n * - `invalid_trials`: the caller passed run, pass, or `k` counts to `Trials`\n * outside their bounds. Fix the call.\n * - `executor`: the target flow failed for a case.\n * - `ambiguous_score_job`: two score jobs shared a step key and a scorer, so\n * an order-only batch runner's results cannot be attributed to a case. Give\n * each case its own step key, or use a runner that implements\n * `runBatchCorrelated`.\n * - `scorer_protocol`: a batch runner broke the `runBatch` contract by returning\n * the wrong number of observations, or observations that identify jobs other\n * than the ones it was given. Nothing it returned can be trusted.\n * - `scorer_unavailable`: no batch runner was available to score with.\n *\n * @category models\n * @since 0.1.0\n */\nexport const EvalErrorCode = Schema.Literals([\n \"invalid_suite\",\n \"invalid_run_options\",\n \"invalid_baseline\",\n \"invalid_tolerance\",\n \"invalid_trials\",\n \"executor\",\n \"ambiguous_score_job\",\n \"scorer_protocol\",\n \"scorer_unavailable\"\n])\n\n/**\n * Stable evaluation failure code.\n *\n * @category models\n * @since 0.1.0\n */\nexport type EvalErrorCode = typeof EvalErrorCode.Type\n\n/**\n * A typed failure raised while loading or executing an evaluation.\n *\n * `code` is the stable branch point, `message` is the sentence a CI log shows,\n * and `path` locates the offending value inside the input the caller supplied\n * (`records[3].score`, `cases[1].input`, `options.at`). `cause` retains the\n * original failure; it is never rendered on its own, so a message that matters\n * to an operator has to say so itself.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class EvalError extends Schema.TaggedError<EvalError>()(\"flows/evals/EvalError\", {\n code: EvalErrorCode,\n message: Schema.String,\n path: Schema.optional(Schema.String),\n cause: Schema.optional(Schema.Defect())\n}) {}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,aAAwB;AA6BjB,MAAM,gBAAgB,OAAO,SAAS;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAsBM,MAAM,kBAAkB,OAAO,YAAuB,EAAE,yBAAyB;AAAA,EACtF,MAAM;AAAA,EACN,SAAS,OAAO;AAAA,EAChB,MAAM,OAAO,SAAS,OAAO,MAAM;AAAA,EACnC,OAAO,OAAO,SAAS,OAAO,OAAO,CAAC;AACxC,CAAC,EAAE;AAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pass/fail decision over a finished evaluation run: how a threshold
|
|
3
|
+
* is applied and what a failing gate reports.
|
|
4
|
+
*
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*/
|
|
7
|
+
import { type ScoreGateError, type Verdict } from "@smthrs/scorers/ScoreGate";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import type { Report } from "./Regression.ts";
|
|
10
|
+
/**
|
|
11
|
+
* Thresholds accepted by a CI score gate.
|
|
12
|
+
*
|
|
13
|
+
* `mean` gates the arithmetic mean of every score observation, `min` gates the
|
|
14
|
+
* lowest one, and `perCase` gates each named case's lowest score. With none of
|
|
15
|
+
* them set the gate still runs, as `mean(0)`, so a run with no score at all is
|
|
16
|
+
* undecidable rather than a pass.
|
|
17
|
+
*
|
|
18
|
+
* @since 0.1.0
|
|
19
|
+
* @category models
|
|
20
|
+
*/
|
|
21
|
+
export interface Options {
|
|
22
|
+
readonly mean?: number | undefined;
|
|
23
|
+
readonly min?: number | undefined;
|
|
24
|
+
readonly perCase?: Readonly<Record<string, number>> | undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Checks thresholds through `@smthrs/scorers`' shared ScoreGate arithmetic.
|
|
28
|
+
*
|
|
29
|
+
* The threshold gates always run: an unobserved case cannot excuse the cases
|
|
30
|
+
* that were observed. Faults and findings are kept apart, and the verdict
|
|
31
|
+
* carries both, so a regression is a red while an unusable harness stays
|
|
32
|
+
* undecided.
|
|
33
|
+
*
|
|
34
|
+
* @category constructors
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
*/
|
|
37
|
+
export declare const check: (report: Report, options?: Options) => Effect.Effect<Verdict, ScoreGateError>;
|
|
38
|
+
/**
|
|
39
|
+
* Maps a gate verdict to the shared CI convention: a finding is exit code 1,
|
|
40
|
+
* an undecidable run is exit code 5.
|
|
41
|
+
*
|
|
42
|
+
* The summary is one log line: every C0 control and DEL in it becomes a
|
|
43
|
+
* space, the way a report cell is written, so a value that reached a verdict
|
|
44
|
+
* reason from a baseline, a step key or a target's failure cannot emit its own
|
|
45
|
+
* line, which on GitHub Actions would be a workflow command, or a terminal
|
|
46
|
+
* escape.
|
|
47
|
+
*
|
|
48
|
+
* @category grading
|
|
49
|
+
* @since 0.1.0
|
|
50
|
+
*/
|
|
51
|
+
export declare const ciGrade: (verdict: Verdict) => {
|
|
52
|
+
readonly exitCode: 0 | 1 | 5;
|
|
53
|
+
readonly summary: string;
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=Gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Gate.d.ts","sourceRoot":"","sources":["../../src/Gate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAIL,KAAK,cAAc,EAEnB,KAAK,OAAO,EACb,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAA;CAChE;AAkDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAAI,QAAQ,MAAM,EAAE,UAAS,OAAY,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,cAAc,CAa/F,CAAA;AAEJ;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,GAAI,SAAS,OAAO,KAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAGlG,CAAA"}
|
package/dist/cjs/Gate.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var Gate_exports = {};
|
|
30
|
+
__export(Gate_exports, {
|
|
31
|
+
check: () => check,
|
|
32
|
+
ciGrade: () => ciGrade
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(Gate_exports);
|
|
35
|
+
var import_ScoreGate = require("@smthrs/scorers/ScoreGate");
|
|
36
|
+
var Effect = __toESM(require("effect/Effect"), 1);
|
|
37
|
+
var import_controlCharacters = require("./internal/controlCharacters.js");
|
|
38
|
+
var import_scorerLabel = require("./internal/scorerLabel.js");
|
|
39
|
+
const samples = (report) => report.run.observations.map(
|
|
40
|
+
(observation) => observation.kind === "score" ? {
|
|
41
|
+
case: observation.case,
|
|
42
|
+
scorer: observation.scorer,
|
|
43
|
+
stepKey: observation.stepKey,
|
|
44
|
+
kind: "score",
|
|
45
|
+
value: observation.score,
|
|
46
|
+
reason: observation.reason
|
|
47
|
+
} : {
|
|
48
|
+
case: observation.case,
|
|
49
|
+
scorer: observation.scorer,
|
|
50
|
+
stepKey: observation.stepKey,
|
|
51
|
+
kind: "inconclusive",
|
|
52
|
+
reason: observation.reason
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
const environmentFaults = (report) => [
|
|
56
|
+
...report.run.cases.flatMap(
|
|
57
|
+
(result) => result.error === void 0 ? [] : [`case '${result.case}' failed: ${result.error.code}: ${result.error.message}`]
|
|
58
|
+
),
|
|
59
|
+
...report.missing.map(
|
|
60
|
+
(item) => `missing ${item.side} observation for ${item.case}/${(0, import_scorerLabel.scorerLabel)(item)}/${item.stepKey}`
|
|
61
|
+
)
|
|
62
|
+
];
|
|
63
|
+
const findings = (report) => [
|
|
64
|
+
...report.regressions.map(
|
|
65
|
+
(item) => `regression for ${item.case}/${(0, import_scorerLabel.scorerLabel)({ scorer: item.scorer, scorerName: item.actual.scorerName })}`
|
|
66
|
+
),
|
|
67
|
+
...report.nondeterminism.map(
|
|
68
|
+
(item) => `nondeterminism for ${item.case}/${(0, import_scorerLabel.scorerLabel)({ scorer: item.scorer, scorerName: item.actual.scorerName })}`
|
|
69
|
+
)
|
|
70
|
+
];
|
|
71
|
+
const check = (report, options = {}) => Effect.gen(function* () {
|
|
72
|
+
const expectation = (0, import_ScoreGate.expectScores)(samples(report));
|
|
73
|
+
const verdicts = [];
|
|
74
|
+
if (options.mean !== void 0) verdicts.push(yield* expectation.mean(options.mean));
|
|
75
|
+
if (options.min !== void 0) verdicts.push(yield* expectation.min(options.min));
|
|
76
|
+
if (options.perCase !== void 0) verdicts.push(yield* expectation.perCase(options.perCase));
|
|
77
|
+
if (options.mean === void 0 && options.min === void 0 && options.perCase === void 0) {
|
|
78
|
+
verdicts.push(yield* expectation.mean(0));
|
|
79
|
+
}
|
|
80
|
+
const reasons = findings(report);
|
|
81
|
+
if (reasons.length > 0) verdicts.push({ _tag: "Failed", reasons, inconclusive: [] });
|
|
82
|
+
return (0, import_ScoreGate.combine)(verdicts, environmentFaults(report));
|
|
83
|
+
});
|
|
84
|
+
const ciGrade = (verdict) => {
|
|
85
|
+
const graded = (0, import_ScoreGate.grade)(verdict);
|
|
86
|
+
return { exitCode: graded.exitCode, summary: (0, import_controlCharacters.flattenControlCharacters)(graded.summary) };
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=Gate.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/Gate.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The pass/fail decision over a finished evaluation run: how a threshold\n * is applied and what a failing gate reports.\n *\n * @since 0.1.0\n */\n\nimport {\n combine,\n expectScores,\n grade,\n type ScoreGateError,\n type ScoreSample,\n type Verdict\n} from \"@smthrs/scorers/ScoreGate\"\nimport * as Effect from \"effect/Effect\"\nimport { flattenControlCharacters } from \"./internal/controlCharacters.ts\"\nimport { scorerLabel } from \"./internal/scorerLabel.ts\"\nimport type { Report } from \"./Regression.ts\"\n\n/**\n * Thresholds accepted by a CI score gate.\n *\n * `mean` gates the arithmetic mean of every score observation, `min` gates the\n * lowest one, and `perCase` gates each named case's lowest score. With none of\n * them set the gate still runs, as `mean(0)`, so a run with no score at all is\n * undecidable rather than a pass.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface Options {\n readonly mean?: number | undefined\n readonly min?: number | undefined\n readonly perCase?: Readonly<Record<string, number>> | undefined\n}\n\nconst samples = (report: Report): ReadonlyArray<ScoreSample> =>\n report.run.observations.map((observation) =>\n observation.kind === \"score\"\n ? {\n case: observation.case,\n scorer: observation.scorer,\n stepKey: observation.stepKey,\n kind: \"score\",\n value: observation.score,\n reason: observation.reason\n }\n : {\n case: observation.case,\n scorer: observation.scorer,\n stepKey: observation.stepKey,\n kind: \"inconclusive\",\n reason: observation.reason\n }\n )\n\n/**\n * An environment fault: something the comparison needed was never observed, so\n * the harness owes an answer it cannot give. A fault withholds a decision;\n * it does not make one.\n */\nconst environmentFaults = (report: Report): ReadonlyArray<string> => [\n ...report.run.cases.flatMap((result) =>\n result.error === undefined ? [] : [`case '${result.case}' failed: ${result.error.code}: ${result.error.message}`]\n ),\n ...report.missing.map((item) =>\n `missing ${item.side} observation for ${item.case}/${scorerLabel(item)}/${item.stepKey}`\n )\n]\n\n/**\n * A finding: the run measured something the baseline says it should not have.\n * A regression scored lower at a changed step key, and nondeterminism moved a\n * score at an unchanged one. Both are results, so both are red.\n */\nconst findings = (report: Report): ReadonlyArray<string> => [\n ...report.regressions.map((item) =>\n `regression for ${item.case}/${scorerLabel({ scorer: item.scorer, scorerName: item.actual.scorerName })}`\n ),\n ...report.nondeterminism.map((item) =>\n `nondeterminism for ${item.case}/${scorerLabel({ scorer: item.scorer, scorerName: item.actual.scorerName })}`\n )\n]\n\n/**\n * Checks thresholds through `@smthrs/scorers`' shared ScoreGate arithmetic.\n *\n * The threshold gates always run: an unobserved case cannot excuse the cases\n * that were observed. Faults and findings are kept apart, and the verdict\n * carries both, so a regression is a red while an unusable harness stays\n * undecided.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const check = (report: Report, options: Options = {}): Effect.Effect<Verdict, ScoreGateError> =>\n Effect.gen(function*() {\n const expectation = expectScores(samples(report))\n const verdicts: Array<Verdict> = []\n if (options.mean !== undefined) verdicts.push(yield* expectation.mean(options.mean))\n if (options.min !== undefined) verdicts.push(yield* expectation.min(options.min))\n if (options.perCase !== undefined) verdicts.push(yield* expectation.perCase(options.perCase))\n if (options.mean === undefined && options.min === undefined && options.perCase === undefined) {\n verdicts.push(yield* expectation.mean(0))\n }\n const reasons = findings(report)\n if (reasons.length > 0) verdicts.push({ _tag: \"Failed\", reasons, inconclusive: [] })\n return combine(verdicts, environmentFaults(report))\n })\n\n/**\n * Maps a gate verdict to the shared CI convention: a finding is exit code 1,\n * an undecidable run is exit code 5.\n *\n * The summary is one log line: every C0 control and DEL in it becomes a\n * space, the way a report cell is written, so a value that reached a verdict\n * reason from a baseline, a step key or a target's failure cannot emit its own\n * line, which on GitHub Actions would be a workflow command, or a terminal\n * escape.\n *\n * @category grading\n * @since 0.1.0\n */\nexport const ciGrade = (verdict: Verdict): { readonly exitCode: 0 | 1 | 5; readonly summary: string } => {\n const graded = grade(verdict)\n return { exitCode: graded.exitCode, summary: flattenControlCharacters(graded.summary) }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,uBAOO;AACP,aAAwB;AACxB,+BAAyC;AACzC,yBAA4B;AAoB5B,MAAM,UAAU,CAAC,WACf,OAAO,IAAI,aAAa;AAAA,EAAI,CAAC,gBAC3B,YAAY,SAAS,UACjB;AAAA,IACA,MAAM,YAAY;AAAA,IAClB,QAAQ,YAAY;AAAA,IACpB,SAAS,YAAY;AAAA,IACrB,MAAM;AAAA,IACN,OAAO,YAAY;AAAA,IACnB,QAAQ,YAAY;AAAA,EACtB,IACE;AAAA,IACA,MAAM,YAAY;AAAA,IAClB,QAAQ,YAAY;AAAA,IACpB,SAAS,YAAY;AAAA,IACrB,MAAM;AAAA,IACN,QAAQ,YAAY;AAAA,EACtB;AACJ;AAOF,MAAM,oBAAoB,CAAC,WAA0C;AAAA,EACnE,GAAG,OAAO,IAAI,MAAM;AAAA,IAAQ,CAAC,WAC3B,OAAO,UAAU,SAAY,CAAC,IAAI,CAAC,SAAS,OAAO,IAAI,aAAa,OAAO,MAAM,IAAI,KAAK,OAAO,MAAM,OAAO,EAAE;AAAA,EAClH;AAAA,EACA,GAAG,OAAO,QAAQ;AAAA,IAAI,CAAC,SACrB,WAAW,KAAK,IAAI,oBAAoB,KAAK,IAAI,QAAI,gCAAY,IAAI,CAAC,IAAI,KAAK,OAAO;AAAA,EACxF;AACF;AAOA,MAAM,WAAW,CAAC,WAA0C;AAAA,EAC1D,GAAG,OAAO,YAAY;AAAA,IAAI,CAAC,SACzB,kBAAkB,KAAK,IAAI,QAAI,gCAAY,EAAE,QAAQ,KAAK,QAAQ,YAAY,KAAK,OAAO,WAAW,CAAC,CAAC;AAAA,EACzG;AAAA,EACA,GAAG,OAAO,eAAe;AAAA,IAAI,CAAC,SAC5B,sBAAsB,KAAK,IAAI,QAAI,gCAAY,EAAE,QAAQ,KAAK,QAAQ,YAAY,KAAK,OAAO,WAAW,CAAC,CAAC;AAAA,EAC7G;AACF;AAaO,MAAM,QAAQ,CAAC,QAAgB,UAAmB,CAAC,MACxD,OAAO,IAAI,aAAY;AACrB,QAAM,kBAAc,+BAAa,QAAQ,MAAM,CAAC;AAChD,QAAM,WAA2B,CAAC;AAClC,MAAI,QAAQ,SAAS,OAAW,UAAS,KAAK,OAAO,YAAY,KAAK,QAAQ,IAAI,CAAC;AACnF,MAAI,QAAQ,QAAQ,OAAW,UAAS,KAAK,OAAO,YAAY,IAAI,QAAQ,GAAG,CAAC;AAChF,MAAI,QAAQ,YAAY,OAAW,UAAS,KAAK,OAAO,YAAY,QAAQ,QAAQ,OAAO,CAAC;AAC5F,MAAI,QAAQ,SAAS,UAAa,QAAQ,QAAQ,UAAa,QAAQ,YAAY,QAAW;AAC5F,aAAS,KAAK,OAAO,YAAY,KAAK,CAAC,CAAC;AAAA,EAC1C;AACA,QAAM,UAAU,SAAS,MAAM;AAC/B,MAAI,QAAQ,SAAS,EAAG,UAAS,KAAK,EAAE,MAAM,UAAU,SAAS,cAAc,CAAC,EAAE,CAAC;AACnF,aAAO,0BAAQ,UAAU,kBAAkB,MAAM,CAAC;AACpD,CAAC;AAeI,MAAM,UAAU,CAAC,YAAiF;AACvG,QAAM,aAAS,wBAAM,OAAO;AAC5B,SAAO,EAAE,UAAU,OAAO,UAAU,aAAS,mDAAyB,OAAO,OAAO,EAAE;AACxF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Step-key-aware evaluation regression comparison.
|
|
3
|
+
*
|
|
4
|
+
* The step key is what separates the two findings this module reports. A score
|
|
5
|
+
* that moved at a *changed* step key is a regression: the target produced
|
|
6
|
+
* different work and it graded worse. A score that moved at an *unchanged* step
|
|
7
|
+
* key is nondeterminism: the same work graded differently twice. Both are
|
|
8
|
+
* results, and a gate reads both as red.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import type { Baseline, BaselineRecord } from "./Baseline.ts";
|
|
14
|
+
import { EvalError } from "./EvalError.ts";
|
|
15
|
+
import type { Observation, RunResult } from "./Runner.ts";
|
|
16
|
+
/**
|
|
17
|
+
* Tolerances used for score comparisons.
|
|
18
|
+
*
|
|
19
|
+
* A move is reported only when it exceeds both tolerances, so either one alone
|
|
20
|
+
* is enough to silence it. Both default to `0`, which reports every move.
|
|
21
|
+
*
|
|
22
|
+
* @category models
|
|
23
|
+
* @since 0.1.0
|
|
24
|
+
*/
|
|
25
|
+
export interface Tolerances {
|
|
26
|
+
readonly absolute?: number | undefined;
|
|
27
|
+
readonly relative?: number | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A score drop at a changed step key.
|
|
31
|
+
*
|
|
32
|
+
* @category models
|
|
33
|
+
* @since 0.1.0
|
|
34
|
+
*/
|
|
35
|
+
export interface Regression {
|
|
36
|
+
readonly case: string;
|
|
37
|
+
readonly scorer: string;
|
|
38
|
+
readonly baseline: BaselineRecord;
|
|
39
|
+
readonly actual: Extract<Observation, {
|
|
40
|
+
readonly kind: "score";
|
|
41
|
+
}>;
|
|
42
|
+
readonly drop: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A changed score at the same step key, indicating nondeterminism.
|
|
46
|
+
*
|
|
47
|
+
* @category models
|
|
48
|
+
* @since 0.1.0
|
|
49
|
+
*/
|
|
50
|
+
export interface Nondeterminism {
|
|
51
|
+
readonly case: string;
|
|
52
|
+
readonly scorer: string;
|
|
53
|
+
readonly baseline: BaselineRecord;
|
|
54
|
+
readonly actual: Extract<Observation, {
|
|
55
|
+
readonly kind: "score";
|
|
56
|
+
}>;
|
|
57
|
+
readonly delta: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* An observation present on only one side of a comparison.
|
|
61
|
+
*
|
|
62
|
+
* `side` names the side it is missing from: `"run"` for a baseline record the
|
|
63
|
+
* run never reproduced, `"baseline"` for a score the run produced that no
|
|
64
|
+
* baseline record accounts for. `scorerName` comes from the observation that
|
|
65
|
+
* is present, when that artifact carried one.
|
|
66
|
+
*
|
|
67
|
+
* @category models
|
|
68
|
+
* @since 0.1.0
|
|
69
|
+
*/
|
|
70
|
+
export interface MissingObservation {
|
|
71
|
+
readonly side: "baseline" | "run";
|
|
72
|
+
readonly case: string;
|
|
73
|
+
readonly scorer: string;
|
|
74
|
+
readonly scorerName?: string | undefined;
|
|
75
|
+
readonly stepKey: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Complete regression comparison.
|
|
79
|
+
*
|
|
80
|
+
* @category models
|
|
81
|
+
* @since 0.1.0
|
|
82
|
+
*/
|
|
83
|
+
export interface Report {
|
|
84
|
+
readonly suite: string;
|
|
85
|
+
readonly baseline: Baseline;
|
|
86
|
+
readonly run: RunResult;
|
|
87
|
+
readonly regressions: ReadonlyArray<Regression>;
|
|
88
|
+
readonly nondeterminism: ReadonlyArray<Nondeterminism>;
|
|
89
|
+
readonly missing: ReadonlyArray<MissingObservation>;
|
|
90
|
+
readonly samples: ReadonlyArray<Extract<Observation, {
|
|
91
|
+
readonly kind: "score";
|
|
92
|
+
}>>;
|
|
93
|
+
readonly inconclusive: ReadonlyArray<Extract<Observation, {
|
|
94
|
+
readonly kind: "inconclusive";
|
|
95
|
+
}>>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Compares a run to a baseline, preserving missing and inconclusive observations.
|
|
99
|
+
*
|
|
100
|
+
* Records and observations are grouped by `(case, scorer)` and then paired by
|
|
101
|
+
* step key, so a scorer that ran several times against one case is compared
|
|
102
|
+
* pairwise instead of by array position. A pair whose step key is unchanged and
|
|
103
|
+
* whose score moved is nondeterminism; a pair whose step key changed and whose
|
|
104
|
+
* score dropped is a regression; an unpaired record on either side is a missing
|
|
105
|
+
* observation. Inconclusive observations are carried through untouched, because
|
|
106
|
+
* they decide nothing and a gate has to see them.
|
|
107
|
+
*
|
|
108
|
+
* Fails with `invalid_tolerance` when a tolerance is not finite and
|
|
109
|
+
* non-negative, and with `invalid_baseline` when the artifact or any of its
|
|
110
|
+
* records belongs to a suite other than the one the run reports.
|
|
111
|
+
*
|
|
112
|
+
* @category constructors
|
|
113
|
+
* @since 0.1.0
|
|
114
|
+
*/
|
|
115
|
+
export declare const compare: (baseline: Baseline, run: RunResult, tolerances?: Tolerances) => Effect.Effect<Report, EvalError>;
|
|
116
|
+
//# sourceMappingURL=Regression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Regression.d.ts","sourceRoot":"","sources":["../../src/Regression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAI1C,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEzD;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,KAAK,CAAA;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAA;IACvB,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;IAC/C,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;IACtD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;IACnD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,CAAA;IACjF,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,OAAO,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;KAAE,CAAC,CAAC,CAAA;CAC9F;AA2ED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,OAAO,GAClB,UAAU,QAAQ,EAClB,KAAK,SAAS,EACd,aAAY,UAAe,KAC1B,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAwHjC,CAAA"}
|