lastlight-evals 0.1.1 → 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 +198 -39
- package/dashboard/dist/assets/index-YwScAAxm.js +238 -0
- package/dashboard/dist/assets/index-uU9Sj3M4.css +1 -0
- package/dashboard/dist/index.html +19 -0
- package/dashboard/dist/logo.png +0 -0
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/.github/workflows/ci.yml +16 -0
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/README.md +7 -1
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/package.json +3 -1
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/scripts/lint.mjs +25 -0
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/test/date-range.test.ts +14 -0
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/tsconfig.json +13 -0
- package/datasets/code-fix/tests/codefix__date-range-off-by-one/date-range.test.ts +5 -5
- package/dist/config.js +100 -0
- package/dist/config.js.map +1 -0
- package/dist/init.js +229 -32
- package/dist/init.js.map +1 -1
- package/dist/mechanism.test.js +41 -0
- package/dist/mechanism.test.js.map +1 -1
- package/dist/metrics.js +91 -3
- package/dist/metrics.js.map +1 -1
- package/dist/paths.js +51 -1
- package/dist/paths.js.map +1 -1
- package/dist/report.js +128 -6
- package/dist/report.js.map +1 -1
- package/dist/run-instance.js +143 -14
- package/dist/run-instance.js.map +1 -1
- package/dist/run.js +401 -89
- package/dist/run.js.map +1 -1
- package/dist/serve.js +136 -0
- package/dist/serve.js.map +1 -0
- package/examples/overlay/README.md +30 -0
- package/examples/overlay/config.yaml +36 -0
- package/examples/overlay-anthropic/config.yaml +27 -0
- package/package.json +16 -6
- package/dist/html-report.js +0 -325
- package/dist/html-report.js.map +0 -1
package/dist/init.js
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `lastlight-evals init [dir]` — scaffold
|
|
3
|
-
* from the shipped defaults, then optionally version it as a private GitHub repo.
|
|
2
|
+
* `lastlight-evals init [dir]` — scaffold an eval workspace, in one of two shapes.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
* `workflows/` / `skills/` / `agent-context/` (which shadow the
|
|
7
|
-
* logical name) AND its own `evals/datasets/` + `evals/models.json`.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* installed CLI supplies the runtime.
|
|
4
|
+
* 1. **Plain** (`init [dir]`) — a self-contained overlay+evals repo: it carries
|
|
5
|
+
* its own `workflows/` / `skills/` / `agent-context/` (which shadow the
|
|
6
|
+
* built-ins by logical name) AND its own `evals/datasets/` + `evals/models.json`.
|
|
7
|
+
* Run it with `--overlay .`. Offered git/GitHub bootstrap via core's
|
|
8
|
+
* `detectGh` + `bootstrapOverlayRepo`.
|
|
11
9
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* 2. **Separate** (`init [dir] --clone <owner/repo>`) — the recommended shape
|
|
11
|
+
* when you already have a deployment overlay repo (e.g. `lastlight-instance`).
|
|
12
|
+
* The overlay is cloned into `<dir>/instance/` (its own git checkout, which
|
|
13
|
+
* the workspace `.gitignore`s) and the evals live at the workspace root
|
|
14
|
+
* (`evals/datasets/`, `evals/models.json`). The runner auto-detects `./instance`
|
|
15
|
+
* as the overlay and the local `./evals/datasets`, so a bare
|
|
16
|
+
* `lastlight-evals run` from the workspace "just works" — no flags. Evals stay
|
|
17
|
+
* out of your deployment repo; you `git pull` the overlay in `instance/`.
|
|
18
|
+
*
|
|
19
|
+
* It's pure data/config — no node deps; the globally installed CLI supplies the runtime.
|
|
15
20
|
*/
|
|
16
|
-
import { existsSync, mkdirSync, writeFileSync, cpSync } from "node:fs";
|
|
21
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, cpSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
17
22
|
import { join, resolve, basename } from "node:path";
|
|
23
|
+
import { execFile } from "node:child_process";
|
|
24
|
+
import { promisify } from "node:util";
|
|
18
25
|
import * as p from "@clack/prompts";
|
|
19
26
|
import chalk from "chalk";
|
|
20
27
|
import { detectGh, bootstrapOverlayRepo } from "lastlight/evals";
|
|
21
28
|
import { builtinDatasetsRoot, builtinModelsPath } from "./paths.js";
|
|
29
|
+
const exec = promisify(execFile);
|
|
22
30
|
const CONFIG_YAML = `# Last Light — eval/overlay config (merged over the core config/default.yaml).
|
|
23
31
|
# Arrays replace, maps deep-merge, env vars override. See the lastlight docs.
|
|
24
32
|
#
|
|
@@ -37,7 +45,37 @@ secrets/*
|
|
|
37
45
|
!secrets/.env.example
|
|
38
46
|
*.pem
|
|
39
47
|
`;
|
|
40
|
-
|
|
48
|
+
// Entries every eval workspace MUST ignore. `eval-results/` is always required;
|
|
49
|
+
// the Separate layout adds `instance/` (a nested overlay checkout). These are
|
|
50
|
+
// enforced even when a `.gitignore` already exists (plain init writes the full
|
|
51
|
+
// template; clone-into-subdir adds these to whatever the dir already has).
|
|
52
|
+
const EVAL_OUTPUT_IGNORE = "eval-results/";
|
|
53
|
+
const INSTANCE_IGNORE = "instance/";
|
|
54
|
+
/**
|
|
55
|
+
* Guarantee `.gitignore` exists and ignores `required` entries. Writes the full
|
|
56
|
+
* template when absent; otherwise appends only the lines it's missing
|
|
57
|
+
* (idempotent) so an existing `.gitignore` is preserved but the eval output (and,
|
|
58
|
+
* in the Separate layout, the `instance/` overlay checkout) is never committed.
|
|
59
|
+
*/
|
|
60
|
+
function ensureGitignore(dir, created, required) {
|
|
61
|
+
const abs = join(dir, ".gitignore");
|
|
62
|
+
if (!existsSync(abs)) {
|
|
63
|
+
const extra = required.filter((l) => !GITIGNORE.includes(l));
|
|
64
|
+
writeFileSync(abs, extra.length ? `${GITIGNORE}\n${extra.join("\n")}\n` : GITIGNORE, "utf8");
|
|
65
|
+
created.push(".gitignore");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const existing = readFileSync(abs, "utf8");
|
|
69
|
+
const have = new Set(existing.split("\n").map((l) => l.trim()));
|
|
70
|
+
const missing = required.filter((l) => !have.has(l));
|
|
71
|
+
if (!missing.length)
|
|
72
|
+
return;
|
|
73
|
+
const sep = existing.endsWith("\n") ? "" : "\n";
|
|
74
|
+
writeFileSync(abs, `${existing}${sep}\n# Last Light eval workspace — regenerated/checkout artifacts, never commit.\n${missing.join("\n")}\n`, "utf8");
|
|
75
|
+
created.push(`.gitignore (added ${missing.join(", ")})`);
|
|
76
|
+
}
|
|
77
|
+
/** README for the plain (self-overlay) workspace. */
|
|
78
|
+
function readmePlain(name) {
|
|
41
79
|
return `# ${name}
|
|
42
80
|
|
|
43
81
|
A Last Light **eval + overlay** workspace, created by \`lastlight-evals init\`.
|
|
@@ -64,8 +102,81 @@ lastlight-evals run code-fix --overlay .
|
|
|
64
102
|
Override which core build the evals test with \`LASTLIGHT_CORE_DIR=/path/to/lastlight\`.
|
|
65
103
|
`;
|
|
66
104
|
}
|
|
67
|
-
/**
|
|
68
|
-
function
|
|
105
|
+
/** README for the Separate layout — overlay checked out in `instance/`. */
|
|
106
|
+
function readmeSeparate(name, repo) {
|
|
107
|
+
return `# ${name}
|
|
108
|
+
|
|
109
|
+
A Last Light **eval workspace** created by \`lastlight-evals init --clone ${repo}\`.
|
|
110
|
+
|
|
111
|
+
The deployment overlay [\`${repo}\`](https://github.com/${repo}) is checked out in
|
|
112
|
+
\`instance/\` (its own git repo, \`.gitignore\`d here) and the evals live at the
|
|
113
|
+
workspace root. The runner auto-detects \`./instance\` as the overlay and your
|
|
114
|
+
local \`./evals/datasets\`, so a bare run "just works".
|
|
115
|
+
|
|
116
|
+
## Layout
|
|
117
|
+
|
|
118
|
+
- \`instance/\` — your deployment overlay (config, workflows, skills, persona).
|
|
119
|
+
A separate git checkout — \`cd instance && git pull\` to update it.
|
|
120
|
+
- \`evals/datasets/<tier>/\` — eval datasets, seeded with the shipped
|
|
121
|
+
\`triage\` / \`code-fix\` samples. Edit or add tiers here.
|
|
122
|
+
- \`evals/models.json\` — the model registry these evals run against.
|
|
123
|
+
- \`.env\` — your provider key(s). GitHub is mocked, so no GitHub token is needed.
|
|
124
|
+
|
|
125
|
+
## Run
|
|
126
|
+
|
|
127
|
+
\`\`\`bash
|
|
128
|
+
lastlight-evals run # auto: overlay ./instance + local datasets
|
|
129
|
+
lastlight-evals run code-fix # one tier
|
|
130
|
+
lastlight-evals run --compare # cross-vendor set (key-gated)
|
|
131
|
+
\`\`\`
|
|
132
|
+
|
|
133
|
+
Override which core build the evals test with \`LASTLIGHT_CORE_DIR=/path/to/lastlight\`.
|
|
134
|
+
`;
|
|
135
|
+
}
|
|
136
|
+
/** Turn a `--clone` value into a clone URL. Accepts a full URL/scp ref as-is,
|
|
137
|
+
* else treats `owner/repo` as a GitHub slug. */
|
|
138
|
+
function cloneUrl(repo) {
|
|
139
|
+
return /^(https?:\/\/|git@|ssh:\/\/)/.test(repo) ? repo : `https://github.com/${repo}.git`;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Clone an existing overlay repo (e.g. a deployment's `lastlight-instance`) into
|
|
143
|
+
* `target` as its own git checkout. Idempotent — if `target` is already a repo it
|
|
144
|
+
* leaves it untouched (so re-running `init` doesn't reclone). Clones to a temp
|
|
145
|
+
* sibling first and moves the contents in, so a pre-populated `target` keeps its
|
|
146
|
+
* files. Prefers `gh` (handles private-repo auth) and falls back to plain `git`.
|
|
147
|
+
*/
|
|
148
|
+
async function cloneOverlayInto(target, repo) {
|
|
149
|
+
if (existsSync(join(target, ".git"))) {
|
|
150
|
+
p.log.info(`${chalk.cyan(target)} is already a checkout — skipping clone of ${chalk.bold(repo)} (\`cd instance && git pull\` to update).`);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
mkdirSync(target, { recursive: true });
|
|
154
|
+
const tmp = `${target}.clone-tmp`;
|
|
155
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
156
|
+
p.log.step(`Cloning ${chalk.bold(repo)} → ${chalk.cyan(target)}`);
|
|
157
|
+
try {
|
|
158
|
+
await exec("gh", ["repo", "clone", repo, tmp]);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
await exec("git", ["clone", "--quiet", cloneUrl(repo), tmp]);
|
|
162
|
+
}
|
|
163
|
+
// Move cloned entries (including .git) into target, never clobbering what's there.
|
|
164
|
+
for (const entry of readdirSync(tmp)) {
|
|
165
|
+
const dst = join(target, entry);
|
|
166
|
+
if (!existsSync(dst))
|
|
167
|
+
renameSync(join(tmp, entry), dst);
|
|
168
|
+
}
|
|
169
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Write the default files/dirs into `dir`, never overwriting what exists.
|
|
174
|
+
* `overlayRepo` set ⇒ Separate layout: the overlay is a cloned `instance/`, so we
|
|
175
|
+
* skip the root `config.yaml` + `workflows/skills/agent-context` placeholders
|
|
176
|
+
* (those belong to — and are read from — `instance/`) and `.gitignore` the
|
|
177
|
+
* checkout. Either way we always seed `evals/datasets/` + `evals/models.json`.
|
|
178
|
+
*/
|
|
179
|
+
function scaffoldEvalRepo(dir, overlayRepo) {
|
|
69
180
|
const created = [];
|
|
70
181
|
const writeIfMissing = (rel, content) => {
|
|
71
182
|
const abs = join(dir, rel);
|
|
@@ -75,12 +186,9 @@ function scaffoldEvalRepo(dir) {
|
|
|
75
186
|
writeFileSync(abs, content, "utf8");
|
|
76
187
|
created.push(rel);
|
|
77
188
|
};
|
|
78
|
-
// Overlay asset dirs (empty placeholders the user fills in).
|
|
79
|
-
for (const d of ["workflows", "skills", "agent-context"]) {
|
|
80
|
-
const keep = join(d, ".gitkeep");
|
|
81
|
-
writeIfMissing(keep, "");
|
|
82
|
-
}
|
|
83
189
|
// Seed datasets + models from the shipped defaults (a starting point to edit).
|
|
190
|
+
// Always created — the workspace's own `evals/datasets/` is what the runner
|
|
191
|
+
// auto-discovers, so it must exist even when the overlay lives in `instance/`.
|
|
84
192
|
const datasetsDst = join(dir, "evals", "datasets");
|
|
85
193
|
if (!existsSync(datasetsDst)) {
|
|
86
194
|
mkdirSync(datasetsDst, { recursive: true });
|
|
@@ -92,31 +200,120 @@ function scaffoldEvalRepo(dir) {
|
|
|
92
200
|
cpSync(builtinModelsPath(), modelsDst);
|
|
93
201
|
created.push("evals/models.json");
|
|
94
202
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
203
|
+
if (overlayRepo) {
|
|
204
|
+
// Separate layout — overlay assets + config come from the `instance/` checkout.
|
|
205
|
+
ensureGitignore(dir, created, [EVAL_OUTPUT_IGNORE, INSTANCE_IGNORE]);
|
|
206
|
+
writeIfMissing("README.md", readmeSeparate(basename(dir), overlayRepo));
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Plain layout — this repo is its own overlay.
|
|
210
|
+
for (const d of ["workflows", "skills", "agent-context"]) {
|
|
211
|
+
writeIfMissing(join(d, ".gitkeep"), "");
|
|
212
|
+
}
|
|
213
|
+
writeIfMissing("config.yaml", CONFIG_YAML);
|
|
214
|
+
ensureGitignore(dir, created, [EVAL_OUTPUT_IGNORE]);
|
|
215
|
+
writeIfMissing("README.md", readmePlain(basename(dir)));
|
|
216
|
+
}
|
|
98
217
|
return created;
|
|
99
218
|
}
|
|
100
|
-
|
|
219
|
+
const INIT_USAGE = `lastlight-evals init [dir] [options]
|
|
220
|
+
|
|
221
|
+
Scaffold a fresh overlay + evals workspace (seeded triage + code-fix datasets).
|
|
222
|
+
|
|
223
|
+
Arguments:
|
|
224
|
+
dir Target directory (default: lastlight-evals-workspace; "." = here)
|
|
225
|
+
|
|
226
|
+
Options:
|
|
227
|
+
--clone <repo> Separate layout: clone an existing overlay repo (owner/repo or
|
|
228
|
+
URL) into <dir>/instance/ (its own checkout, git-ignored) and
|
|
229
|
+
scaffold evals/ at the root. The runner auto-detects ./instance
|
|
230
|
+
+ ./evals/datasets, so a bare \`lastlight-evals run\` just works.
|
|
231
|
+
--yes, -y Non-interactive: scaffold without prompting; print the manual
|
|
232
|
+
git/GitHub commands instead of running them.
|
|
233
|
+
--no-git Scaffold files only; skip the git/GitHub bootstrap entirely.
|
|
234
|
+
--help, -h Show this help.
|
|
235
|
+
|
|
236
|
+
With no TTY (piped/automation), behaves as --yes so it never blocks on a prompt.
|
|
237
|
+
|
|
238
|
+
Example — point an eval workspace at your deployment overlay:
|
|
239
|
+
lastlight-evals init . --clone your-org/lastlight-instance
|
|
240
|
+
lastlight-evals run`;
|
|
241
|
+
/**
|
|
242
|
+
* `init [dir] [flags]`. Parses its own argv slice so it's safe to drive
|
|
243
|
+
* head-less: a leading `--flag` is never mistaken for the target dir, and the
|
|
244
|
+
* interactive git/GitHub bootstrap is skipped when there's no TTY (or when
|
|
245
|
+
* `--yes`/`--no-git` is passed) so an agent or CI run can't hang on a prompt.
|
|
246
|
+
*/
|
|
247
|
+
export async function runInit(args = []) {
|
|
248
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
249
|
+
console.log(INIT_USAGE);
|
|
250
|
+
return 0;
|
|
251
|
+
}
|
|
252
|
+
const noGit = args.includes("--no-git");
|
|
253
|
+
// Non-interactive when asked (--yes/-y) or whenever stdin isn't a TTY, so
|
|
254
|
+
// automation never blocks on the version-as-repo confirm prompt.
|
|
255
|
+
const yes = args.includes("--yes") || args.includes("-y") || !process.stdin.isTTY;
|
|
256
|
+
// `--clone <repo>` / `--from <repo>` — its value isn't the target dir.
|
|
257
|
+
const valFlag = (...names) => {
|
|
258
|
+
for (let i = 0; i < args.length; i++) {
|
|
259
|
+
const eq = names.find((n) => args[i].startsWith(`${n}=`));
|
|
260
|
+
if (eq)
|
|
261
|
+
return args[i].slice(eq.length + 1);
|
|
262
|
+
if (names.includes(args[i]))
|
|
263
|
+
return args[i + 1];
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
};
|
|
267
|
+
const cloneRepo = valFlag("--clone", "--from");
|
|
268
|
+
const dirArg = args.find((a, i) => !a.startsWith("-") && args[i - 1] !== "--clone" && args[i - 1] !== "--from");
|
|
101
269
|
p.intro(chalk.bold(`Last Light ${chalk.yellow("·")} eval init`));
|
|
102
270
|
const dir = resolve(dirArg ?? "lastlight-evals-workspace");
|
|
271
|
+
// Separate layout: clone the overlay into `<dir>/instance/` (its own checkout),
|
|
272
|
+
// then scaffold the evals at the root with `instance/` git-ignored. The runner
|
|
273
|
+
// auto-detects `./instance` + `./evals/datasets`.
|
|
274
|
+
if (cloneRepo) {
|
|
275
|
+
try {
|
|
276
|
+
await cloneOverlayInto(join(dir, "instance"), cloneRepo);
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
p.log.error(`Clone of ${cloneRepo} failed: ${err.message}`);
|
|
280
|
+
p.outro(chalk.red("aborted"));
|
|
281
|
+
return 1;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
103
284
|
p.log.step(`Scaffolding ${chalk.cyan(dir)}`);
|
|
104
|
-
const created = scaffoldEvalRepo(dir);
|
|
285
|
+
const created = scaffoldEvalRepo(dir, cloneRepo);
|
|
105
286
|
if (created.length) {
|
|
106
287
|
p.log.success(`Created:\n ${created.join("\n ")}`);
|
|
107
288
|
}
|
|
108
289
|
else {
|
|
109
290
|
p.log.info("Nothing to scaffold — every default file already existed.");
|
|
110
291
|
}
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
292
|
+
// Git/GitHub bootstrap (plain layout only — the overlay carries its own repo).
|
|
293
|
+
// `--no-git` skips it; otherwise `yes` forwards core's non-interactive path
|
|
294
|
+
// (scaffolds + prints the manual git/gh commands rather than prompting).
|
|
295
|
+
if (cloneRepo) {
|
|
296
|
+
p.log.info(`Overlay checked out in ${chalk.cyan("instance/")} — ${chalk.dim("cd instance && git pull")} to update it.`);
|
|
297
|
+
}
|
|
298
|
+
else if (noGit) {
|
|
299
|
+
p.log.info(`Skipped git/GitHub bootstrap (--no-git). Version it yourself with ${chalk.dim("git init")}.`);
|
|
115
300
|
}
|
|
116
|
-
|
|
117
|
-
|
|
301
|
+
else {
|
|
302
|
+
try {
|
|
303
|
+
const gh = await detectGh();
|
|
304
|
+
await bootstrapOverlayRepo(dir, { gh, yes });
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
p.log.warn(`Skipped git/GitHub bootstrap: ${err.message}`);
|
|
308
|
+
}
|
|
118
309
|
}
|
|
119
|
-
|
|
310
|
+
const here = dir === process.cwd();
|
|
311
|
+
const runCmd = cloneRepo
|
|
312
|
+
? here
|
|
313
|
+
? "lastlight-evals run"
|
|
314
|
+
: `cd ${dir} && lastlight-evals run`
|
|
315
|
+
: `lastlight-evals run --overlay ${here ? "." : dir}`;
|
|
316
|
+
p.outro(chalk.green(`done — run it with: ${chalk.bold(runCmd)}`));
|
|
120
317
|
return 0;
|
|
121
318
|
}
|
|
122
319
|
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/mechanism.test.js
CHANGED
|
@@ -15,6 +15,7 @@ import { tmpdir } from "node:os";
|
|
|
15
15
|
import { startFakeGitHub } from "./fake-github.js";
|
|
16
16
|
import { seedWorkspace } from "./seed.js";
|
|
17
17
|
import { gradeExecution, gradeBehavioral, gradeTriage } from "./grade.js";
|
|
18
|
+
import { loadMergedConfig, resolvePhaseModel } from "./config.js";
|
|
18
19
|
const staticAuth = { getToken: async () => "fake-token", expiresAt: null };
|
|
19
20
|
describe("fake GitHub + agentic-pi github tools (baseUrl seam)", () => {
|
|
20
21
|
it("serves seeded issues and records mutations made through the real GitHubClient", async () => {
|
|
@@ -61,6 +62,46 @@ describe("fake GitHub + agentic-pi github tools (baseUrl seam)", () => {
|
|
|
61
62
|
}
|
|
62
63
|
});
|
|
63
64
|
});
|
|
65
|
+
describe("config run type — per-step model resolution (config.ts)", () => {
|
|
66
|
+
it("deep-merges overlay config.yaml over core default.yaml (overlay wins per key)", () => {
|
|
67
|
+
const root = mkdtempSync(join(tmpdir(), "ll-eval-cfg-"));
|
|
68
|
+
try {
|
|
69
|
+
// A stand-in core root: just the one file loadMergedConfig reads.
|
|
70
|
+
mkdirSync(join(root, "config"), { recursive: true });
|
|
71
|
+
writeFileSync(join(root, "config", "default.yaml"), "models:\n default: anthropic/claude-sonnet-4-6\nvariants: {}\n");
|
|
72
|
+
// An overlay that retargets some phases + sets a variant.
|
|
73
|
+
const overlay = join(root, "overlay");
|
|
74
|
+
mkdirSync(overlay, { recursive: true });
|
|
75
|
+
writeFileSync(join(overlay, "config.yaml"), "models:\n default: openai/gpt-5.4-mini\n architect: openai/gpt-5.5\nvariants:\n guardrails: low\n");
|
|
76
|
+
const { models, variants } = loadMergedConfig(root, overlay);
|
|
77
|
+
expect(models.default).toBe("openai/gpt-5.4-mini"); // overlay wins
|
|
78
|
+
expect(models.architect).toBe("openai/gpt-5.5"); // overlay-only key kept
|
|
79
|
+
expect(variants.guardrails).toBe("low");
|
|
80
|
+
// No overlay ⇒ just the core defaults.
|
|
81
|
+
const core = loadMergedConfig(root);
|
|
82
|
+
expect(core.models.default).toBe("anthropic/claude-sonnet-4-6");
|
|
83
|
+
expect(core.models.architect).toBeUndefined();
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
rmSync(root, { recursive: true, force: true });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
it("resolvePhaseModel mirrors core precedence: {{models.X}} template → models[phase] → default", () => {
|
|
90
|
+
const models = { default: "m-default", guardrails: "m-guard", explore: "m-explore" };
|
|
91
|
+
// 1. A phase whose YAML names `{{models.guardrails}}` → that template wins.
|
|
92
|
+
expect(resolvePhaseModel("{{models.guardrails}}", "guardrails", models)).toBe("m-guard");
|
|
93
|
+
// 2. A template keyed differently from the phase name (explore.yaml's
|
|
94
|
+
// `read_context` phase uses `{{models.explore}}`) → the TEMPLATE key wins,
|
|
95
|
+
// NOT the phase-name lookup. This is the case the `ctx.models` wiring guards.
|
|
96
|
+
expect(resolvePhaseModel("{{models.explore}}", "read_context", models)).toBe("m-explore");
|
|
97
|
+
// 3. No template, phase name present in the map → that entry.
|
|
98
|
+
expect(resolvePhaseModel(undefined, "guardrails", models)).toBe("m-guard");
|
|
99
|
+
// 4. No template, unmapped phase → the default.
|
|
100
|
+
expect(resolvePhaseModel(undefined, "executor", models)).toBe("m-default");
|
|
101
|
+
// 5. Template referencing an unset key → falls through to phase/default.
|
|
102
|
+
expect(resolvePhaseModel("{{models.missing}}", "executor", models)).toBe("m-default");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
64
105
|
describe("workspace seed + execution grade (SWE-bench resolved)", () => {
|
|
65
106
|
it("flips red→green when the bug is fixed, and detects PASS_TO_PASS regressions", () => {
|
|
66
107
|
const stateDir = mkdtempSync(join(tmpdir(), "ll-eval-mech-"));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mechanism.test.js","sourceRoot":"","sources":["../src/mechanism.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"mechanism.test.js","sourceRoot":"","sources":["../src/mechanism.test.ts"],"names":[],"mappings":"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"}
|
package/dist/metrics.js
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
* Pull per-run token/cost/turn metrics out of the session jsonl the event
|
|
3
3
|
* shim writes (`src/engine/event-shim.ts`). Each phase emits one `result`
|
|
4
4
|
* envelope carrying `total_cost_usd` / `total_input_tokens` /
|
|
5
|
-
* `total_output_tokens
|
|
6
|
-
*
|
|
5
|
+
* `total_output_tokens` (plus `total_cache_read_input_tokens` /
|
|
6
|
+
* `total_cache_creation_input_tokens`, which we sum into `cachedTokens` —
|
|
7
|
+
* Anthropic bills most of its prompt as cached, so omitting it made input
|
|
8
|
+
* collapse to ~zero). We sum them across every jsonl under the run's sessions
|
|
9
|
+
* dir. Best-effort: missing files / lines just contribute zero.
|
|
7
10
|
*/
|
|
8
11
|
import { readdirSync, readFileSync, existsSync, statSync } from "node:fs";
|
|
9
12
|
import { join } from "node:path";
|
|
@@ -43,6 +46,89 @@ export async function drainSessions(sessionsDir, maxMs = 4000, quietMs = 250) {
|
|
|
43
46
|
await new Promise((r) => setTimeout(r, 80));
|
|
44
47
|
}
|
|
45
48
|
}
|
|
49
|
+
/** Enumerate the run's session jsonl files (one per sessionId / phase / sub-agent
|
|
50
|
+
* run), each with its first-line timestamp, sorted chronologically. The harness
|
|
51
|
+
* maps these to workflow phases by start-time window (see run-instance). */
|
|
52
|
+
export function listSessionFiles(sessionsDir) {
|
|
53
|
+
const files = [];
|
|
54
|
+
walkJsonl(join(sessionsDir, "projects"), files);
|
|
55
|
+
walkJsonl(sessionsDir, files);
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
if (seen.has(file))
|
|
60
|
+
continue;
|
|
61
|
+
seen.add(file);
|
|
62
|
+
let firstTs = 0;
|
|
63
|
+
try {
|
|
64
|
+
for (const line of readFileSync(file, "utf8").split("\n")) {
|
|
65
|
+
const t = line.trim();
|
|
66
|
+
if (!t)
|
|
67
|
+
continue;
|
|
68
|
+
try {
|
|
69
|
+
const o = JSON.parse(t);
|
|
70
|
+
if (o.timestamp != null) {
|
|
71
|
+
firstTs = typeof o.timestamp === "number" ? o.timestamp * 1000 : Date.parse(o.timestamp);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
/* keep scanning */
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
/* unreadable — leave firstTs 0 */
|
|
82
|
+
}
|
|
83
|
+
out.push({ file, firstTs });
|
|
84
|
+
}
|
|
85
|
+
return out.sort((a, b) => a.firstTs - b.firstTs);
|
|
86
|
+
}
|
|
87
|
+
/** Concatenate the given jsonl files (already in order) into one raw jsonl
|
|
88
|
+
* string. Best-effort: an unreadable file contributes nothing. */
|
|
89
|
+
export function concatJsonl(files) {
|
|
90
|
+
const chunks = [];
|
|
91
|
+
for (const f of files) {
|
|
92
|
+
try {
|
|
93
|
+
const text = readFileSync(f, "utf8").trim();
|
|
94
|
+
if (text)
|
|
95
|
+
chunks.push(text);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* skip */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return chunks.length ? chunks.join("\n") + "\n" : "";
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Concatenate every session jsonl under a run's sessions dir into one raw jsonl
|
|
105
|
+
* string (stable phase order by path, de-duplicated), for archiving alongside
|
|
106
|
+
* the run + later rendering in the dashboard. Each run uses a fresh temp
|
|
107
|
+
* `stateDir`, so all jsonl under it belong to that one instance — no slug
|
|
108
|
+
* matching needed (the same set `collectMetrics` reads). Best-effort: an
|
|
109
|
+
* unreadable file just contributes nothing.
|
|
110
|
+
*/
|
|
111
|
+
export function readSessionLog(sessionsDir) {
|
|
112
|
+
const files = [];
|
|
113
|
+
walkJsonl(join(sessionsDir, "projects"), files);
|
|
114
|
+
walkJsonl(sessionsDir, files);
|
|
115
|
+
const seen = new Set();
|
|
116
|
+
const chunks = [];
|
|
117
|
+
for (const file of [...files].sort()) {
|
|
118
|
+
if (seen.has(file))
|
|
119
|
+
continue;
|
|
120
|
+
seen.add(file);
|
|
121
|
+
try {
|
|
122
|
+
const text = readFileSync(file, "utf8").trim();
|
|
123
|
+
if (text)
|
|
124
|
+
chunks.push(text);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
/* best-effort */
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return chunks.length ? chunks.join("\n") + "\n" : "";
|
|
131
|
+
}
|
|
46
132
|
function walkJsonl(dir, out) {
|
|
47
133
|
if (!existsSync(dir))
|
|
48
134
|
return;
|
|
@@ -60,7 +146,7 @@ export function collectMetrics(sessionsDir) {
|
|
|
60
146
|
// Fallback: some shim configs write directly under sessionsDir.
|
|
61
147
|
walkJsonl(sessionsDir, files);
|
|
62
148
|
const seen = new Set();
|
|
63
|
-
const metrics = { inputTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
149
|
+
const metrics = { inputTokens: 0, cachedTokens: 0, outputTokens: 0, costUsd: 0 };
|
|
64
150
|
for (const file of files) {
|
|
65
151
|
if (seen.has(file))
|
|
66
152
|
continue;
|
|
@@ -73,6 +159,8 @@ export function collectMetrics(sessionsDir) {
|
|
|
73
159
|
if (env.type !== "result")
|
|
74
160
|
continue;
|
|
75
161
|
metrics.inputTokens += env.total_input_tokens ?? 0;
|
|
162
|
+
metrics.cachedTokens +=
|
|
163
|
+
(env.total_cache_read_input_tokens ?? 0) + (env.total_cache_creation_input_tokens ?? 0);
|
|
76
164
|
metrics.outputTokens += env.total_output_tokens ?? 0;
|
|
77
165
|
metrics.costUsd += env.total_cost_usd ?? 0;
|
|
78
166
|
}
|
package/dist/metrics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.js
CHANGED
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
* Run OUTPUT, by contrast, is written under the caller's cwd (an installed
|
|
11
11
|
* package dir is read-only), overridable via `LASTLIGHT_EVALS_OUT`.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { spawnSync } from "node:child_process";
|
|
15
17
|
/** Package root: the dir holding `datasets/`, `models.json`, `package.json`. */
|
|
16
18
|
export function packageRoot() {
|
|
17
19
|
return resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -24,10 +26,58 @@ export function builtinDatasetsRoot() {
|
|
|
24
26
|
export function builtinModelsPath() {
|
|
25
27
|
return resolve(packageRoot(), "models.json");
|
|
26
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Built dashboard SPA assets (`<pkg>/dashboard/dist`) served by `serve`. Shipped
|
|
31
|
+
* prebuilt in the npm package so an installed CLI needs no Vite at runtime; in
|
|
32
|
+
* this repo it's produced by `npm run build` (which also builds the harness).
|
|
33
|
+
* Overridable via `LASTLIGHT_EVALS_DASHBOARD` for development.
|
|
34
|
+
*/
|
|
35
|
+
export function dashboardDistRoot() {
|
|
36
|
+
return process.env.LASTLIGHT_EVALS_DASHBOARD
|
|
37
|
+
? resolve(process.env.LASTLIGHT_EVALS_DASHBOARD)
|
|
38
|
+
: resolve(packageRoot(), "dashboard", "dist");
|
|
39
|
+
}
|
|
27
40
|
/** Where scorecards/artifacts are written (cwd-relative, NOT the package dir). */
|
|
28
41
|
export function resultsRoot() {
|
|
29
42
|
return process.env.LASTLIGHT_EVALS_OUT
|
|
30
43
|
? resolve(process.env.LASTLIGHT_EVALS_OUT)
|
|
31
44
|
: resolve(process.cwd(), "eval-results");
|
|
32
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The tier-combo directory — `<resultsRoot>/<tiersKey>`. It holds the
|
|
48
|
+
* overview/history `index.html` plus one timestamped subdir per run, so runs
|
|
49
|
+
* accumulate instead of overwriting each other.
|
|
50
|
+
*/
|
|
51
|
+
export function tierResultsDir(tiersKey) {
|
|
52
|
+
return join(resultsRoot(), tiersKey);
|
|
53
|
+
}
|
|
54
|
+
/** Short git SHA of HEAD, or `undefined` outside a repo / on any failure. */
|
|
55
|
+
export function gitShortSha() {
|
|
56
|
+
try {
|
|
57
|
+
const r = spawnSync("git", ["rev-parse", "--short", "HEAD"], { encoding: "utf8" });
|
|
58
|
+
const sha = r.status === 0 ? r.stdout.trim() : "";
|
|
59
|
+
return sha || undefined;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A sortable, filesystem-safe run id: `YYYY-MM-DD_HHMMSS` (UTC, matching the
|
|
67
|
+
* `toISOString()` timestamps used elsewhere) optionally suffixed with the short
|
|
68
|
+
* git SHA of the code under test (e.g. `2026-06-28_143052-a0229c5`). If
|
|
69
|
+
* `parentDir` already holds that id, a numeric `-2`/`-3` suffix is appended so
|
|
70
|
+
* two runs in the same second never collide.
|
|
71
|
+
*/
|
|
72
|
+
export function makeRunId(date, gitSha, parentDir) {
|
|
73
|
+
// 2026-06-28T14:30:52.123Z → 2026-06-28_143052
|
|
74
|
+
const stamp = date.toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "").replace("T", "_");
|
|
75
|
+
const base = gitSha ? `${stamp}-${gitSha}` : stamp;
|
|
76
|
+
if (!parentDir)
|
|
77
|
+
return base;
|
|
78
|
+
let id = base;
|
|
79
|
+
for (let n = 2; existsSync(join(parentDir, id)); n++)
|
|
80
|
+
id = `${base}-${n}`;
|
|
81
|
+
return id;
|
|
82
|
+
}
|
|
33
83
|
//# sourceMappingURL=paths.js.map
|