@cef-ai/eval 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -2
- package/dist/capture.d.ts +37 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +70 -0
- package/dist/capture.js.map +1 -0
- package/dist/conformance.d.ts +6 -0
- package/dist/conformance.d.ts.map +1 -0
- package/dist/conformance.js +256 -0
- package/dist/conformance.js.map +1 -0
- package/dist/contract.d.ts +18 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +176 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +105 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +215 -0
- package/dist/layout.js.map +1 -0
- package/dist/pool.d.ts +13 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +34 -0
- package/dist/pool.js.map +1 -0
- package/dist/result.d.ts +11 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +72 -0
- package/dist/result.js.map +1 -0
- package/dist/run.d.ts +86 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +240 -0
- package/dist/run.js.map +1 -0
- package/dist/score.d.ts +102 -0
- package/dist/score.d.ts.map +1 -0
- package/dist/score.js +433 -0
- package/dist/score.js.map +1 -0
- package/dist/storage.d.ts +218 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +577 -0
- package/dist/storage.js.map +1 -0
- package/dist/summary.d.ts +105 -0
- package/dist/summary.d.ts.map +1 -0
- package/dist/summary.js +219 -0
- package/dist/summary.js.map +1 -0
- package/dist/types.d.ts +265 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/dist/vault-store.d.ts +46 -0
- package/dist/vault-store.d.ts.map +1 -0
- package/dist/vault-store.js +127 -0
- package/dist/vault-store.js.map +1 -0
- package/package.json +27 -3
package/dist/pool.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded-concurrency for-each, shared by `runExperiment` and the store
|
|
3
|
+
* conformance suite. Dependency-free and browser-safe.
|
|
4
|
+
*
|
|
5
|
+
* IT FAILS SAFELY. When one item throws, no further item is started, every
|
|
6
|
+
* item already in flight is awaited, and only then does the pool reject, with
|
|
7
|
+
* the first error. `Promise.all` over the workers would reject at the first
|
|
8
|
+
* throw while its siblings kept running — so a caller's cleanup (releasing a
|
|
9
|
+
* version pin, writing the final summary) would run under work that was still
|
|
10
|
+
* writing, and land before it.
|
|
11
|
+
*/
|
|
12
|
+
export async function pool(items, limit, fn) {
|
|
13
|
+
let next = 0;
|
|
14
|
+
let failed = false;
|
|
15
|
+
let first;
|
|
16
|
+
const worker = async () => {
|
|
17
|
+
while (!failed && next < items.length) {
|
|
18
|
+
const item = items[next++];
|
|
19
|
+
try {
|
|
20
|
+
await fn(item);
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
if (!failed) {
|
|
24
|
+
failed = true;
|
|
25
|
+
first = e;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, worker));
|
|
31
|
+
if (failed)
|
|
32
|
+
throw first;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=pool.js.map
|
package/dist/pool.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAI,KAAmB,EAAE,KAAa,EAAE,EAA8B;IAC9F,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,KAAc,CAAC;IACnB,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,OAAO,CAAC,MAAM,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,GAAG,IAAI,CAAC;oBACd,KAAK,GAAG,CAAC,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9F,IAAI,MAAM;QAAE,MAAM,KAAK,CAAC;AAC1B,CAAC"}
|
package/dist/result.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ResultField } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The fields the graph's `output` node(s) declare. A graph with several
|
|
4
|
+
* outputs (branches ending differently) yields the union; a field two outputs
|
|
5
|
+
* type differently is reported untyped, since either type can come back.
|
|
6
|
+
*
|
|
7
|
+
* undefined when no output declares a result — the Result is unknown, and
|
|
8
|
+
* scoring then judges every expected field as before.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resultFieldsFromGraph(graph: unknown): ResultField[] | undefined;
|
|
11
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA0B9C;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,EAAE,GAAG,SAAS,CAmB/E"}
|
package/dist/result.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a workflow version's declared Result off its graph — what ROC and
|
|
3
|
+
* the CLI record as `Experiment.resultFields`.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the runner's own reading (`@cef-ai/agent-sdk` `readResultSpec`)
|
|
6
|
+
* without depending on it: this package is zero-dependency and browser-safe.
|
|
7
|
+
* An `output` node declares `params.result` (`{ field: value }`, or that object
|
|
8
|
+
* as JSON text — what a builder text field saves) and optionally
|
|
9
|
+
* `params.resultTypes` (`{ field: type }`, object or JSON text).
|
|
10
|
+
*/
|
|
11
|
+
import { isObject } from "./contract.js";
|
|
12
|
+
/** An object, or JSON text that parses to one; anything else is undefined. */
|
|
13
|
+
function objectOf(raw) {
|
|
14
|
+
if (isObject(raw))
|
|
15
|
+
return raw;
|
|
16
|
+
if (typeof raw !== "string" || !raw.trim().startsWith("{"))
|
|
17
|
+
return undefined;
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
return isObject(parsed) ? parsed : undefined;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The graph itself, from any of the forms it travels in: the object, its JSON
|
|
28
|
+
* text, or a manifest param (`params.graph` = `{ type: "string", default: "<json>" }`).
|
|
29
|
+
*/
|
|
30
|
+
function graphOf(raw) {
|
|
31
|
+
const g = objectOf(raw);
|
|
32
|
+
if (!g)
|
|
33
|
+
return undefined;
|
|
34
|
+
if (Array.isArray(g.nodes))
|
|
35
|
+
return g;
|
|
36
|
+
if ("default" in g)
|
|
37
|
+
return graphOf(g.default);
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The fields the graph's `output` node(s) declare. A graph with several
|
|
42
|
+
* outputs (branches ending differently) yields the union; a field two outputs
|
|
43
|
+
* type differently is reported untyped, since either type can come back.
|
|
44
|
+
*
|
|
45
|
+
* undefined when no output declares a result — the Result is unknown, and
|
|
46
|
+
* scoring then judges every expected field as before.
|
|
47
|
+
*/
|
|
48
|
+
export function resultFieldsFromGraph(graph) {
|
|
49
|
+
const g = graphOf(graph);
|
|
50
|
+
if (!g)
|
|
51
|
+
return undefined;
|
|
52
|
+
const types = new Map();
|
|
53
|
+
let declared = false;
|
|
54
|
+
for (const node of g.nodes) {
|
|
55
|
+
if (!isObject(node) || (node.kind ?? node.type) !== "output" || !isObject(node.params))
|
|
56
|
+
continue;
|
|
57
|
+
const result = objectOf(node.params.result);
|
|
58
|
+
if (!result)
|
|
59
|
+
continue;
|
|
60
|
+
declared = true;
|
|
61
|
+
const typed = objectOf(node.params.resultTypes) ?? {};
|
|
62
|
+
for (const name of Object.keys(result)) {
|
|
63
|
+
const t = typeof typed[name] === "string" && typed[name] ? typed[name] : undefined;
|
|
64
|
+
// First sighting sets the type; any disagreement (typed vs untyped included) drops it.
|
|
65
|
+
types.set(name, types.has(name) && types.get(name) !== t ? undefined : t);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!declared)
|
|
69
|
+
return undefined;
|
|
70
|
+
return [...types].sort(([a], [b]) => a.localeCompare(b)).map(([name, type]) => (type ? { name, type } : { name }));
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"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"}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running an experiment: one dataset version × one workflow version × N
|
|
3
|
+
* repeats, against an injected {@link WorkflowTarget}.
|
|
4
|
+
*
|
|
5
|
+
* WHAT IS WRITTEN, AND WHEN. The meta first, `running`, so an experiment that
|
|
6
|
+
* dies half way is still visible as one. Each run record as its run finishes,
|
|
7
|
+
* write-once, so a long experiment is resumable: rerun with the same
|
|
8
|
+
* `experimentId` and every run already on file is reused rather than repeated.
|
|
9
|
+
* The summary last, with the final status.
|
|
10
|
+
*
|
|
11
|
+
* A NON-LIVE VERSION is run through `target.pinVersion`, which routes only this
|
|
12
|
+
* experiment's contexts (`eval-<expId>-…`) to the candidate and is released
|
|
13
|
+
* however the experiment ends.
|
|
14
|
+
*/
|
|
15
|
+
import { type EvalStore } from "./storage.js";
|
|
16
|
+
import type { Case, Experiment, Header, ResultField, RunRecord, WorkflowTarget } from "./types.js";
|
|
17
|
+
export interface ExperimentProgress {
|
|
18
|
+
/** Runs finished (or reused) so far, including this one. */
|
|
19
|
+
done: number;
|
|
20
|
+
total: number;
|
|
21
|
+
run: RunRecord;
|
|
22
|
+
/** True when the run was already on file from an earlier attempt. */
|
|
23
|
+
reused: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface RunExperimentOptions {
|
|
26
|
+
store: EvalStore;
|
|
27
|
+
dataset: string;
|
|
28
|
+
/**
|
|
29
|
+
* The dataset version to run. Absent: `ensureDatasetVersion` — the latest,
|
|
30
|
+
* or a new one cut automatically when the cases changed since.
|
|
31
|
+
*/
|
|
32
|
+
datasetVersion?: number;
|
|
33
|
+
workflowId: string;
|
|
34
|
+
/** The version being evaluated. With `live`, the version the live deployment runs (recorded only). */
|
|
35
|
+
workflowVersion: string;
|
|
36
|
+
/** Run against the live deployment (no pin). False requires `target.pinVersion`. */
|
|
37
|
+
live: boolean;
|
|
38
|
+
/** How many times each case runs. Default 1. */
|
|
39
|
+
repeats?: number;
|
|
40
|
+
/**
|
|
41
|
+
* The Result the workflow version declares (`resultFieldsFromGraph`).
|
|
42
|
+
* Recorded on the experiment; expected fields it rules out score n/a.
|
|
43
|
+
*/
|
|
44
|
+
resultFields?: ResultField[];
|
|
45
|
+
/** Runs in flight at once. Default 4. */
|
|
46
|
+
concurrency?: number;
|
|
47
|
+
/** How long one run may take to end. Default 300 000 ms. */
|
|
48
|
+
timeoutMs?: number;
|
|
49
|
+
target: WorkflowTarget;
|
|
50
|
+
onProgress?: (p: ExperimentProgress) => void;
|
|
51
|
+
baselineId?: string;
|
|
52
|
+
/** Resume this experiment instead of starting a new one. */
|
|
53
|
+
experimentId?: string;
|
|
54
|
+
origin?: "roc" | "cli";
|
|
55
|
+
createdBy?: string;
|
|
56
|
+
note?: string;
|
|
57
|
+
/** Stops dispatching new runs; the experiment ends `cancelled`. */
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
/** Clock, for tests. */
|
|
60
|
+
now?: () => number;
|
|
61
|
+
/** Waits between retries of a run record's write; for tests. */
|
|
62
|
+
sleep?: (ms: number) => Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
export declare const DEFAULT_TIMEOUT_MS = 300000;
|
|
65
|
+
export declare const DEFAULT_CONCURRENCY = 4;
|
|
66
|
+
/** Backoff before each retry of a run record whose write-once lost to nothing. */
|
|
67
|
+
export declare const RECORD_WRITE_BACKOFF_MS: readonly [250, 1000, 4000];
|
|
68
|
+
/** Run one case × repeat against the target, and score it. Never throws. */
|
|
69
|
+
export declare function runOne(opts: {
|
|
70
|
+
target: WorkflowTarget;
|
|
71
|
+
header?: Pick<Header, "limits">;
|
|
72
|
+
case: Case;
|
|
73
|
+
caseRev: number;
|
|
74
|
+
/** 1-based. */
|
|
75
|
+
repeat: number;
|
|
76
|
+
/** Recorded on the run; the context already carries it. */
|
|
77
|
+
attempt?: number;
|
|
78
|
+
context: string;
|
|
79
|
+
timeoutMs: number;
|
|
80
|
+
now?: () => number;
|
|
81
|
+
/** The version's declared Result; see {@link scoreFields}. */
|
|
82
|
+
resultFields?: readonly ResultField[];
|
|
83
|
+
}): Promise<RunRecord>;
|
|
84
|
+
/** Run an experiment end to end. Resolves with the final meta (summary included). */
|
|
85
|
+
export declare function runExperiment(opts: RunExperimentOptions): Promise<Experiment>;
|
|
86
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EASL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAMtB,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAiB,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAElH,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,SAAS,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,sGAAsG;IACtG,eAAe,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,IAAI,EAAE,OAAO,CAAC;IACd,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,gEAAgE;IAChE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAC1C,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,kFAAkF;AAClF,eAAO,MAAM,uBAAuB,4BAA6B,CAAC;AAKlE,4EAA4E;AAC5E,wBAAsB,MAAM,CAAC,IAAI,EAAE;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAChC,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe;IACf,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CACvC,GAAG,OAAO,CAAC,SAAS,CAAC,CAuErB;AAED,qFAAqF;AACrF,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,CA0InF"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running an experiment: one dataset version × one workflow version × N
|
|
3
|
+
* repeats, against an injected {@link WorkflowTarget}.
|
|
4
|
+
*
|
|
5
|
+
* WHAT IS WRITTEN, AND WHEN. The meta first, `running`, so an experiment that
|
|
6
|
+
* dies half way is still visible as one. Each run record as its run finishes,
|
|
7
|
+
* write-once, so a long experiment is resumable: rerun with the same
|
|
8
|
+
* `experimentId` and every run already on file is reused rather than repeated.
|
|
9
|
+
* The summary last, with the final status.
|
|
10
|
+
*
|
|
11
|
+
* A NON-LIVE VERSION is run through `target.pinVersion`, which routes only this
|
|
12
|
+
* experiment's contexts (`eval-<expId>-…`) to the candidate and is released
|
|
13
|
+
* however the experiment ends.
|
|
14
|
+
*/
|
|
15
|
+
import { claimAttempt, ensureDatasetVersion, getExperiment, getRunRecord, loadVersion, putExperiment, putRunRecord, requireHeader, } from "./storage.js";
|
|
16
|
+
import { experimentContextPrefix, newExperimentId, runContext, runIdOf } from "./layout.js";
|
|
17
|
+
import { casePass, effectiveLimits, scoreFields, scoreLimits } from "./score.js";
|
|
18
|
+
import { emptyMetrics, summarize } from "./summary.js";
|
|
19
|
+
import { EvalContractError } from "./contract.js";
|
|
20
|
+
import { pool } from "./pool.js";
|
|
21
|
+
export const DEFAULT_TIMEOUT_MS = 300_000;
|
|
22
|
+
export const DEFAULT_CONCURRENCY = 4;
|
|
23
|
+
/** Backoff before each retry of a run record whose write-once lost to nothing. */
|
|
24
|
+
export const RECORD_WRITE_BACKOFF_MS = [250, 1000, 4000];
|
|
25
|
+
const iso = (ms) => new Date(ms).toISOString();
|
|
26
|
+
const message = (e) => (e instanceof Error ? e.message : String(e));
|
|
27
|
+
/** Run one case × repeat against the target, and score it. Never throws. */
|
|
28
|
+
export async function runOne(opts) {
|
|
29
|
+
const now = opts.now ?? Date.now;
|
|
30
|
+
const c = opts.case;
|
|
31
|
+
const started = now();
|
|
32
|
+
const base = {
|
|
33
|
+
caseId: c.id,
|
|
34
|
+
caseRev: opts.caseRev,
|
|
35
|
+
repeat: opts.repeat,
|
|
36
|
+
...(opts.attempt !== undefined ? { attempt: opts.attempt } : {}),
|
|
37
|
+
context: opts.context,
|
|
38
|
+
runId: runIdOf(opts.context),
|
|
39
|
+
startedAt: iso(started),
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
await opts.target.start(c.input, opts.context);
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
return {
|
|
46
|
+
...base, status: "error", output: null, error: `could not start the run: ${message(e)}`,
|
|
47
|
+
endedAt: null, metrics: emptyMetrics(), scores: [], pass: null,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
let end;
|
|
51
|
+
try {
|
|
52
|
+
end = await opts.target.waitForEnd(opts.context, opts.timeoutMs);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
return {
|
|
56
|
+
...base, status: "error", output: null, error: `could not read how the run ended: ${message(e)}`,
|
|
57
|
+
endedAt: null, metrics: emptyMetrics(), scores: [], pass: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const endedMs = end.endedAt ? Date.parse(end.endedAt) : NaN;
|
|
61
|
+
const startedMs = end.startedAt ? Date.parse(end.startedAt) : NaN;
|
|
62
|
+
let metrics;
|
|
63
|
+
let metricsError;
|
|
64
|
+
try {
|
|
65
|
+
metrics = await opts.target.metrics(opts.context);
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
metrics = emptyMetrics();
|
|
69
|
+
metricsError = `metrics unavailable: ${message(e)}`;
|
|
70
|
+
}
|
|
71
|
+
/*
|
|
72
|
+
* ONE CLOCK. Duration is the end event's timestamp minus the start event's,
|
|
73
|
+
* both stamped by the server. When the target could not read both, whatever
|
|
74
|
+
* it measured itself (also server-side — the CLI target uses the jobs'
|
|
75
|
+
* createdAt/completedAt) stands, and otherwise there is no duration. This
|
|
76
|
+
* machine's clock is never subtracted from the server's: their skew would
|
|
77
|
+
* land in every number and differ between the CLI and ROC.
|
|
78
|
+
*/
|
|
79
|
+
if (Number.isFinite(endedMs) && Number.isFinite(startedMs)) {
|
|
80
|
+
metrics = { ...metrics, durationMs: Math.max(0, endedMs - startedMs) };
|
|
81
|
+
}
|
|
82
|
+
const output = end.status === "done" ? (end.output ?? null) : null;
|
|
83
|
+
const fieldScores = scoreFields(c.expected, end.status === "done" ? (end.output ?? null) : undefined, opts.resultFields);
|
|
84
|
+
const scores = [...fieldScores, ...scoreLimits(effectiveLimits(opts.header, c), metrics)];
|
|
85
|
+
// A run that did not complete has no Result to judge; it is a failure of the
|
|
86
|
+
// workflow, whether or not the case states expectations.
|
|
87
|
+
const pass = end.status === "done" ? casePass(scores) : false;
|
|
88
|
+
const error = [end.error, metricsError].filter(Boolean).join("; ");
|
|
89
|
+
return {
|
|
90
|
+
...base,
|
|
91
|
+
...(Number.isFinite(startedMs) ? { startedAt: iso(startedMs) } : {}),
|
|
92
|
+
status: end.status,
|
|
93
|
+
output,
|
|
94
|
+
...(end.outcome ? { outcome: end.outcome } : {}),
|
|
95
|
+
...(error ? { error } : {}),
|
|
96
|
+
endedAt: Number.isFinite(endedMs) ? iso(endedMs) : null,
|
|
97
|
+
metrics,
|
|
98
|
+
scores,
|
|
99
|
+
pass,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Run an experiment end to end. Resolves with the final meta (summary included). */
|
|
103
|
+
export async function runExperiment(opts) {
|
|
104
|
+
const now = opts.now ?? Date.now;
|
|
105
|
+
const repeats = opts.repeats ?? 1;
|
|
106
|
+
const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
|
|
107
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
108
|
+
if (!Number.isInteger(repeats) || repeats < 1)
|
|
109
|
+
throw new EvalContractError("repeats must be a whole number ≥ 1");
|
|
110
|
+
if (!Number.isInteger(concurrency) || concurrency < 1)
|
|
111
|
+
throw new EvalContractError("concurrency must be a whole number ≥ 1");
|
|
112
|
+
if (!opts.live && !opts.target.pinVersion) {
|
|
113
|
+
throw new EvalContractError(`running workflow version ${opts.workflowVersion} that is not live needs a target that can pin a version`);
|
|
114
|
+
}
|
|
115
|
+
await requireHeader(opts.store, opts.workflowId, opts.dataset);
|
|
116
|
+
const existing = opts.experimentId ? await getExperiment(opts.store, opts.workflowId, opts.experimentId) : null;
|
|
117
|
+
if (opts.experimentId && (!existing || existing.dataset !== opts.dataset)) {
|
|
118
|
+
throw new EvalContractError(`experiment "${opts.experimentId}" not found for dataset "${opts.dataset}"`);
|
|
119
|
+
}
|
|
120
|
+
// A resume runs the version it started with; a new experiment the chosen one, or the working set's.
|
|
121
|
+
const datasetVersion = opts.datasetVersion
|
|
122
|
+
?? existing?.datasetVersion
|
|
123
|
+
?? (await ensureDatasetVersion(opts.store, opts.workflowId, opts.dataset, { now: new Date(now()) })).version;
|
|
124
|
+
const { manifest, cases } = await loadVersion(opts.store, opts.workflowId, opts.dataset, datasetVersion);
|
|
125
|
+
let experiment;
|
|
126
|
+
if (existing) {
|
|
127
|
+
if (existing.datasetVersion !== datasetVersion || existing.workflowVersion !== opts.workflowVersion
|
|
128
|
+
|| existing.repeats !== repeats) {
|
|
129
|
+
throw new EvalContractError(`experiment "${existing.id}" ran dataset v${existing.datasetVersion} × workflow ${existing.workflowVersion} × ${existing.repeats}; `
|
|
130
|
+
+ "resume it with the same settings");
|
|
131
|
+
}
|
|
132
|
+
// A new attempt, claimed write-once before anything is published: runs the
|
|
133
|
+
// dead one may have started — or a concurrent resumer is starting — keep
|
|
134
|
+
// their contexts to themselves.
|
|
135
|
+
const attempt = await claimAttempt(opts.store, opts.workflowId, existing.id, {
|
|
136
|
+
atLeast: (existing.attempt ?? 1) + 1, claimedAt: iso(now()),
|
|
137
|
+
});
|
|
138
|
+
experiment = { ...existing, status: "running", attempt };
|
|
139
|
+
delete experiment.finishedAt;
|
|
140
|
+
delete experiment.error;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const id = newExperimentId(now());
|
|
144
|
+
await claimAttempt(opts.store, opts.workflowId, id, { atLeast: 1, claimedAt: iso(now()) });
|
|
145
|
+
experiment = {
|
|
146
|
+
id,
|
|
147
|
+
dataset: opts.dataset,
|
|
148
|
+
datasetVersion: manifest.version,
|
|
149
|
+
workflowId: opts.workflowId,
|
|
150
|
+
workflowVersion: opts.workflowVersion,
|
|
151
|
+
live: opts.live,
|
|
152
|
+
repeats,
|
|
153
|
+
...(opts.resultFields ? { resultFields: opts.resultFields } : {}),
|
|
154
|
+
attempt: 1,
|
|
155
|
+
startedAt: iso(now()),
|
|
156
|
+
status: "running",
|
|
157
|
+
origin: opts.origin ?? "cli",
|
|
158
|
+
...(opts.createdBy ? { createdBy: opts.createdBy } : {}),
|
|
159
|
+
...(opts.baselineId ? { baselineId: opts.baselineId } : {}),
|
|
160
|
+
...(opts.note ? { note: opts.note } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
await putExperiment(opts.store, experiment);
|
|
164
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
165
|
+
/*
|
|
166
|
+
* Write-once, and honest about what happened. `putIfAbsent` answering false
|
|
167
|
+
* means "not written", which is two different things: the key EXISTS (a
|
|
168
|
+
* concurrent resumer got there first — its record is the one kept, and this
|
|
169
|
+
* run is reported as reused) or it does NOT (the store refused for another
|
|
170
|
+
* reason, e.g. a 409 from a colliding write). The second is retried with
|
|
171
|
+
* backoff and, if it never sticks, fails the experiment loudly — a record
|
|
172
|
+
* that was never stored must not be reported as reused.
|
|
173
|
+
*/
|
|
174
|
+
const storeRecord = async (record) => {
|
|
175
|
+
for (let attempt = 0;; attempt++) {
|
|
176
|
+
if (await putRunRecord(opts.store, opts.workflowId, experiment.id, record))
|
|
177
|
+
return { kept: record, reused: false };
|
|
178
|
+
const there = await getRunRecord(opts.store, opts.workflowId, experiment.id, record.caseId, record.repeat);
|
|
179
|
+
if (there)
|
|
180
|
+
return { kept: there, reused: true };
|
|
181
|
+
const wait = RECORD_WRITE_BACKOFF_MS[attempt];
|
|
182
|
+
if (wait === undefined) {
|
|
183
|
+
throw new EvalContractError(`could not store the run record for ${record.caseId}#${record.repeat}: the store refused the write `
|
|
184
|
+
+ `${attempt + 1} times and the key is still empty`);
|
|
185
|
+
}
|
|
186
|
+
await sleep(wait);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const work = cases.flatMap(({ case: c, rev }) => Array.from({ length: repeats }, (_, i) => ({ c, rev, repeat: i + 1 })));
|
|
190
|
+
const records = [];
|
|
191
|
+
let done = 0;
|
|
192
|
+
let release;
|
|
193
|
+
let failure;
|
|
194
|
+
try {
|
|
195
|
+
if (!opts.live)
|
|
196
|
+
release = await opts.target.pinVersion(opts.workflowVersion, experimentContextPrefix(experiment.id));
|
|
197
|
+
await pool(work, concurrency, async ({ c, rev, repeat }) => {
|
|
198
|
+
const onFile = await getRunRecord(opts.store, opts.workflowId, experiment.id, c.id, repeat);
|
|
199
|
+
if (onFile) {
|
|
200
|
+
records.push(onFile);
|
|
201
|
+
opts.onProgress?.({ done: ++done, total: work.length, run: onFile, reused: true });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (opts.signal?.aborted)
|
|
205
|
+
return;
|
|
206
|
+
const record = await runOne({
|
|
207
|
+
target: opts.target, header: manifest.header, case: c, caseRev: rev, repeat,
|
|
208
|
+
attempt: experiment.attempt ?? 1,
|
|
209
|
+
context: runContext(experiment.id, c.id, repeat, experiment.attempt ?? 1), timeoutMs, now,
|
|
210
|
+
...(experiment.resultFields ? { resultFields: experiment.resultFields } : {}),
|
|
211
|
+
});
|
|
212
|
+
const { kept, reused } = await storeRecord(record);
|
|
213
|
+
records.push(kept);
|
|
214
|
+
opts.onProgress?.({ done: ++done, total: work.length, run: kept, reused });
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
catch (e) {
|
|
218
|
+
failure = e;
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
if (release) {
|
|
222
|
+
try {
|
|
223
|
+
await release();
|
|
224
|
+
}
|
|
225
|
+
catch (e) {
|
|
226
|
+
failure ??= new Error(`could not release the version pin: ${message(e)}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const finished = {
|
|
231
|
+
...experiment,
|
|
232
|
+
finishedAt: iso(now()),
|
|
233
|
+
status: failure ? "error" : opts.signal?.aborted ? "cancelled" : "done",
|
|
234
|
+
...(failure ? { error: message(failure) } : {}),
|
|
235
|
+
summary: summarize(records),
|
|
236
|
+
};
|
|
237
|
+
await putExperiment(opts.store, finished);
|
|
238
|
+
return finished;
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,GAEd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAoDjC,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,kFAAkF;AAClF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAElE,MAAM,GAAG,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AAC/D,MAAM,OAAO,GAAG,CAAC,CAAU,EAAU,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAErF,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAc5B;IACC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACjC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;IACpB,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;IACtB,MAAM,IAAI,GAAG;QACX,MAAM,EAAE,CAAC,CAAC,EAAE;QACZ,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAC5B,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC;KACxB,CAAC;IACF,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,4BAA4B,OAAO,CAAC,CAAC,CAAC,EAAE;YACvF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI;SAC/D,CAAC;IACJ,CAAC;IACD,IAAI,GAAsD,CAAC;IAC3D,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACnE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,qCAAqC,OAAO,CAAC,CAAC,CAAC,EAAE;YAChG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI;SAC/D,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5D,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClE,IAAI,OAAgB,CAAC;IACrB,IAAI,YAAgC,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,YAAY,EAAE,CAAC;QACzB,YAAY,GAAG,wBAAwB,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,CAAC;IACD;;;;;;;OAOG;IACH,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3D,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC;IACzE,CAAC;IAED,MAAM,MAAM,GAAgB,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IACzH,MAAM,MAAM,GAAG,CAAC,GAAG,WAAW,EAAE,GAAG,WAAW,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1F,6EAA6E;IAC7E,yDAAyD;IACzD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9D,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,OAAO;QACL,GAAG,IAAI;QACP,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,MAAM;QACN,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;QACvD,OAAO;QACP,MAAM;QACN,IAAI;KACL,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAA0B;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;IAClC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,mBAAmB,CAAC;IAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;QAAE,MAAM,IAAI,iBAAiB,CAAC,oCAAoC,CAAC,CAAC;IACjH,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC;QAAE,MAAM,IAAI,iBAAiB,CAAC,wCAAwC,CAAC,CAAC;IAC7H,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1C,MAAM,IAAI,iBAAiB,CACzB,4BAA4B,IAAI,CAAC,eAAe,yDAAyD,CAC1G,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChH,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,iBAAiB,CAAC,eAAe,IAAI,CAAC,YAAY,4BAA4B,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;IAC3G,CAAC;IACD,oGAAoG;IACpG,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc;WACrC,QAAQ,EAAE,cAAc;WACxB,CAAC,MAAM,oBAAoB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/G,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAEzG,IAAI,UAAsB,CAAC;IAC3B,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,cAAc,KAAK,cAAc,IAAI,QAAQ,CAAC,eAAe,KAAK,IAAI,CAAC,eAAe;eAC9F,QAAQ,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YAClC,MAAM,IAAI,iBAAiB,CACzB,eAAe,QAAQ,CAAC,EAAE,kBAAkB,QAAQ,CAAC,cAAc,eAAe,QAAQ,CAAC,eAAe,MAAM,QAAQ,CAAC,OAAO,IAAI;kBAChI,kCAAkC,CACvC,CAAC;QACJ,CAAC;QACD,2EAA2E;QAC3E,yEAAyE;QACzE,gCAAgC;QAChC,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE;YAC3E,OAAO,EAAE,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;SAC5D,CAAC,CAAC;QACH,UAAU,GAAG,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;QACzD,OAAO,UAAU,CAAC,UAAU,CAAC;QAC7B,OAAO,UAAU,CAAC,KAAK,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,CAAC,CAAC;QAClC,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,UAAU,GAAG;YACX,EAAE;YACF,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,cAAc,EAAE,QAAQ,CAAC,OAAO;YAChC,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO;YACP,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YACrB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;YAC5B,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,CAAC;IACJ,CAAC;IACD,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAE5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1F;;;;;;;;OAQG;IACH,MAAM,WAAW,GAAG,KAAK,EAAE,MAAiB,EAAiD,EAAE;QAC7F,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;YAClC,IAAI,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;YACnH,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3G,IAAI,KAAK;gBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;YAC9C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,MAAM,IAAI,iBAAiB,CACzB,sCAAsC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,gCAAgC;sBAChG,GAAG,OAAO,GAAG,CAAC,mCAAmC,CACtD,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAC9C,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,OAA0C,CAAC;IAC/C,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAW,CAAC,IAAI,CAAC,eAAe,EAAE,uBAAuB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QACtH,MAAM,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;YACzD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;YAC5F,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACrB,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;gBACnF,OAAO;YACT,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;gBAAE,OAAO;YACjC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC;gBAC1B,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM;gBAC3E,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC;gBAChC,OAAO,EAAE,UAAU,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG;gBACzF,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9E,CAAC,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,CAAC;IACd,CAAC;YAAS,CAAC;QACT,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,OAAO,EAAE,CAAC;YAClB,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,KAAK,IAAI,KAAK,CAAC,sCAAsC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAe;QAC3B,GAAG,UAAU;QACb,UAAU,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;QACtB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM;QACvE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC;KAC5B,CAAC;IACF,MAAM,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/score.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic scoring of a run's Result against a case's expectations.
|
|
3
|
+
*
|
|
4
|
+
* One score per expected field (`eval/field@1`). Field access is by dot path
|
|
5
|
+
* (`"forecast.amount"`, `"items.0.id"`). An expected field the output does not
|
|
6
|
+
* have FAILS — silently skipping a missing field is how a broken run goes green.
|
|
7
|
+
*
|
|
8
|
+
* Per field, the expectation is:
|
|
9
|
+
* - a matcher object (`{ $oneOf: [...] }`, see {@link Matcher});
|
|
10
|
+
* - a numeric band string (`">= 0.8"`) when the actual value is a number;
|
|
11
|
+
* - a plain object: SUBSET match — every key it names must match (leaves
|
|
12
|
+
* recursively by these same rules), keys it does not name are ignored;
|
|
13
|
+
* - anything else: deep equality (arrays ordered and length-sensitive).
|
|
14
|
+
*
|
|
15
|
+
* Limits (`eval/limit@1`) are scored too, and only WARN: a breach is counted
|
|
16
|
+
* and shown, and never flips a case's pass.
|
|
17
|
+
*
|
|
18
|
+
* AGAINST A DECLARED RESULT. When the experiment knows the Result the workflow
|
|
19
|
+
* version declares (`Experiment.resultFields`), an expectation that version
|
|
20
|
+
* cannot meet by construction is not a failure of the run: a field it does
|
|
21
|
+
* not declare, or declares with a type the expectation cannot match, is scored
|
|
22
|
+
* `pass: null` (n/a) with the reason. The dataset is older than the version;
|
|
23
|
+
* that is a finding about the dataset, shown as one, not a regression.
|
|
24
|
+
*/
|
|
25
|
+
import type { Case, Header, Json, Limits, Matcher, Metrics, ResultField, Score } from "./types.js";
|
|
26
|
+
export declare const FIELD_SCORER = "eval/field@1";
|
|
27
|
+
export declare const LIMIT_SCORER = "eval/limit@1";
|
|
28
|
+
declare const MISSING: unique symbol;
|
|
29
|
+
type Actual = Json | typeof MISSING;
|
|
30
|
+
/** Read a dot path out of a value. `MISSING` when any segment is absent. */
|
|
31
|
+
export declare function readField(value: Json | null | undefined, path: string): Json | undefined;
|
|
32
|
+
export declare function deepEqual(a: Json, b: Json): boolean;
|
|
33
|
+
export type BandOp = ">=" | "<=" | ">" | "<";
|
|
34
|
+
export declare function parseBand(expr: string): {
|
|
35
|
+
op: BandOp;
|
|
36
|
+
threshold: number;
|
|
37
|
+
} | null;
|
|
38
|
+
/** Whether an expectation is a matcher object (every key starts with `$`). */
|
|
39
|
+
export declare function isMatcher(v: unknown): v is Matcher;
|
|
40
|
+
/** Longest `$regex` pattern scoring will compile. */
|
|
41
|
+
export declare const MAX_REGEX_LENGTH = 256;
|
|
42
|
+
/**
|
|
43
|
+
* Why a `$regex` pattern is refused before it is compiled, or null.
|
|
44
|
+
*
|
|
45
|
+
* TRUST MODEL. Cases come from a shared bucket, and JavaScript's regex engine
|
|
46
|
+
* backtracks: a pattern like `(a+)+$` runs for exponential time on a short
|
|
47
|
+
* non-matching string, synchronously, inside scoring — no timeout can
|
|
48
|
+
* interrupt it, and the CLI or the ROC tab freezes. So a pattern is refused
|
|
49
|
+
* when it is longer than {@link MAX_REGEX_LENGTH} or quantifies a group that
|
|
50
|
+
* itself contains a quantifier (`(a+)+`, `(a*)*`, `((ab)+){2,}`; a fixed
|
|
51
|
+
* `{n}` does not count). This is a heuristic, not a proof: overlapping
|
|
52
|
+
* alternations (`(a|a)*`) still pass it.
|
|
53
|
+
* Anyone who can write cases can make scoring slow; datasets are trusted the
|
|
54
|
+
* way the workflow code they test is.
|
|
55
|
+
*/
|
|
56
|
+
export declare function unsafeRegex(pattern: string): string | null;
|
|
57
|
+
/** null when `actual` satisfies `expected`, otherwise why not. */
|
|
58
|
+
export declare function matchValue(expected: Json | Matcher, actual: Actual | Json): string | null;
|
|
59
|
+
/**
|
|
60
|
+
* The value types an expectation can match: `["number"]` for `{ $gte: 1 }`,
|
|
61
|
+
* `["number", "string"]` for a band string such as `">= 0.8"` (a band against
|
|
62
|
+
* a number, plain equality against a string), null when it can match any
|
|
63
|
+
* (`null`, `{ $exists }`, an empty or unknown matcher).
|
|
64
|
+
*/
|
|
65
|
+
export declare function expectedTypesOf(want: Json | Matcher): string[] | null;
|
|
66
|
+
/**
|
|
67
|
+
* One `eval/field@1` score per expected field. With `resultFields` (the
|
|
68
|
+
* version's declared Result), an expectation the declaration rules out is
|
|
69
|
+
* n/a (`pass: null`) rather than a failure — see the file comment.
|
|
70
|
+
*/
|
|
71
|
+
export declare function scoreFields(expected: Case["expected"], output: Json | null | undefined, resultFields?: readonly ResultField[]): Score[];
|
|
72
|
+
/** The limits in force for a case: the header's, each overridden by the case's own. */
|
|
73
|
+
export declare function effectiveLimits(header: Pick<Header, "limits"> | undefined, c: Pick<Case, "limits">): Limits;
|
|
74
|
+
/** One `eval/limit@1` score per limit in force. A metric that was not measured is not judged. */
|
|
75
|
+
export declare function scoreLimits(limits: Limits, metrics: Metrics): Score[];
|
|
76
|
+
/** A case passes when every field score passes; null when there were none. Limits never count. */
|
|
77
|
+
export declare function casePass(scores: Score[]): boolean | null;
|
|
78
|
+
export interface ResultCompatibility {
|
|
79
|
+
/** Expected fields the Result does not declare, with the cases expecting each. */
|
|
80
|
+
missing: Array<{
|
|
81
|
+
field: string;
|
|
82
|
+
caseIds: string[];
|
|
83
|
+
}>;
|
|
84
|
+
/** Expected fields whose expectation cannot match the declared type. */
|
|
85
|
+
typeChanged: Array<{
|
|
86
|
+
field: string;
|
|
87
|
+
expectedType: string;
|
|
88
|
+
declaredType: string;
|
|
89
|
+
caseIds: string[];
|
|
90
|
+
}>;
|
|
91
|
+
/** Declared fields no case states an expectation for. */
|
|
92
|
+
unexpectedNew: string[];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Whether a dataset's expectations fit a workflow version's declared Result —
|
|
96
|
+
* the check behind "2 dataset cases expect `priority`, which this version no
|
|
97
|
+
* longer returns". Fields are reported by their top-level name; each list is
|
|
98
|
+
* sorted.
|
|
99
|
+
*/
|
|
100
|
+
export declare function checkResultCompatibility(resultFields: readonly ResultField[], cases: ReadonlyArray<Pick<Case, "id" | "expected">>): ResultCompatibility;
|
|
101
|
+
export {};
|
|
102
|
+
//# sourceMappingURL=score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../src/score.ts"],"names":[],"mappings":"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"}
|