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.mjs
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as ChannelName, o as PromptId, s as VersionNumber, t as AnpordApi } from "./client-
|
|
2
|
+
import { a as ChannelName, o as PromptId, s as VersionNumber, t as AnpordApi } from "./client-BfdjFtlV.mjs";
|
|
3
|
+
import { C as EvalTrigger, s as EvalRun } from "./evals-Dcc_x6jW.mjs";
|
|
3
4
|
import { i as tokenMatcher, r as asAnpordError } from "./errors-B0YknR5V.mjs";
|
|
4
5
|
import { ClientLayer, webUrlConfig } from "./config.mjs";
|
|
5
|
-
import { n as compileEvalEffect } from "./compiler-
|
|
6
|
+
import { n as compileEvalEffect } from "./compiler-D96wI7UP.mjs";
|
|
6
7
|
import { Args, Command, Options } from "@effect/cli";
|
|
7
8
|
import { FetchHttpClient, FileSystem, HttpClient, HttpClientError, HttpClientRequest } from "@effect/platform";
|
|
8
9
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
9
10
|
import { Cause, Clock, Config, Console, Data, Duration, Effect, Layer, Option, ParseResult, Redacted, Ref, Schema } from "effect";
|
|
10
11
|
import { parseDocument } from "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.gen(function* () {
|
|
|
58
59
|
}).pipe(Effect.withSpan("Cli.evalFilesIn"));
|
|
59
60
|
//#endregion
|
|
60
61
|
//#region src/cli/eval-gate.ts
|
|
62
|
+
const EvalGate = 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.gen(function* () {
|
|
|
160
172
|
return (run, elapsedMs) => Effect.gen(function* () {
|
|
161
173
|
if (!interactive) return;
|
|
162
174
|
const rows = yield* 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* 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 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 = Command.make("import", {
|
|
|
515
527
|
return yield* note(summaryOf(suite.tally));
|
|
516
528
|
}).pipe(Effect.withSpan("Cli.evalImport"))).pipe(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 = Schema.Struct({
|
|
532
|
+
file: Schema.String,
|
|
533
|
+
problems: Schema.Array(Schema.String),
|
|
534
|
+
run: Schema.NullOr(EvalRun),
|
|
535
|
+
runId: Schema.NullOr(Schema.String)
|
|
536
|
+
});
|
|
537
|
+
Schema.Struct({
|
|
538
|
+
conclusion: Schema.Literal("failure", "neutral", "success"),
|
|
539
|
+
details_url: Schema.optional(Schema.String),
|
|
540
|
+
name: Schema.Literal("anpord"),
|
|
541
|
+
output: Schema.Struct({
|
|
542
|
+
summary: Schema.String,
|
|
543
|
+
title: 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 = Schema.encodeSync(Schema.parseJson(Schema.Array(EvalOutcome)));
|
|
598
|
+
const appendSummary = (text) => Effect.gen(function* () {
|
|
599
|
+
const path = yield* Config.string("GITHUB_STEP_SUMMARY").pipe(Config.option);
|
|
600
|
+
if (Option.isSome(path) && path.value !== "") yield* (yield* FileSystem.FileSystem).writeFileString(path.value, `${text}\n\n`, { flag: "a" });
|
|
601
|
+
});
|
|
602
|
+
const reportStarted = (file, id) => Effect.gen(function* () {
|
|
603
|
+
const url = runUrl(yield* webUrlConfig, id);
|
|
604
|
+
yield* note(`${file}: ${url}`);
|
|
605
|
+
yield* appendSummary(`[Run ${id}](${url}) started.`);
|
|
606
|
+
});
|
|
607
|
+
const writeReport = (outcomes, path) => Effect.gen(function* () {
|
|
608
|
+
if (Option.isSome(path)) yield* (yield* FileSystem.FileSystem).writeFileString(path.value, reportJson(outcomes));
|
|
609
|
+
});
|
|
610
|
+
const reportFinished = (outcomes) => Effect.gen(function* () {
|
|
611
|
+
const report = buildGithubCheck(outcomes, yield* 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 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.gen(function* () {
|
|
524
626
|
const api = yield* AnpordApi;
|
|
525
627
|
const startedAt = yield* Clock.currentTimeMillis;
|
|
526
628
|
const gap = yield* Ref.make(FIRST_POLL);
|
|
@@ -538,73 +640,28 @@ const waitForRun = (id, onProgress) => Effect.gen(function* () {
|
|
|
538
640
|
body: () => waitThenPoll,
|
|
539
641
|
while: running
|
|
540
642
|
});
|
|
541
|
-
}).pipe(Effect.
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
output: Schema.Struct({
|
|
547
|
-
summary: Schema.String,
|
|
548
|
-
title: Schema.String
|
|
643
|
+
}).pipe(Effect.timeoutFail({
|
|
644
|
+
duration: Duration.seconds(timeoutSeconds),
|
|
645
|
+
onTimeout: () => new EvalWaitTimeout({
|
|
646
|
+
runId: id,
|
|
647
|
+
seconds: timeoutSeconds
|
|
549
648
|
})
|
|
649
|
+
}), Effect.withSpan("Cli.waitForRun", { attributes: { runId: id } }));
|
|
650
|
+
//#endregion
|
|
651
|
+
//#region src/cli/eval-trigger.ts
|
|
652
|
+
const evalTrigger = Effect.gen(function* () {
|
|
653
|
+
const github = yield* Config.boolean("GITHUB_ACTIONS").pipe(Config.withDefault(false));
|
|
654
|
+
const ci = yield* Config.boolean("CI").pipe(Config.withDefault(false));
|
|
655
|
+
if (!github) return { source: ci ? "ci" : "cli" };
|
|
656
|
+
const repository = yield* Config.string("GITHUB_REPOSITORY");
|
|
657
|
+
const runId = yield* Config.string("GITHUB_RUN_ID");
|
|
658
|
+
const attempt = yield* Config.string("GITHUB_RUN_ATTEMPT").pipe(Config.withDefault("1"));
|
|
659
|
+
const server = yield* Config.string("GITHUB_SERVER_URL").pipe(Config.withDefault("https://github.com"));
|
|
660
|
+
return yield* Schema.decodeUnknown(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) => 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.gen(function* () {
|
|
|
665
722
|
});
|
|
666
723
|
//#endregion
|
|
667
724
|
//#region src/cli/eval-command.ts
|
|
668
|
-
const asJson$1 = Options.boolean("json").pipe(Options.withDescription("Print
|
|
669
|
-
const evalFile = Args.text({ name: "file" }).pipe(Args.withDescription("A TypeScript file
|
|
670
|
-
const noWait = Options.boolean("no-wait").pipe(Options.withDescription("Start
|
|
671
|
-
const failOn = Options.choice("fail-on",
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
yield*
|
|
682
|
-
|
|
725
|
+
const asJson$1 = Options.boolean("json").pipe(Options.withDescription("Print each finished run as JSON"));
|
|
726
|
+
const evalFile = Args.text({ name: "file" }).pipe(Args.withDescription("A TypeScript eval file; discovers *.eval.ts when omitted"), Args.optional);
|
|
727
|
+
const noWait = Options.boolean("no-wait").pipe(Options.withDescription("Start runs without waiting"));
|
|
728
|
+
const failOn = Options.choice("fail-on", EvalGate.literals).pipe(Options.withDescription("strict requires every trial to pass"), Options.withDefault("strict"));
|
|
729
|
+
const timeout = Options.integer("timeout").pipe(Options.withDescription("Maximum seconds to wait per run"), Options.withSchema(Schema.Int.pipe(Schema.positive())), Options.withDefault(1200));
|
|
730
|
+
const output = Options.text("output").pipe(Options.withDescription("Write a JSON report to this file"), Options.optional);
|
|
731
|
+
const describe$1 = (error) => error instanceof Error ? error.message : String(error);
|
|
732
|
+
const runOneEval = (file, options, save) => Effect.gen(function* () {
|
|
733
|
+
let runId = null;
|
|
734
|
+
return yield* Effect.gen(function* () {
|
|
735
|
+
const api = yield* AnpordApi;
|
|
736
|
+
const payload = yield* 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.catchAll((error) => 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.gen(function* () {
|
|
701
|
-
if (outcomes.every((outcome) => Option.isNone(outcome.run))) return;
|
|
702
776
|
const context = yield* githubContext;
|
|
703
777
|
if (Option.isNone(context)) return;
|
|
704
778
|
const webUrl = yield* 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.catchAll((error) => note(`The GitHub check was not posted. ${describe$1(error)}`)));
|
|
708
781
|
const runEval = 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.gen(function* () {
|
|
714
789
|
const files = yield* Option.match(file, {
|
|
715
790
|
onNone: () => evalFilesIn("."),
|
|
716
791
|
onSome: (one) => Effect.succeed([one])
|
|
717
792
|
});
|
|
718
793
|
if (files.length === 0) return yield* Effect.fail(new NoEvalFiles());
|
|
719
|
-
const outcomes =
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
794
|
+
const outcomes = [];
|
|
795
|
+
yield* Effect.forEach(files, (one, index) => Effect.gen(function* () {
|
|
796
|
+
const save = (outcome) => 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.provide(ClientLayer))).pipe(Command.withDescription("Compile and run an eval from TypeScript"), Command.withSubcommands([importEval]));
|
|
728
811
|
//#endregion
|
package/dist/cli-runtime.cjs
CHANGED
|
@@ -118,8 +118,8 @@ const executeCli = (definition, args, journal = JOURNAL) => effect.Effect.gen(fu
|
|
|
118
118
|
stdout: ""
|
|
119
119
|
}))));
|
|
120
120
|
}).pipe(effect.Effect.withSpan("CliMock.execute"), effect.Effect.runPromise);
|
|
121
|
-
const runCli = async (definition) => {
|
|
122
|
-
const result = await executeCli(definition, process.argv.slice(2));
|
|
121
|
+
const runCli = async (definition, journal) => {
|
|
122
|
+
const result = await executeCli(definition, process.argv.slice(2), journal);
|
|
123
123
|
process.stdout.write(result.stdout);
|
|
124
124
|
process.stderr.write(result.stderr);
|
|
125
125
|
process.exitCode = result.exitCode;
|
package/dist/cli-runtime.d.cts
CHANGED
|
@@ -6,6 +6,6 @@ interface CliExecution {
|
|
|
6
6
|
readonly stdout: string;
|
|
7
7
|
}
|
|
8
8
|
declare const executeCli: (definition: CliDefinition, args: readonly string[], journal?: string) => Promise<CliExecution>;
|
|
9
|
-
declare const runCli: (definition: CliDefinition) => Promise<void>;
|
|
9
|
+
declare const runCli: (definition: CliDefinition, journal?: string) => Promise<void>;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { CliExecution, executeCli, runCli };
|
package/dist/cli-runtime.d.mts
CHANGED
|
@@ -6,6 +6,6 @@ interface CliExecution {
|
|
|
6
6
|
readonly stdout: string;
|
|
7
7
|
}
|
|
8
8
|
declare const executeCli: (definition: CliDefinition, args: readonly string[], journal?: string) => Promise<CliExecution>;
|
|
9
|
-
declare const runCli: (definition: CliDefinition) => Promise<void>;
|
|
9
|
+
declare const runCli: (definition: CliDefinition, journal?: string) => Promise<void>;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { CliExecution, executeCli, runCli };
|
package/dist/cli-runtime.mjs
CHANGED
|
@@ -117,8 +117,8 @@ const executeCli = (definition, args, journal = JOURNAL) => Effect.gen(function*
|
|
|
117
117
|
stdout: ""
|
|
118
118
|
}))));
|
|
119
119
|
}).pipe(Effect.withSpan("CliMock.execute"), Effect.runPromise);
|
|
120
|
-
const runCli = async (definition) => {
|
|
121
|
-
const result = await executeCli(definition, process.argv.slice(2));
|
|
120
|
+
const runCli = async (definition, journal) => {
|
|
121
|
+
const result = await executeCli(definition, process.argv.slice(2), journal);
|
|
122
122
|
process.stdout.write(result.stdout);
|
|
123
123
|
process.stderr.write(result.stderr);
|
|
124
124
|
process.exitCode = result.exitCode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as EvalTrigger, E as EvalVerify, T as EvalPrompt, _ as HarnessProfile, a as EvalPageCursor, c as EvalRunPage, d as EvalValidator, f as EvalVariables, h as StartedEval, i as EvalName, l as EvalSandbox, m as RerunCellRequest, n as EvalCellHistoryEntry, o as EvalPrepare, p as ModelCatalogue, r as EvalHarness, s as EvalRun, t as CaseCache, u as EvalSource, v as PROFILE_HARNESS_RULE, w as EvalCaseName, x as profileFitsHarness } from "./evals-Dcc_x6jW.mjs";
|
|
2
2
|
import { FetchHttpClient, HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, HttpApiSecurity, HttpClient, HttpClientRequest, OpenApi } from "@effect/platform";
|
|
3
3
|
import { Context, Effect, Layer, Redacted, Schema } from "effect";
|
|
4
4
|
//#region ../schema/src/domain/prompts.ts
|
|
@@ -135,9 +135,9 @@ const EvalModelsRequest = Schema.Struct({
|
|
|
135
135
|
description: "Select a harness whose available models should be listed.",
|
|
136
136
|
identifier: "EvalModelsRequest"
|
|
137
137
|
});
|
|
138
|
-
const
|
|
139
|
-
description: "
|
|
140
|
-
identifier: "
|
|
138
|
+
const PublicEvalSandbox = EvalSandbox.annotations({
|
|
139
|
+
description: "The hosted sandbox a task runs in.",
|
|
140
|
+
identifier: "PublicEvalSandbox"
|
|
141
141
|
});
|
|
142
142
|
const ListEvalsRequest = Schema.Struct({
|
|
143
143
|
cursor: Schema.optional(Schema.NullOr(EvalPageCursor)),
|
|
@@ -162,12 +162,13 @@ const PublicEvalTask = Schema.Struct({
|
|
|
162
162
|
harness: EvalHarness,
|
|
163
163
|
model: Schema.String.pipe(Schema.minLength(1)),
|
|
164
164
|
profile: Schema.optional(HarnessProfile),
|
|
165
|
-
|
|
165
|
+
sandbox: Schema.optional(PublicEvalSandbox)
|
|
166
166
|
}).pipe(Schema.filter(profileFitsHarness, { message: () => PROFILE_HARNESS_RULE })).annotations({
|
|
167
|
-
description: `A harness
|
|
167
|
+
description: `A harness and model, with an optional sandbox and an optional profile layered on the harness. Omit the sandbox to use the default. ${PROFILE_HARNESS_RULE}`,
|
|
168
168
|
identifier: "StartEvalTask"
|
|
169
169
|
});
|
|
170
170
|
const PublicStartEvalRequest = Schema.Struct({
|
|
171
|
+
trigger: Schema.optional(EvalTrigger),
|
|
171
172
|
cases: Schema.Array(PublicEvalCase).pipe(Schema.minItems(1), Schema.maxItems(100)),
|
|
172
173
|
name: Schema.optional(EvalName),
|
|
173
174
|
prompt: EvalPrompt,
|