anpord 0.1.12 → 0.1.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/dist/bin.cjs +208 -125
- package/dist/bin.mjs +208 -125
- package/dist/cli-runtime.cjs +2 -2
- package/dist/cli-runtime.d.cts +1 -1
- package/dist/cli-runtime.d.mts +1 -1
- package/dist/cli-runtime.mjs +2 -2
- package/dist/{client-D22IiHGL.mjs → client-BfdjFtlV.mjs} +7 -6
- package/dist/{client-Byoa41pX.d.mts → client-BgRs8JvW.d.mts} +1891 -227
- package/dist/{client-CFTj9ad0.d.cts → client-BirFO3jz.d.cts} +1891 -227
- package/dist/{client-BnimnYiJ.cjs → client-C-P2I00z.cjs} +7 -6
- package/dist/{compiler-CBMPXIzV.mjs → compiler-D96wI7UP.mjs} +87 -82
- package/dist/{compiler-BFxukiJy.cjs → compiler-uwb1pkdK.cjs} +85 -80
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +1 -1
- package/dist/eval-validations-BRdoZrTQ.mjs +113 -0
- package/dist/eval-validations-_MlnQ_uv.cjs +148 -0
- package/dist/eval.cjs +1 -1
- package/dist/eval.d.cts +33 -13
- package/dist/eval.d.mts +33 -13
- package/dist/eval.mjs +1 -1
- package/dist/evals-B6aT3xgH.d.cts +1364 -0
- package/dist/evals-B6aT3xgH.d.mts +1364 -0
- package/dist/{evals-CvOTyvCX.mjs → evals-Dcc_x6jW.mjs} +54 -11
- package/dist/{evals-B04M0apj.cjs → evals-DouMsrsM.cjs} +71 -16
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +128 -4
- package/dist/index.d.mts +128 -4
- package/dist/index.mjs +1 -1
- package/dist/source.d.cts +1 -1
- package/dist/source.d.mts +1 -1
- package/dist/{types-oXWOHA-X.d.mts → types-Codjaq-o.d.mts} +65 -19
- package/dist/{types-nr4-1c3W.d.cts → types-ISsteq95.d.cts} +65 -19
- package/dist/validator-runtime.cjs +238 -0
- package/dist/validator-runtime.d.cts +9 -0
- package/dist/validator-runtime.d.mts +9 -0
- package/dist/validator-runtime.mjs +237 -0
- package/package.json +1 -1
- package/dist/evals-BUBzpke_.d.cts +0 -647
- package/dist/evals-BUBzpke_.d.mts +0 -647
package/dist/bin.cjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_client = require("./client-
|
|
2
|
+
const require_client = require("./client-C-P2I00z.cjs");
|
|
3
|
+
const require_evals = require("./evals-DouMsrsM.cjs");
|
|
3
4
|
const require_errors = require("./errors-BX1wry8K.cjs");
|
|
4
5
|
const require_config = require("./config.cjs");
|
|
5
|
-
const require_compiler = require("./compiler-
|
|
6
|
+
const require_compiler = require("./compiler-uwb1pkdK.cjs");
|
|
6
7
|
let _effect_cli = require("@effect/cli");
|
|
7
8
|
let _effect_platform = require("@effect/platform");
|
|
8
9
|
let _effect_platform_node = require("@effect/platform-node");
|
|
9
10
|
let effect = require("effect");
|
|
10
11
|
let yaml = require("yaml");
|
|
11
12
|
//#region package.json
|
|
12
|
-
var version$1 = "0.1.
|
|
13
|
+
var version$1 = "0.1.13";
|
|
13
14
|
//#endregion
|
|
14
15
|
//#region ../template/src/extract.ts
|
|
15
16
|
/** Reads escapes too, so text the renderer leaves literal is never reported as
|
|
@@ -58,10 +59,11 @@ const evalFilesIn = (directory) => effect.Effect.gen(function* () {
|
|
|
58
59
|
}).pipe(effect.Effect.withSpan("Cli.evalFilesIn"));
|
|
59
60
|
//#endregion
|
|
60
61
|
//#region src/cli/eval-gate.ts
|
|
62
|
+
const EvalGate = effect.Schema.Literal("strict", "never", "regressed", "unscored");
|
|
61
63
|
const regressions = (run) => run.cells.filter((cell) => cell.comparison?.verdict === "regressed");
|
|
62
64
|
const unscored = (run) => run.cells.filter((cell) => (cell.distribution?.scored ?? 0) === 0);
|
|
63
65
|
const rate = (value) => `${Math.round(value * 100) / 100}`;
|
|
64
|
-
const versionClause
|
|
66
|
+
const versionClause = (run, cell, found) => found.baselineHarnessVersion === found.candidateHarnessVersion ? "" : `${run.tasks[cell.taskIndex]?.harness ?? "harness"} ${found.baselineHarnessVersion} → ${found.candidateHarnessVersion}, `;
|
|
65
67
|
const profileClause = (run, cell, found) => {
|
|
66
68
|
const { baselineProfileVersion, candidateProfileVersion } = found;
|
|
67
69
|
if (baselineProfileVersion === null || candidateProfileVersion === null || baselineProfileVersion === candidateProfileVersion) return "";
|
|
@@ -70,10 +72,20 @@ const profileClause = (run, cell, found) => {
|
|
|
70
72
|
const regressionSentence = (run, cell) => {
|
|
71
73
|
const found = cell.comparison;
|
|
72
74
|
if (found === null) return `${cell.caseName} regressed against its baseline.`;
|
|
73
|
-
return `${cell.caseName} regressed against its baseline: ${versionClause
|
|
75
|
+
return `${cell.caseName} regressed against its baseline: ${versionClause(run, cell, found)}${profileClause(run, cell, found)}pass rate ${rate(found.baselinePassRate)} → ${rate(found.candidatePassRate)}.`;
|
|
74
76
|
};
|
|
75
|
-
const problemsWith = (run, failOn) => {
|
|
77
|
+
const problemsWith = (run, failOn, expected) => {
|
|
76
78
|
if (run.status === "failed") return [run.failure ?? "The run failed."];
|
|
79
|
+
if (run.status !== "finished") return ["The run has not finished."];
|
|
80
|
+
if (run.cells.length === 0) return ["The run recorded no cells."];
|
|
81
|
+
if (failOn === "strict") {
|
|
82
|
+
if (expected && run.cells.length !== expected.cells) return [`Expected ${expected.cells} cells, received ${run.cells.length}.`];
|
|
83
|
+
return run.cells.flatMap((cell) => {
|
|
84
|
+
if (cell.status !== "finished" || cell.trials.length === 0) return [`${cell.caseName} has no complete trial results.`];
|
|
85
|
+
if (expected && cell.trials.length !== expected.trials) return [`${cell.caseName}: expected ${expected.trials} trials, received ${cell.trials.length}.`];
|
|
86
|
+
return cell.trials.flatMap((trial) => trial.status === "passed" && trial.passed ? [] : [`${cell.caseName}, trial ${trial.ordinal}: ${trial.status}.`]);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
77
89
|
if (failOn === "never") return [];
|
|
78
90
|
const found = regressions(run).map((cell) => regressionSentence(run, cell));
|
|
79
91
|
return failOn === "unscored" ? [...found, ...unscored(run).map((cell) => `${cell.caseName} produced no scored trials.`)] : found;
|
|
@@ -115,43 +127,43 @@ const DONE = "●";
|
|
|
115
127
|
const RUNNING = "◐";
|
|
116
128
|
const FILLED = "▰";
|
|
117
129
|
const HOLLOW = "▱";
|
|
118
|
-
const PERCENT
|
|
130
|
+
const PERCENT = 100;
|
|
119
131
|
const SECONDS = 1e3;
|
|
120
132
|
const MINUTE = 60;
|
|
121
133
|
const paint = (colour, text) => `${colour}${text}${RESET}`;
|
|
122
|
-
const
|
|
134
|
+
const formatElapsed = (ms) => {
|
|
123
135
|
const total = Math.floor(ms / SECONDS);
|
|
124
136
|
const minutes = Math.floor(total / MINUTE);
|
|
125
137
|
return minutes === 0 ? `${total}s` : `${minutes}m${String(total % MINUTE).padStart(2, "0")}s`;
|
|
126
138
|
};
|
|
127
|
-
const
|
|
139
|
+
const formatStatus = (cell) => {
|
|
128
140
|
if (cell.status === "finished") return paint(GREEN, DONE);
|
|
129
141
|
return cell.status === "failed" ? paint(RED, DONE) : paint(YELLOW, RUNNING);
|
|
130
142
|
};
|
|
131
|
-
const
|
|
143
|
+
const formatTrialProgress = (cell, trials) => {
|
|
132
144
|
const settled = cell.trials.filter((trial) => trial.status !== "queued" && trial.status !== "running").length;
|
|
133
145
|
return `${FILLED.repeat(settled)}${paint(DIM, HOLLOW.repeat(Math.max(0, trials - settled)))}`;
|
|
134
146
|
};
|
|
135
|
-
const
|
|
147
|
+
const formatPassRate = (cell) => {
|
|
136
148
|
const rate = cell.distribution?.passRate;
|
|
137
149
|
if (rate === void 0 || cell.distribution?.scored === 0) return paint(DIM, "—");
|
|
138
|
-
const shown = `${Math.round(rate * PERCENT
|
|
150
|
+
const shown = `${Math.round(rate * PERCENT)}%`;
|
|
139
151
|
return paint(rate === 1 ? GREEN : RED, shown);
|
|
140
152
|
};
|
|
141
|
-
const
|
|
153
|
+
const formatVariant = (run, cell) => {
|
|
142
154
|
const task = run.tasks[cell.taskIndex];
|
|
143
155
|
return task === void 0 ? "?" : `${task.harness}/${task.model}`;
|
|
144
156
|
};
|
|
145
|
-
const widest = (run) => run.cells.reduce((width, cell) => Math.max(width,
|
|
146
|
-
const
|
|
157
|
+
const widest = (run) => run.cells.reduce((width, cell) => Math.max(width, formatVariant(run, cell).length), 0);
|
|
158
|
+
const formatGrid = (run, trials, elapsedMs) => {
|
|
147
159
|
const width = widest(run);
|
|
148
160
|
const lines = [];
|
|
149
161
|
for (const caseName of run.cases) {
|
|
150
162
|
lines.push(` ${BOLD}${caseName}${RESET}`);
|
|
151
|
-
for (const cell of run.cells.filter((one) => one.caseName === caseName)) lines.push(` ${
|
|
163
|
+
for (const cell of run.cells.filter((one) => one.caseName === caseName)) lines.push(` ${formatStatus(cell)} ${formatVariant(run, cell).padEnd(width)} ${formatTrialProgress(cell, trials)} ${formatPassRate(cell)}`);
|
|
152
164
|
lines.push("");
|
|
153
165
|
}
|
|
154
|
-
lines.push(paint(DIM, ` ${
|
|
166
|
+
lines.push(paint(DIM, ` ${formatElapsed(elapsedMs)} elapsed`));
|
|
155
167
|
return lines;
|
|
156
168
|
};
|
|
157
169
|
const up = (rows) => `[${rows}A[0J`;
|
|
@@ -160,12 +172,12 @@ const liveGrid = (trials, interactive) => effect.Effect.gen(function* () {
|
|
|
160
172
|
return (run, elapsedMs) => effect.Effect.gen(function* () {
|
|
161
173
|
if (!interactive) return;
|
|
162
174
|
const rows = yield* effect.Ref.getAndSet(drawn, 0);
|
|
163
|
-
const lines =
|
|
175
|
+
const lines = formatGrid(run, trials, elapsedMs);
|
|
164
176
|
yield* note(`${rows === 0 ? "" : up(rows)}${lines.join("\n")}`);
|
|
165
177
|
yield* effect.Ref.set(drawn, lines.length);
|
|
166
178
|
});
|
|
167
179
|
});
|
|
168
|
-
const
|
|
180
|
+
const formatGridSummary = (run, trials, drawn) => drawn ? "" : formatGrid(run, trials, 0).join("\n");
|
|
169
181
|
//#endregion
|
|
170
182
|
//#region src/imports/evals-json-errors.ts
|
|
171
183
|
var CaseFileUnreadable = class extends effect.Data.TaggedError("CaseFileUnreadable") {
|
|
@@ -282,7 +294,7 @@ const renderEvalSuite = (file) => {
|
|
|
282
294
|
" ],",
|
|
283
295
|
" tasks: [",
|
|
284
296
|
" /* Name the harness, model and sandbox this suite runs on. */",
|
|
285
|
-
" { harness: \"codex\", model: \"gpt-5.6-sol\"
|
|
297
|
+
" { harness: \"codex\", model: \"gpt-5.6-sol\" },",
|
|
286
298
|
" ],",
|
|
287
299
|
"});"
|
|
288
300
|
].join("\n")}\n`;
|
|
@@ -415,7 +427,7 @@ const renderYamlSuite = (files) => `${[
|
|
|
415
427
|
" ],",
|
|
416
428
|
" tasks: [",
|
|
417
429
|
" /* Name the harness, model and sandbox this suite runs on. */",
|
|
418
|
-
" { harness: \"codex\", model: \"gpt-5.6-sol\"
|
|
430
|
+
" { harness: \"codex\", model: \"gpt-5.6-sol\" },",
|
|
419
431
|
" ],",
|
|
420
432
|
"});"
|
|
421
433
|
].join("\n")}\n`;
|
|
@@ -515,12 +527,102 @@ const importEval = _effect_cli.Command.make("import", {
|
|
|
515
527
|
return yield* note(summaryOf(suite.tally));
|
|
516
528
|
}).pipe(effect.Effect.withSpan("Cli.evalImport"))).pipe(_effect_cli.Command.withDescription("Turn a case file a team already wrote into a suite"));
|
|
517
529
|
//#endregion
|
|
530
|
+
//#region src/cli/eval-outcome.ts
|
|
531
|
+
const EvalOutcome = effect.Schema.Struct({
|
|
532
|
+
file: effect.Schema.String,
|
|
533
|
+
problems: effect.Schema.Array(effect.Schema.String),
|
|
534
|
+
run: effect.Schema.NullOr(require_evals.EvalRun),
|
|
535
|
+
runId: effect.Schema.NullOr(effect.Schema.String)
|
|
536
|
+
});
|
|
537
|
+
effect.Schema.Struct({
|
|
538
|
+
conclusion: effect.Schema.Literal("failure", "neutral", "success"),
|
|
539
|
+
details_url: effect.Schema.optional(effect.Schema.String),
|
|
540
|
+
name: effect.Schema.Literal("anpord"),
|
|
541
|
+
output: effect.Schema.Struct({
|
|
542
|
+
summary: effect.Schema.String,
|
|
543
|
+
title: effect.Schema.String
|
|
544
|
+
})
|
|
545
|
+
});
|
|
546
|
+
const TRUNCATED = "\n\n… truncated";
|
|
547
|
+
const TRAILING_SLASH = /\/$/;
|
|
548
|
+
const TITLES = {
|
|
549
|
+
failure: "Eval gate failed",
|
|
550
|
+
success: "Eval gate passed",
|
|
551
|
+
neutral: "Evals still running"
|
|
552
|
+
};
|
|
553
|
+
const percent = (rate) => rate === void 0 ? "-" : `${Math.round(rate * 100)}%`;
|
|
554
|
+
const escaped = (text) => text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("|", "\\|").replaceAll(/[\r\n]/g, " ");
|
|
555
|
+
const formatComparison = (run, cell) => {
|
|
556
|
+
const comparison = cell.comparison;
|
|
557
|
+
if (comparison === null) return "-";
|
|
558
|
+
const { baselineHarnessVersion: before, candidateHarnessVersion: after } = comparison;
|
|
559
|
+
const changed = before === after ? "" : ` (${run.tasks[cell.taskIndex]?.harness} ${before} → ${after})`;
|
|
560
|
+
return `${comparison.verdict}${changed}`;
|
|
561
|
+
};
|
|
562
|
+
const formatCellRow = (run, cell) => {
|
|
563
|
+
const rate = cell.distribution?.scored ? cell.distribution.passRate : void 0;
|
|
564
|
+
return `| ${escaped(cell.caseName)} | ${escaped(formatVariant(run, cell))} | ${percent(rate)} | ${percent(cell.comparison?.baselinePassRate)} | ${escaped(formatComparison(run, cell))} |`;
|
|
565
|
+
};
|
|
566
|
+
const runUrl = (webUrl, id) => `${webUrl.replace(TRAILING_SLASH, "")}/evals/${encodeURIComponent(id)}`;
|
|
567
|
+
const formatOutcome = ({ file, problems, run, runId }, webUrl) => [
|
|
568
|
+
`### ${escaped(file)}`,
|
|
569
|
+
"",
|
|
570
|
+
...runId === null ? [] : [`[View run](${runUrl(webUrl, runId)})`, ""],
|
|
571
|
+
...problems.map((problem) => `- ${escaped(problem)}`),
|
|
572
|
+
...run === null ? [] : [
|
|
573
|
+
"",
|
|
574
|
+
"| Case | Variant | Pass rate | Baseline | Verdict |",
|
|
575
|
+
"| --- | --- | --- | --- | --- |",
|
|
576
|
+
...run.cells.map((cell) => formatCellRow(run, cell))
|
|
577
|
+
]
|
|
578
|
+
].join("\n");
|
|
579
|
+
const buildGithubCheck = (outcomes, webUrl) => {
|
|
580
|
+
const failed = outcomes.some((outcome) => outcome.problems.length > 0);
|
|
581
|
+
const completed = outcomes.length > 0 && outcomes.every((outcome) => outcome.run !== null);
|
|
582
|
+
const first = outcomes.find((outcome) => outcome.runId !== null);
|
|
583
|
+
const conclusion = failed ? "failure" : completed ? "success" : "neutral";
|
|
584
|
+
const summary = outcomes.map((outcome) => formatOutcome(outcome, webUrl)).join("\n\n");
|
|
585
|
+
return {
|
|
586
|
+
conclusion,
|
|
587
|
+
details_url: first?.runId ? runUrl(webUrl, first.runId) : void 0,
|
|
588
|
+
name: "anpord",
|
|
589
|
+
output: {
|
|
590
|
+
title: TITLES[conclusion],
|
|
591
|
+
summary: summary.length < 65535 ? summary : `${summary.slice(0, 65521)}${TRUNCATED}`
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
};
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region src/cli/eval-report.ts
|
|
597
|
+
const reportJson = effect.Schema.encodeSync(effect.Schema.parseJson(effect.Schema.Array(EvalOutcome)));
|
|
598
|
+
const appendSummary = (text) => effect.Effect.gen(function* () {
|
|
599
|
+
const path = yield* effect.Config.string("GITHUB_STEP_SUMMARY").pipe(effect.Config.option);
|
|
600
|
+
if (effect.Option.isSome(path) && path.value !== "") yield* (yield* _effect_platform.FileSystem.FileSystem).writeFileString(path.value, `${text}\n\n`, { flag: "a" });
|
|
601
|
+
});
|
|
602
|
+
const reportStarted = (file, id) => effect.Effect.gen(function* () {
|
|
603
|
+
const url = runUrl(yield* require_config.webUrlConfig, id);
|
|
604
|
+
yield* note(`${file}: ${url}`);
|
|
605
|
+
yield* appendSummary(`[Run ${id}](${url}) started.`);
|
|
606
|
+
});
|
|
607
|
+
const writeReport = (outcomes, path) => effect.Effect.gen(function* () {
|
|
608
|
+
if (effect.Option.isSome(path)) yield* (yield* _effect_platform.FileSystem.FileSystem).writeFileString(path.value, reportJson(outcomes));
|
|
609
|
+
});
|
|
610
|
+
const reportFinished = (outcomes) => effect.Effect.gen(function* () {
|
|
611
|
+
const report = buildGithubCheck(outcomes, yield* require_config.webUrlConfig);
|
|
612
|
+
yield* appendSummary(`## ${report.output.title}\n\n${report.output.summary}`);
|
|
613
|
+
});
|
|
614
|
+
//#endregion
|
|
518
615
|
//#region src/cli/eval-run.ts
|
|
519
616
|
const FIRST_POLL = 2e3;
|
|
520
617
|
const SLOWEST_POLL = 1e4;
|
|
521
618
|
const WIDENING = 1.5;
|
|
522
619
|
const running = (run) => run.status === "running";
|
|
523
|
-
|
|
620
|
+
var EvalWaitTimeout = class extends effect.Data.TaggedError("EvalWaitTimeout") {
|
|
621
|
+
get message() {
|
|
622
|
+
return `Timed out after ${this.seconds}s waiting for ${this.runId}. The remote run was not cancelled.`;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
const waitForRun = (id, onProgress, timeoutSeconds) => effect.Effect.gen(function* () {
|
|
524
626
|
const api = yield* require_client.AnpordApi;
|
|
525
627
|
const startedAt = yield* effect.Clock.currentTimeMillis;
|
|
526
628
|
const gap = yield* effect.Ref.make(FIRST_POLL);
|
|
@@ -538,73 +640,28 @@ const waitForRun = (id, onProgress) => effect.Effect.gen(function* () {
|
|
|
538
640
|
body: () => waitThenPoll,
|
|
539
641
|
while: running
|
|
540
642
|
});
|
|
541
|
-
}).pipe(effect.Effect.
|
|
542
|
-
effect.
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
output: effect.Schema.Struct({
|
|
547
|
-
summary: effect.Schema.String,
|
|
548
|
-
title: effect.Schema.String
|
|
643
|
+
}).pipe(effect.Effect.timeoutFail({
|
|
644
|
+
duration: effect.Duration.seconds(timeoutSeconds),
|
|
645
|
+
onTimeout: () => new EvalWaitTimeout({
|
|
646
|
+
runId: id,
|
|
647
|
+
seconds: timeoutSeconds
|
|
549
648
|
})
|
|
649
|
+
}), effect.Effect.withSpan("Cli.waitForRun", { attributes: { runId: id } }));
|
|
650
|
+
//#endregion
|
|
651
|
+
//#region src/cli/eval-trigger.ts
|
|
652
|
+
const evalTrigger = effect.Effect.gen(function* () {
|
|
653
|
+
const github = yield* effect.Config.boolean("GITHUB_ACTIONS").pipe(effect.Config.withDefault(false));
|
|
654
|
+
const ci = yield* effect.Config.boolean("CI").pipe(effect.Config.withDefault(false));
|
|
655
|
+
if (!github) return { source: ci ? "ci" : "cli" };
|
|
656
|
+
const repository = yield* effect.Config.string("GITHUB_REPOSITORY");
|
|
657
|
+
const runId = yield* effect.Config.string("GITHUB_RUN_ID");
|
|
658
|
+
const attempt = yield* effect.Config.string("GITHUB_RUN_ATTEMPT").pipe(effect.Config.withDefault("1"));
|
|
659
|
+
const server = yield* effect.Config.string("GITHUB_SERVER_URL").pipe(effect.Config.withDefault("https://github.com"));
|
|
660
|
+
return yield* effect.Schema.decodeUnknown(require_evals.EvalTrigger)({
|
|
661
|
+
source: "ci",
|
|
662
|
+
url: `${server}/${repository}/actions/runs/${runId}/attempts/${attempt}`
|
|
663
|
+
});
|
|
550
664
|
});
|
|
551
|
-
const TRUNCATED = "\n\n… truncated";
|
|
552
|
-
const PERCENT = 100;
|
|
553
|
-
const ABSENT = "—";
|
|
554
|
-
const percent = (rate) => rate === void 0 ? ABSENT : `${Math.round(rate * PERCENT)}%`;
|
|
555
|
-
const rateOf = (cell) => cell.distribution === null || cell.distribution.scored === 0 ? ABSENT : percent(cell.distribution.passRate);
|
|
556
|
-
const versionsOf = (comparison) => ({
|
|
557
|
-
baseline: "baselineHarnessVersion" in comparison && typeof comparison.baselineHarnessVersion === "string" ? comparison.baselineHarnessVersion : void 0,
|
|
558
|
-
candidate: "candidateHarnessVersion" in comparison && typeof comparison.candidateHarnessVersion === "string" ? comparison.candidateHarnessVersion : void 0
|
|
559
|
-
});
|
|
560
|
-
const versionClause = (run, cell) => {
|
|
561
|
-
if (cell.comparison === null) return "";
|
|
562
|
-
const { baseline, candidate } = versionsOf(cell.comparison);
|
|
563
|
-
const harness = run.tasks[cell.taskIndex]?.harness ?? "harness";
|
|
564
|
-
return baseline === void 0 || candidate === void 0 || baseline === candidate ? "" : ` (${harness} ${baseline} → ${candidate})`;
|
|
565
|
-
};
|
|
566
|
-
const verdictOf = (run, cell) => cell.comparison === null ? ABSENT : `${cell.comparison.verdict}${versionClause(run, cell)}`;
|
|
567
|
-
const escaped = (text) => text.replaceAll("|", "\\|");
|
|
568
|
-
const rowOf = (run, cell) => `| ${escaped(cell.caseName)} | ${escaped(variantOf(run, cell))} | ${rateOf(cell)} | ${percent(cell.comparison?.baselinePassRate)} | ${verdictOf(run, cell)} |`;
|
|
569
|
-
const tableOf = (file, run) => [
|
|
570
|
-
`### ${escaped(file)}`,
|
|
571
|
-
"",
|
|
572
|
-
...run.failure === null ? [] : [`Run failed: ${run.failure}`, ""],
|
|
573
|
-
"| Case | Variant | Pass rate | Baseline | Verdict |",
|
|
574
|
-
"| --- | --- | --- | --- | --- |",
|
|
575
|
-
...run.cells.map((cell) => rowOf(run, cell))
|
|
576
|
-
].join("\n");
|
|
577
|
-
const truncated = (summary) => summary.length < 65535 ? summary : `${summary.slice(0, 65521)}${TRUNCATED}`;
|
|
578
|
-
const conclusionOf = (cells) => {
|
|
579
|
-
const verdicts = cells.flatMap((cell) => cell.comparison === null ? [] : [cell.comparison.verdict]);
|
|
580
|
-
if (verdicts.includes("regressed")) return "failure";
|
|
581
|
-
return verdicts.every((verdict) => verdict === "incomparable") ? "neutral" : "success";
|
|
582
|
-
};
|
|
583
|
-
const TITLES = {
|
|
584
|
-
failure: "A cell regressed against its baseline",
|
|
585
|
-
neutral: "Nothing to compare against a baseline",
|
|
586
|
-
success: "No cell regressed against its baseline"
|
|
587
|
-
};
|
|
588
|
-
const checkRunOf = (outcomes, webUrl) => {
|
|
589
|
-
const finished = outcomes.flatMap((outcome) => effect.Option.match(outcome.run, {
|
|
590
|
-
onNone: () => [],
|
|
591
|
-
onSome: (run) => [{
|
|
592
|
-
file: outcome.file,
|
|
593
|
-
run
|
|
594
|
-
}]
|
|
595
|
-
}));
|
|
596
|
-
const conclusion = conclusionOf(finished.flatMap(({ run }) => run.cells));
|
|
597
|
-
const first = finished[0];
|
|
598
|
-
return {
|
|
599
|
-
conclusion,
|
|
600
|
-
details_url: first === void 0 ? void 0 : `${webUrl}/evals/${first.run.id}`,
|
|
601
|
-
name: "anpord",
|
|
602
|
-
output: {
|
|
603
|
-
summary: truncated(finished.map(({ file, run }) => tableOf(file, run)).join("\n\n")),
|
|
604
|
-
title: TITLES[conclusion]
|
|
605
|
-
}
|
|
606
|
-
};
|
|
607
|
-
};
|
|
608
665
|
//#endregion
|
|
609
666
|
//#region src/cli/github-check-client.ts
|
|
610
667
|
const API = "https://api.github.com";
|
|
@@ -665,64 +722,90 @@ const githubContext = effect.Effect.gen(function* () {
|
|
|
665
722
|
});
|
|
666
723
|
//#endregion
|
|
667
724
|
//#region src/cli/eval-command.ts
|
|
668
|
-
const asJson$1 = _effect_cli.Options.boolean("json").pipe(_effect_cli.Options.withDescription("Print
|
|
669
|
-
const evalFile = _effect_cli.Args.text({ name: "file" }).pipe(_effect_cli.Args.withDescription("A TypeScript file
|
|
670
|
-
const noWait = _effect_cli.Options.boolean("no-wait").pipe(_effect_cli.Options.withDescription("Start
|
|
671
|
-
const failOn = _effect_cli.Options.choice("fail-on",
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
yield*
|
|
682
|
-
|
|
725
|
+
const asJson$1 = _effect_cli.Options.boolean("json").pipe(_effect_cli.Options.withDescription("Print each finished run as JSON"));
|
|
726
|
+
const evalFile = _effect_cli.Args.text({ name: "file" }).pipe(_effect_cli.Args.withDescription("A TypeScript eval file; discovers *.eval.ts when omitted"), _effect_cli.Args.optional);
|
|
727
|
+
const noWait = _effect_cli.Options.boolean("no-wait").pipe(_effect_cli.Options.withDescription("Start runs without waiting"));
|
|
728
|
+
const failOn = _effect_cli.Options.choice("fail-on", EvalGate.literals).pipe(_effect_cli.Options.withDescription("strict requires every trial to pass"), _effect_cli.Options.withDefault("strict"));
|
|
729
|
+
const timeout = _effect_cli.Options.integer("timeout").pipe(_effect_cli.Options.withDescription("Maximum seconds to wait per run"), _effect_cli.Options.withSchema(effect.Schema.Int.pipe(effect.Schema.positive())), _effect_cli.Options.withDefault(1200));
|
|
730
|
+
const output = _effect_cli.Options.text("output").pipe(_effect_cli.Options.withDescription("Write a JSON report to this file"), _effect_cli.Options.optional);
|
|
731
|
+
const describe$1 = (error) => error instanceof Error ? error.message : String(error);
|
|
732
|
+
const runOneEval = (file, options, save) => effect.Effect.gen(function* () {
|
|
733
|
+
let runId = null;
|
|
734
|
+
return yield* effect.Effect.gen(function* () {
|
|
735
|
+
const api = yield* require_client.AnpordApi;
|
|
736
|
+
const payload = yield* require_compiler.compileEvalEffect(file);
|
|
737
|
+
const trigger = yield* evalTrigger;
|
|
738
|
+
const started = yield* api.evals.start({ payload: {
|
|
739
|
+
...payload,
|
|
740
|
+
trigger
|
|
741
|
+
} });
|
|
742
|
+
runId = started.id;
|
|
743
|
+
yield* reportStarted(file, runId);
|
|
744
|
+
const pending = {
|
|
683
745
|
file,
|
|
746
|
+
runId,
|
|
684
747
|
problems: [],
|
|
685
|
-
run:
|
|
748
|
+
run: null
|
|
686
749
|
};
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
750
|
+
yield* save(pending);
|
|
751
|
+
if (options.skipWait) {
|
|
752
|
+
yield* json(started);
|
|
753
|
+
return pending;
|
|
754
|
+
}
|
|
755
|
+
const live = !options.wantsJson && (yield* attended);
|
|
756
|
+
const draw = yield* liveGrid(payload.trials, live);
|
|
757
|
+
const run = yield* waitForRun(runId, draw, options.timeoutSeconds);
|
|
758
|
+
yield* options.wantsJson ? json(run) : note(formatGridSummary(run, payload.trials, live));
|
|
759
|
+
return {
|
|
760
|
+
file,
|
|
761
|
+
runId,
|
|
762
|
+
run,
|
|
763
|
+
problems: problemsWith(run, options.gate, {
|
|
764
|
+
cells: payload.cases.length * payload.tasks.length,
|
|
765
|
+
trials: payload.trials
|
|
766
|
+
})
|
|
767
|
+
};
|
|
768
|
+
}).pipe(effect.Effect.catchAll((error) => effect.Effect.succeed({
|
|
694
769
|
file,
|
|
695
|
-
|
|
696
|
-
run:
|
|
697
|
-
|
|
770
|
+
runId,
|
|
771
|
+
run: null,
|
|
772
|
+
problems: [describe$1(error)]
|
|
773
|
+
})));
|
|
698
774
|
});
|
|
699
|
-
const describe$1 = (error) => error instanceof Error ? error.message : String(error);
|
|
700
775
|
const reportToGithub = (outcomes) => effect.Effect.gen(function* () {
|
|
701
|
-
if (outcomes.every((outcome) => effect.Option.isNone(outcome.run))) return;
|
|
702
776
|
const context = yield* githubContext;
|
|
703
777
|
if (effect.Option.isNone(context)) return;
|
|
704
778
|
const webUrl = yield* require_config.webUrlConfig;
|
|
705
|
-
yield* postCheckRun(context.value,
|
|
706
|
-
yield* note(`Posted the anpord check on ${context.value.sha}`);
|
|
779
|
+
yield* postCheckRun(context.value, buildGithubCheck(outcomes, webUrl));
|
|
707
780
|
}).pipe(effect.Effect.catchAll((error) => note(`The GitHub check was not posted. ${describe$1(error)}`)));
|
|
708
781
|
const runEval = _effect_cli.Command.make("eval", {
|
|
709
782
|
asJson: asJson$1,
|
|
710
783
|
evalFile,
|
|
711
784
|
failOn,
|
|
712
|
-
noWait
|
|
713
|
-
|
|
785
|
+
noWait,
|
|
786
|
+
output,
|
|
787
|
+
timeout
|
|
788
|
+
}, ({ asJson: wantsJson, evalFile: file, failOn: gate, noWait: skipWait, output: path, timeout: timeoutSeconds }) => effect.Effect.gen(function* () {
|
|
714
789
|
const files = yield* effect.Option.match(file, {
|
|
715
790
|
onNone: () => evalFilesIn("."),
|
|
716
791
|
onSome: (one) => effect.Effect.succeed([one])
|
|
717
792
|
});
|
|
718
793
|
if (files.length === 0) return yield* effect.Effect.fail(new NoEvalFiles());
|
|
719
|
-
const outcomes =
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
794
|
+
const outcomes = [];
|
|
795
|
+
yield* effect.Effect.forEach(files, (one, index) => effect.Effect.gen(function* () {
|
|
796
|
+
const save = (outcome) => effect.Effect.gen(function* () {
|
|
797
|
+
outcomes[index] = outcome;
|
|
798
|
+
yield* writeReport(outcomes, path);
|
|
799
|
+
});
|
|
800
|
+
yield* save(yield* runOneEval(one, {
|
|
801
|
+
gate,
|
|
802
|
+
skipWait,
|
|
803
|
+
timeoutSeconds,
|
|
804
|
+
wantsJson
|
|
805
|
+
}, save));
|
|
724
806
|
}));
|
|
725
|
-
yield*
|
|
807
|
+
yield* reportFinished(outcomes);
|
|
808
|
+
if (!skipWait) yield* reportToGithub(outcomes);
|
|
726
809
|
return yield* failWhen(outcomes.flatMap((outcome) => outcome.problems));
|
|
727
810
|
}).pipe(effect.Effect.provide(require_config.ClientLayer))).pipe(_effect_cli.Command.withDescription("Compile and run an eval from TypeScript"), _effect_cli.Command.withSubcommands([importEval]));
|
|
728
811
|
//#endregion
|