lastlight-evals 0.1.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 +187 -0
- package/datasets/code-fix/instances.json +26 -0
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/README.md +8 -0
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/package.json +9 -0
- package/datasets/code-fix/repos/codefix__date-range-off-by-one/src/date-range.ts +25 -0
- package/datasets/code-fix/tests/codefix__date-range-off-by-one/date-range.test.ts +18 -0
- package/datasets/code-fix/tier.json +5 -0
- package/datasets/triage/instances.json +62 -0
- package/datasets/triage/tier.json +5 -0
- package/dist/bootstrap.js +46 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/discovery.js +100 -0
- package/dist/discovery.js.map +1 -0
- package/dist/env.js +109 -0
- package/dist/env.js.map +1 -0
- package/dist/fake-github.js +249 -0
- package/dist/fake-github.js.map +1 -0
- package/dist/grade.js +134 -0
- package/dist/grade.js.map +1 -0
- package/dist/html-report.js +325 -0
- package/dist/html-report.js.map +1 -0
- package/dist/init.js +122 -0
- package/dist/init.js.map +1 -0
- package/dist/mechanism.test.js +108 -0
- package/dist/mechanism.test.js.map +1 -0
- package/dist/metrics.js +86 -0
- package/dist/metrics.js.map +1 -0
- package/dist/paths.js +33 -0
- package/dist/paths.js.map +1 -0
- package/dist/report.js +137 -0
- package/dist/report.js.map +1 -0
- package/dist/run-instance.js +203 -0
- package/dist/run-instance.js.map +1 -0
- package/dist/run.js +476 -0
- package/dist/run.js.map +1 -0
- package/dist/schema.js +15 -0
- package/dist/schema.js.map +1 -0
- package/dist/seed.js +49 -0
- package/dist/seed.js.map +1 -0
- package/models.json +17 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# lastlight-evals
|
|
2
|
+
|
|
3
|
+
A standalone, **SWE-bench-compatible** eval harness for [Last
|
|
4
|
+
Light](https://github.com/cliftonc/lastlight) workflows. It drives the **real**
|
|
5
|
+
production workflows (`issue-triage`, `build`, …) — their actual prompts and
|
|
6
|
+
skills — against a **mocked GitHub**, grades the result deterministically, and
|
|
7
|
+
prints a model-comparison scorecard. It answers "what do we expect from the
|
|
8
|
+
agent, and which model does it best?"
|
|
9
|
+
|
|
10
|
+
Nothing here talks to real GitHub. The agent's `github_*` tool calls are served
|
|
11
|
+
by an in-process fake (seeded + recording), and `git push` goes to a local bare
|
|
12
|
+
repo. The only deviations from production are the two we can't do unattended:
|
|
13
|
+
approval gates are disabled and outward side-effects are mocked.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
instance (SWE-bench shape)
|
|
17
|
+
│
|
|
18
|
+
├─ start fake GitHub (seeded with the issue, records every mutation)
|
|
19
|
+
├─ (code-fix) seed workspace: fixture repo @ base_commit + local bare origin
|
|
20
|
+
├─ load the REAL workflow YAML (issue-triage / build / …) from lastlight core
|
|
21
|
+
├─ runWorkflow(sandbox:"none", githubApiBaseUrl→fake, approvalConfig:{})
|
|
22
|
+
└─ grade:
|
|
23
|
+
• execution — apply held-out tests, run them → FAIL_TO_PASS / PASS_TO_PASS
|
|
24
|
+
• behavioral — recorded GitHub calls vs the instance's expectations
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> Working on the harness itself? See `CLAUDE.md` for the seams and invariants
|
|
28
|
+
> (the base-URL mock, static-token mode, the no-clone seeding trick, the
|
|
29
|
+
> asset-bootstrap footgun, the metrics drain).
|
|
30
|
+
|
|
31
|
+
## How it depends on Last Light
|
|
32
|
+
|
|
33
|
+
`lastlight-evals` is a thin CLI on top of the `lastlight` npm package. It imports
|
|
34
|
+
exactly four things from core's public `lastlight/evals` barrel —
|
|
35
|
+
`getWorkflow`, `runWorkflow`, `ExecutorConfig`, `TemplateContext` — plus the
|
|
36
|
+
`gh`-repo bootstrap helpers used by `init`. Core ships its `workflows/`,
|
|
37
|
+
`skills/`, and `agent-context/` in the package, so the evals run the same assets
|
|
38
|
+
core does.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install # installs `lastlight` (and agentic-pi as a peer)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Local development against an un-published core.** Until the matching
|
|
45
|
+
`lastlight` version is on npm — or whenever you want to eval your working-tree
|
|
46
|
+
core — link a checkout:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cd ../lastlight && npm run build && npm link
|
|
50
|
+
cd ../lastlight-evals && npm link lastlight
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or set `LASTLIGHT_CORE_DIR=/path/to/lastlight` to point just the **asset roots**
|
|
54
|
+
(workflows/skills/agent-context — the bulk of what `lastlight server update`
|
|
55
|
+
ships) at a checkout without touching the npm dep. (The runner *code* still
|
|
56
|
+
comes from `node_modules/lastlight`; use `npm link` to exercise working-tree
|
|
57
|
+
engine code too.)
|
|
58
|
+
|
|
59
|
+
## Run it
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# no tier args → interactively pick which tiers to run (one or all).
|
|
63
|
+
# Non-interactive (CI / piped) falls back to the cheapest default.
|
|
64
|
+
lastlight-evals run # (or: npm run eval)
|
|
65
|
+
|
|
66
|
+
# name tiers explicitly to skip the prompt
|
|
67
|
+
lastlight-evals run triage
|
|
68
|
+
lastlight-evals run code-fix # the full build cycle (heavy)
|
|
69
|
+
lastlight-evals run triage code-fix # both → combined tabbed report
|
|
70
|
+
|
|
71
|
+
# cross-vendor comparison (OpenAI + Anthropic + open source) — see models.json.
|
|
72
|
+
# Families run in PARALLEL; serial within a family. Force serial with --serial.
|
|
73
|
+
lastlight-evals run --compare
|
|
74
|
+
|
|
75
|
+
# pick ONE model (fuzzy-matched against models.json id/label)
|
|
76
|
+
lastlight-evals run triage --model haiku
|
|
77
|
+
lastlight-evals run triage --model glm,deepseek # a comma-list also works
|
|
78
|
+
|
|
79
|
+
# repeat each case N times; verdicts WORST-case, cost/tokens/latency MEAN
|
|
80
|
+
lastlight-evals run triage --runs 3
|
|
81
|
+
|
|
82
|
+
# run against an overlay repo's OWN workflows + datasets (see below)
|
|
83
|
+
lastlight-evals run --overlay ~/work/lastlight-instance
|
|
84
|
+
|
|
85
|
+
# add your own datasets dir without an overlay
|
|
86
|
+
lastlight-evals run --datasets ~/my-evals/datasets
|
|
87
|
+
|
|
88
|
+
# ad-hoc model set / focus one instance / no browser
|
|
89
|
+
EVAL_MODELS="openai/gpt-5.5,anthropic/claude-sonnet-4-6" lastlight-evals run
|
|
90
|
+
EVAL_INSTANCE=off-by-one lastlight-evals run code-fix
|
|
91
|
+
lastlight-evals run triage --no-open
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The runner opens `index.html` and **rewrites it after every run** (auto-refresh,
|
|
95
|
+
preserving the active tab + scroll), so you watch the scorecard fill in live.
|
|
96
|
+
Output lands under `./eval-results/<tiers>/` (override with `LASTLIGHT_EVALS_OUT`):
|
|
97
|
+
|
|
98
|
+
- `index.html` — styled scorecard.
|
|
99
|
+
- `scorecard.json` — structured roll-up per model.
|
|
100
|
+
- `predictions.jsonl` — SWE-bench predictions shape.
|
|
101
|
+
|
|
102
|
+
Needs a provider key (`OPENAI_API_KEY` / `ANTHROPIC_API_KEY` /
|
|
103
|
+
`FIREWORKS_API_KEY` / `OPENROUTER_API_KEY`) in the environment or a cwd `.env`.
|
|
104
|
+
The runner exits non-zero **only** if the harness itself errors — a weak model
|
|
105
|
+
scoring poorly is the measurement, not a build failure.
|
|
106
|
+
|
|
107
|
+
## Your own workflows + datasets (overlays)
|
|
108
|
+
|
|
109
|
+
An **overlay** is a directory (often its own repo, like `lastlight-instance`)
|
|
110
|
+
that carries its own `workflows/` / `skills/` / `agent-context/` (which shadow
|
|
111
|
+
the core built-ins by name) and its own `evals/datasets/`. One flag wires both:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
lastlight-evals run --overlay ~/work/lastlight-instance # or LASTLIGHT_OVERLAY_DIR
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- Overlay **workflows/skills** are layered over core via core's asset overlay
|
|
118
|
+
(same mechanism the production harness uses).
|
|
119
|
+
- Overlay **datasets** are discovered at `<overlay>/evals/datasets/<tier>/`, and
|
|
120
|
+
shadow built-in tiers of the same name.
|
|
121
|
+
- An overlay **`evals/models.json`** is picked up automatically (or pass
|
|
122
|
+
`--models-file`).
|
|
123
|
+
|
|
124
|
+
### `lastlight-evals init [dir]` — scaffold a fresh overlay+evals repo
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
lastlight-evals init my-evals
|
|
128
|
+
cd my-evals && lastlight-evals run --overlay .
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Scaffolds `workflows/` `skills/` `agent-context/` (empty, to fill in),
|
|
132
|
+
`evals/datasets/` + `evals/models.json` (seeded from the shipped samples),
|
|
133
|
+
`config.yaml`, and a `.gitignore`/`README`, then offers to `git init` + create a
|
|
134
|
+
private GitHub repo via `gh` (reusing core's `lastlight server setup` flow).
|
|
135
|
+
|
|
136
|
+
## Datasets & tiers
|
|
137
|
+
|
|
138
|
+
A **tier** is a directory containing `instances.json` (+ an optional `tier.json`
|
|
139
|
+
declaring its `defaultWorkflow`). Tiers are discovered from three roots, merged
|
|
140
|
+
by name with **overlay > user (`--datasets`) > built-in** precedence:
|
|
141
|
+
|
|
142
|
+
- **built-in** (shipped here): `triage` → `issue-triage`, `code-fix` → `build`.
|
|
143
|
+
- **user**: `--datasets <dir>` / `LASTLIGHT_EVALS_DATASETS`.
|
|
144
|
+
- **overlay**: `<overlay>/evals/datasets/*`.
|
|
145
|
+
|
|
146
|
+
### Add a case
|
|
147
|
+
|
|
148
|
+
**Triage** — append to a tier's `instances.json`:
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"instance_id": "triage__my-case",
|
|
153
|
+
"repo": "lastlight-evals/widget",
|
|
154
|
+
"workflow": "issue-triage",
|
|
155
|
+
"problem_statement": "short title",
|
|
156
|
+
"issue": { "number": 110, "title": "…", "body": "…", "labels": [] },
|
|
157
|
+
"triage_gold": { "category": "bug", "state": "ready-for-agent" },
|
|
158
|
+
"expect_github": { "labels_added": ["bug"] }
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Code-fix** — three things keyed by `instance_id`, all under the tier dir:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
<tier>/instances.json # the SweBenchInstance (FAIL_TO_PASS / PASS_TO_PASS)
|
|
166
|
+
<tier>/repos/<id>/ # fixture repo at base_commit (NO held-out tests)
|
|
167
|
+
<tier>/tests/<id>/ # held-out test files, copied in at grade time
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
A new tier just needs a directory with an `instances.json` and a `tier.json`
|
|
171
|
+
(`{ "name", "defaultWorkflow", "description" }`); per-instance `workflow` wins
|
|
172
|
+
when present.
|
|
173
|
+
|
|
174
|
+
## Models (`models.json`)
|
|
175
|
+
|
|
176
|
+
- `default` — the single model `run` uses.
|
|
177
|
+
- `compare` — the cross-vendor set `--compare` fans out over. Each entry has an
|
|
178
|
+
`id` (the agentic-pi/pi-ai `provider/model` spec), a `label`, and an `envKey`.
|
|
179
|
+
**An entry only runs if its `envKey` is present**, so the compare set
|
|
180
|
+
auto-trims to whatever keys you have.
|
|
181
|
+
|
|
182
|
+
## Roadmap
|
|
183
|
+
|
|
184
|
+
- **`lastlight-evals extract <owner>/<repo>#<n>`** — generate eval cases from
|
|
185
|
+
GitHub historical issues/PRs (issue → fixture, merged PR → held-out tests).
|
|
186
|
+
- Docker-backed runs; real SWE-bench Lite ingestion; per-fixture test runners.
|
|
187
|
+
- LLM-as-judge stays out by design — grading is deterministic.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"instance_id": "codefix__date-range-off-by-one",
|
|
4
|
+
"repo": "lastlight-evals/daterange",
|
|
5
|
+
"workflow": "build",
|
|
6
|
+
"base_commit": "0000000000000000000000000000000000000000",
|
|
7
|
+
"problem_statement": "inclusiveDayCount is off by one\n\n`inclusiveDayCount(start, end)` should return the number of whole days in the INCLUSIVE range [start, end], but it returns one too few.\n\nExample:\n- `inclusiveDayCount(2026-01-01, 2026-01-01)` returns 0, should be 1.\n- `inclusiveDayCount(2026-01-01, 2026-01-03)` returns 2, should be 3.\n\nThe fix is in `src/date-range.ts`. `eachDay` already enumerates the inclusive range correctly, so don't change it.",
|
|
8
|
+
"issue": {
|
|
9
|
+
"number": 201,
|
|
10
|
+
"title": "inclusiveDayCount is off by one",
|
|
11
|
+
"body": "`inclusiveDayCount(start, end)` should count days in the INCLUSIVE range but returns one too few. `inclusiveDayCount(2026-01-01, 2026-01-01)` returns 0 (should be 1). The bug is in `src/date-range.ts`.",
|
|
12
|
+
"labels": ["bug", "ready-for-agent"],
|
|
13
|
+
"user": "reporter"
|
|
14
|
+
},
|
|
15
|
+
"FAIL_TO_PASS": [
|
|
16
|
+
"inclusive single day counts as one",
|
|
17
|
+
"inclusive three day span counts as three"
|
|
18
|
+
],
|
|
19
|
+
"PASS_TO_PASS": [
|
|
20
|
+
"eachDay still lists every inclusive day"
|
|
21
|
+
],
|
|
22
|
+
"expect_github": {
|
|
23
|
+
"pr_opened": { "base": "main", "head_is_branch": true }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Date-range helpers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Number of whole days covered by the INCLUSIVE range [start, end].
|
|
9
|
+
*
|
|
10
|
+
* e.g. 2026-01-01 .. 2026-01-01 covers 1 day; 2026-01-01 .. 2026-01-03 covers 3.
|
|
11
|
+
*/
|
|
12
|
+
export function inclusiveDayCount(start: Date, end: Date): number {
|
|
13
|
+
const ms = end.getTime() - start.getTime();
|
|
14
|
+
// BUG: drops the inclusive end day — should add 1.
|
|
15
|
+
return Math.round(ms / MS_PER_DAY);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** All YYYY-MM-DD dates in the inclusive range, in order. */
|
|
19
|
+
export function eachDay(start: Date, end: Date): string[] {
|
|
20
|
+
const out: string[] = [];
|
|
21
|
+
for (let t = start.getTime(); t <= end.getTime(); t += MS_PER_DAY) {
|
|
22
|
+
out.push(new Date(t).toISOString().slice(0, 10));
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { inclusiveDayCount, eachDay } from "./src/date-range.ts";
|
|
5
|
+
|
|
6
|
+
const d = (s: string): Date => new Date(`${s}T00:00:00Z`);
|
|
7
|
+
|
|
8
|
+
test("inclusive single day counts as one", () => {
|
|
9
|
+
assert.equal(inclusiveDayCount(d("2026-01-01"), d("2026-01-01")), 1);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("inclusive three day span counts as three", () => {
|
|
13
|
+
assert.equal(inclusiveDayCount(d("2026-01-01"), d("2026-01-03")), 3);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("eachDay still lists every inclusive day", () => {
|
|
17
|
+
assert.deepEqual(eachDay(d("2026-01-01"), d("2026-01-03")), ["2026-01-01", "2026-01-02", "2026-01-03"]);
|
|
18
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"instance_id": "triage__bug-with-repro-ready",
|
|
4
|
+
"repo": "lastlight-evals/widget",
|
|
5
|
+
"workflow": "issue-triage",
|
|
6
|
+
"problem_statement": "Crash on empty config file",
|
|
7
|
+
"issue": {
|
|
8
|
+
"number": 101,
|
|
9
|
+
"title": "Crash on empty config file",
|
|
10
|
+
"body": "## Steps to reproduce\n1. Create an empty `config.yaml`\n2. Run `widget start`\n\n## Expected\nIt should start with defaults.\n\n## Actual\nIt throws `TypeError: Cannot read properties of undefined (reading 'port')` at `src/config.ts:42`.\n\nEnvironment: widget v2.1.0, Node 20. This reproduces 100% of the time.",
|
|
11
|
+
"labels": [],
|
|
12
|
+
"user": "alice"
|
|
13
|
+
},
|
|
14
|
+
"triage_gold": { "category": "bug", "state": "ready-for-agent" },
|
|
15
|
+
"expect_github": { "labels_added": ["bug"] }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"instance_id": "triage__bug-no-repro-needs-info",
|
|
19
|
+
"repo": "lastlight-evals/widget",
|
|
20
|
+
"workflow": "issue-triage",
|
|
21
|
+
"problem_statement": "It's broken",
|
|
22
|
+
"issue": {
|
|
23
|
+
"number": 102,
|
|
24
|
+
"title": "It doesn't work",
|
|
25
|
+
"body": "I tried to use the thing and it didn't work. Please fix.",
|
|
26
|
+
"labels": [],
|
|
27
|
+
"user": "bob"
|
|
28
|
+
},
|
|
29
|
+
"triage_gold": { "state": "needs-info" },
|
|
30
|
+
"expect_github": { "labels_added": ["needs-info"], "labels_absent": ["ready-for-agent"], "comment_matches": "need|provide|repro|version|steps" }
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"instance_id": "triage__feature-well-specified",
|
|
34
|
+
"repo": "lastlight-evals/widget",
|
|
35
|
+
"workflow": "issue-triage",
|
|
36
|
+
"problem_statement": "Add --json flag to widget status",
|
|
37
|
+
"issue": {
|
|
38
|
+
"number": 103,
|
|
39
|
+
"title": "Add a --json output flag to `widget status`",
|
|
40
|
+
"body": "## Use case\nWe script around `widget status` in CI and parsing the human table is brittle.\n\n## Proposal\nAdd a `--json` flag that prints the same status as a JSON object: `{ \"state\": \"running\", \"uptimeSec\": 1234 }`.\n\nThe table output stays the default. This is additive and well-scoped — the status data already exists in `src/status.ts`.",
|
|
41
|
+
"labels": [],
|
|
42
|
+
"user": "carol"
|
|
43
|
+
},
|
|
44
|
+
"triage_gold": { "category": "enhancement" },
|
|
45
|
+
"expect_github": { "labels_added": ["enhancement"] }
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"instance_id": "triage__pure-question",
|
|
49
|
+
"repo": "lastlight-evals/widget",
|
|
50
|
+
"workflow": "issue-triage",
|
|
51
|
+
"problem_statement": "How do I change the port?",
|
|
52
|
+
"issue": {
|
|
53
|
+
"number": 104,
|
|
54
|
+
"title": "How do I change the listening port?",
|
|
55
|
+
"body": "Quick question — what's the right way to change the port widget listens on? Is it an env var or a config key? Not a bug, just want to understand the options.",
|
|
56
|
+
"labels": [],
|
|
57
|
+
"user": "dave"
|
|
58
|
+
},
|
|
59
|
+
"triage_gold": { "category": "question" },
|
|
60
|
+
"expect_github": { "labels_added": ["question"], "labels_absent": ["ready-for-agent", "ready-for-human"] }
|
|
61
|
+
}
|
|
62
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core-asset bootstrap — the one thing an out-of-process eval harness MUST do
|
|
3
|
+
* that the in-repo version never had to.
|
|
4
|
+
*
|
|
5
|
+
* Last Light's `getWorkflow` resolves built-in workflows/skills/agent-context
|
|
6
|
+
* from `DEFAULT_ROOT = resolve(".")` (the process cwd). In-repo that happened to
|
|
7
|
+
* be the core checkout, so it "just worked". As a separate package our cwd is
|
|
8
|
+
* wherever the user invoked the CLI, so we MUST tell core where its assets live
|
|
9
|
+
* by calling `configureWorkflowAssets({ builtInRoot })` BEFORE any
|
|
10
|
+
* `getWorkflow`/`runWorkflow`. Forget this and workflows silently fail to
|
|
11
|
+
* resolve. {@link bootstrapAssets} is therefore the first call in `run`.
|
|
12
|
+
*/
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
import { configureWorkflowAssets } from "lastlight/evals";
|
|
16
|
+
/**
|
|
17
|
+
* The lastlight PACKAGE ROOT — the dir holding `workflows/`, `skills/`,
|
|
18
|
+
* `agent-context/`, `config/`, `dist/`.
|
|
19
|
+
*
|
|
20
|
+
* Default: resolve the installed `lastlight` package (a normal npm dependency).
|
|
21
|
+
*
|
|
22
|
+
* Override: `LASTLIGHT_CORE_DIR` repoints the ASSET roots at a local core
|
|
23
|
+
* checkout, so you can eval un-published workflow/prompt/skill edits — the bulk
|
|
24
|
+
* of what `lastlight server update` ships — without bumping the npm dep. Caveat:
|
|
25
|
+
* the imported runner CODE still comes from `node_modules/lastlight`; to also
|
|
26
|
+
* exercise working-tree engine code, `npm link lastlight` (or a `file:` dep).
|
|
27
|
+
*/
|
|
28
|
+
export function resolveCoreRoot() {
|
|
29
|
+
const override = process.env.LASTLIGHT_CORE_DIR?.trim();
|
|
30
|
+
if (override)
|
|
31
|
+
return override;
|
|
32
|
+
const require = createRequire(import.meta.url);
|
|
33
|
+
return dirname(require.resolve("lastlight/package.json"));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Point core's asset layers at the resolved core root (+ optional overlay).
|
|
37
|
+
* MUST run before the first workflow access. An overlay's workflows/skills/
|
|
38
|
+
* agent-context shadow the built-ins by logical name — the same precedence the
|
|
39
|
+
* production harness uses via `LASTLIGHT_OVERLAY_DIR`.
|
|
40
|
+
*/
|
|
41
|
+
export function bootstrapAssets(opts = {}) {
|
|
42
|
+
const builtInRoot = resolveCoreRoot();
|
|
43
|
+
configureWorkflowAssets({ builtInRoot, overlayRoot: opts.overlayDir });
|
|
44
|
+
return { builtInRoot, overlayDir: opts.overlayDir };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACxD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC;AAC5D,CAAC;AAOD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgC,EAAE;IAChE,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IACtC,uBAAuB,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IACvE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier/dataset discovery — replaces the old hardcoded `TIERS` map.
|
|
3
|
+
*
|
|
4
|
+
* A **tier** is simply a directory that contains an `instances.json`
|
|
5
|
+
* (alongside, for code-fix tiers, `repos/<id>/` fixtures and `tests/<id>/`
|
|
6
|
+
* held-out tests). Tiers are discovered from up to three roots and merged by
|
|
7
|
+
* name with **overlay > user > built-in** precedence — the same shadow-by-name
|
|
8
|
+
* model core uses for workflow assets, so a deployment can override a shipped
|
|
9
|
+
* tier or add entirely new ones without touching this package.
|
|
10
|
+
*
|
|
11
|
+
* 1. built-in — `<pkg>/datasets/*` (the shipped "our" samples)
|
|
12
|
+
* 2. user — `--datasets <dir>` / `LASTLIGHT_EVALS_DATASETS`
|
|
13
|
+
* 3. overlay — `<overlayDir>/evals/datasets/*` (a bootstrapped repo)
|
|
14
|
+
*
|
|
15
|
+
* Which workflow a tier runs is resolved per-instance first (the existing,
|
|
16
|
+
* unchanged `instance.workflow` field), then from an optional per-tier
|
|
17
|
+
* `tier.json` (`{ name?, defaultWorkflow, description? }`). Shipping a
|
|
18
|
+
* `tier.json` next to the two built-in datasets turns the former hardcoded map
|
|
19
|
+
* into data with zero changes to the instance files.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
22
|
+
import { join, resolve } from "node:path";
|
|
23
|
+
/** Scan one root for immediate subdirs that hold an `instances.json`. */
|
|
24
|
+
function scanRoot(root, source) {
|
|
25
|
+
if (!root || !existsSync(root))
|
|
26
|
+
return [];
|
|
27
|
+
const out = [];
|
|
28
|
+
for (const entry of readdirSync(root)) {
|
|
29
|
+
const dir = join(root, entry);
|
|
30
|
+
let isDir = false;
|
|
31
|
+
try {
|
|
32
|
+
isDir = statSync(dir).isDirectory();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (!isDir)
|
|
38
|
+
continue;
|
|
39
|
+
const instancesPath = join(dir, "instances.json");
|
|
40
|
+
if (!existsSync(instancesPath))
|
|
41
|
+
continue;
|
|
42
|
+
let manifest = {};
|
|
43
|
+
const manifestPath = join(dir, "tier.json");
|
|
44
|
+
if (existsSync(manifestPath)) {
|
|
45
|
+
try {
|
|
46
|
+
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* a malformed tier.json just means no defaultWorkflow — not fatal */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
out.push({
|
|
53
|
+
name: manifest.name || entry,
|
|
54
|
+
source,
|
|
55
|
+
root: dir,
|
|
56
|
+
instancesPath,
|
|
57
|
+
defaultWorkflow: manifest.defaultWorkflow,
|
|
58
|
+
description: manifest.description,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Discover all tiers across the roots, overlay-wins by name. Insertion order
|
|
65
|
+
* (built-in first) is preserved for stable display; later sources overwrite the
|
|
66
|
+
* map value for a shared name.
|
|
67
|
+
*/
|
|
68
|
+
export function discoverTiers(opts) {
|
|
69
|
+
const tiers = new Map();
|
|
70
|
+
const add = (list) => {
|
|
71
|
+
for (const t of list)
|
|
72
|
+
tiers.set(t.name, t);
|
|
73
|
+
};
|
|
74
|
+
add(scanRoot(opts.builtinRoot, "builtin"));
|
|
75
|
+
if (opts.userDatasetsDir)
|
|
76
|
+
add(scanRoot(resolve(opts.userDatasetsDir), "user"));
|
|
77
|
+
if (opts.overlayDir)
|
|
78
|
+
add(scanRoot(join(resolve(opts.overlayDir), "evals", "datasets"), "overlay"));
|
|
79
|
+
return tiers;
|
|
80
|
+
}
|
|
81
|
+
/** Load a tier's instances, with the optional `EVAL_INSTANCE` substring filter. */
|
|
82
|
+
export function loadInstances(tier) {
|
|
83
|
+
const all = JSON.parse(readFileSync(tier.instancesPath, "utf8"));
|
|
84
|
+
const filter = process.env.EVAL_INSTANCE?.trim();
|
|
85
|
+
return filter ? all.filter((i) => i.instance_id.includes(filter)) : all;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve the workflow to run for one instance: explicit `instance.workflow`
|
|
89
|
+
* wins, else the tier's `defaultWorkflow`. Throws if neither is set — a loud
|
|
90
|
+
* misconfiguration rather than a silent wrong default.
|
|
91
|
+
*/
|
|
92
|
+
export function workflowFor(tier, inst) {
|
|
93
|
+
const wf = inst.workflow ?? tier.defaultWorkflow;
|
|
94
|
+
if (!wf) {
|
|
95
|
+
throw new Error(`Tier "${tier.name}": instance "${inst.instance_id}" has no \`workflow\` and the ` +
|
|
96
|
+
`tier has no \`defaultWorkflow\` (add one to ${join(tier.root, "tier.json")}).`);
|
|
97
|
+
}
|
|
98
|
+
return wf;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal `.env` loader for the eval harness (no dotenv dependency).
|
|
3
|
+
*
|
|
4
|
+
* Reads the repo-root `.env` (KEY=VALUE lines) and sets any keys not already
|
|
5
|
+
* present in `process.env`. The provider key (OPENAI_API_KEY / ANTHROPIC_…)
|
|
6
|
+
* lives there for local dev; the eval needs it to make real model calls.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
9
|
+
import { join, resolve } from "node:path";
|
|
10
|
+
import { builtinModelsPath } from "./paths.js";
|
|
11
|
+
let loaded = false;
|
|
12
|
+
/**
|
|
13
|
+
* Active model-registry path. Defaults to the shipped `<pkg>/models.json`; a
|
|
14
|
+
* user/overlay can point it elsewhere via {@link setModelsPath} (run.ts wires
|
|
15
|
+
* `--models-file` / `<overlay>/evals/models.json`) so a deployment can ship its
|
|
16
|
+
* own model set the same way it ships its own datasets.
|
|
17
|
+
*/
|
|
18
|
+
let modelsPath = builtinModelsPath();
|
|
19
|
+
/** Override the model-registry path (call once at CLI startup, before reads). */
|
|
20
|
+
export function setModelsPath(path) {
|
|
21
|
+
modelsPath = resolve(path);
|
|
22
|
+
}
|
|
23
|
+
export function loadDotEnv(root = process.cwd()) {
|
|
24
|
+
if (loaded)
|
|
25
|
+
return;
|
|
26
|
+
loaded = true;
|
|
27
|
+
const path = join(root, ".env");
|
|
28
|
+
if (!existsSync(path))
|
|
29
|
+
return;
|
|
30
|
+
for (const raw of readFileSync(path, "utf8").split("\n")) {
|
|
31
|
+
const line = raw.trim();
|
|
32
|
+
if (!line || line.startsWith("#"))
|
|
33
|
+
continue;
|
|
34
|
+
const eq = line.indexOf("=");
|
|
35
|
+
if (eq <= 0)
|
|
36
|
+
continue;
|
|
37
|
+
const key = line.slice(0, eq).trim();
|
|
38
|
+
let val = line.slice(eq + 1).trim();
|
|
39
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
40
|
+
val = val.slice(1, -1);
|
|
41
|
+
}
|
|
42
|
+
if (process.env[key] === undefined)
|
|
43
|
+
process.env[key] = val;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** True if at least one provider key the eval can use is set. */
|
|
47
|
+
export function hasProviderKey() {
|
|
48
|
+
return Boolean(process.env.OPENAI_API_KEY ||
|
|
49
|
+
process.env.ANTHROPIC_API_KEY ||
|
|
50
|
+
process.env.OPENROUTER_API_KEY ||
|
|
51
|
+
process.env.FIREWORKS_API_KEY ||
|
|
52
|
+
process.env.DEEPSEEK_API_KEY);
|
|
53
|
+
}
|
|
54
|
+
function modelsConfig() {
|
|
55
|
+
return JSON.parse(readFileSync(modelsPath, "utf8"));
|
|
56
|
+
}
|
|
57
|
+
/** Single-model run: EVAL_MODELS override, else the `default` from models.json. */
|
|
58
|
+
export function evalModels() {
|
|
59
|
+
const raw = process.env.EVAL_MODELS?.trim();
|
|
60
|
+
if (raw)
|
|
61
|
+
return raw.split(",").map((m) => m.trim()).filter(Boolean);
|
|
62
|
+
return [modelsConfig().default];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Cross-vendor comparison set from `models.json`, filtered to the models whose
|
|
66
|
+
* provider key is actually present — so `npm run eval:compare` runs whatever
|
|
67
|
+
* you have keys for and silently skips the rest.
|
|
68
|
+
*/
|
|
69
|
+
export function compareModels() {
|
|
70
|
+
return modelsConfig().compare.filter((m) => !m.envKey || Boolean(process.env[m.envKey]));
|
|
71
|
+
}
|
|
72
|
+
/** id → display label, from models.json (for the scorecard). */
|
|
73
|
+
export function modelLabels() {
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const m of modelsConfig().compare)
|
|
76
|
+
if (m.label)
|
|
77
|
+
out[m.id] = m.label;
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/** Provider family (env-key) inferred from a `provider/model` id prefix. */
|
|
81
|
+
export function familyForId(id) {
|
|
82
|
+
const provider = id.split("/")[0]?.toLowerCase() ?? "";
|
|
83
|
+
const map = {
|
|
84
|
+
openai: "OPENAI_API_KEY",
|
|
85
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
86
|
+
fireworks: "FIREWORKS_API_KEY",
|
|
87
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
88
|
+
deepseek: "DEEPSEEK_API_KEY",
|
|
89
|
+
};
|
|
90
|
+
return map[provider] ?? "default";
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve a user-supplied model token (from `--model`) to a concrete entry.
|
|
94
|
+
* Matches a models.json `compare` id exactly, else a case-insensitive substring
|
|
95
|
+
* of its id or label (so `--model haiku` works); failing that, treats the token
|
|
96
|
+
* as a raw `provider/model` id and infers the family from its prefix. Not
|
|
97
|
+
* key-gated — `--model` is an explicit request, so we run it even if the key
|
|
98
|
+
* check would otherwise skip it (the provider call surfaces a missing key).
|
|
99
|
+
*/
|
|
100
|
+
export function resolveModel(token) {
|
|
101
|
+
const all = modelsConfig().compare;
|
|
102
|
+
const v = token.toLowerCase();
|
|
103
|
+
const hit = all.find((m) => m.id === token) ??
|
|
104
|
+
all.find((m) => m.id.toLowerCase().includes(v) || m.label?.toLowerCase().includes(v));
|
|
105
|
+
if (hit)
|
|
106
|
+
return { ...hit, family: hit.envKey ?? familyForId(hit.id) };
|
|
107
|
+
return { id: token, family: familyForId(token) };
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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"}
|