evals-lab 0.10.0 → 0.11.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/CHANGELOG.md CHANGED
@@ -5,6 +5,44 @@ Newest first. Read a release's **Upgrade notes** before installing it: an
5
5
  upgrade can rewrite what the lab keeps in your data directory, and an older
6
6
  version cannot always read it back.
7
7
 
8
+ ## 0.11.0
9
+
10
+ ### Upgrade notes
11
+
12
+ - **Copy your data directory before upgrading** (see "User data" in the
13
+ README). A pipeline 0.11.0 saves is pipeline version 16, which 0.10.x
14
+ cannot read. To go back, reinstall 0.10.0 and restore the copy.
15
+ - On first start, each Target profile becomes an AI Connection (its type,
16
+ address, key and model), keeping its key and who it is shared with, plus a
17
+ Connection Profile where it changed any model or image setting. The old
18
+ profiles stay stored, unused.
19
+ - A pipeline's target names a Connection and a Profile. Pipelines read as
20
+ version 16, and are saved as it at their next edit; a run from before
21
+ shows its Target profile as its Profile.
22
+
23
+ ### Added
24
+
25
+ - Setup › Connections holds AI Connections and Connection Profiles: a
26
+ connection is where to send and with which key, and a profile is how to
27
+ ask. A target picks one of each, so one connection serves several
28
+ profiles.
29
+ - Every pipeline, prompt, run, profile and wizard has its own address, and
30
+ Copy link copies it.
31
+ - A run's Responses read as a flow, one ordered table a stage, and name the
32
+ stage and filter that discarded a reply. Move to filter set… and Move all
33
+ turn a filter into part of a filter set.
34
+ - A Call is viewed, edited, renamed and cloned in one dialog, and a
35
+ workflow or dynamic Source's settings are edited where they were made.
36
+ - The package page gives a contact address and links to its home page.
37
+
38
+ ### Fixed
39
+
40
+ - File on disk never reads `/proc`, and a Call holding the lab's own secret
41
+ is refused.
42
+ - Importing a version-15 YAML pipeline keeps each target's profile.
43
+ - A Target profile from before reply limits keeps its 160-token cap when it
44
+ becomes a connection.
45
+
8
46
  ## 0.10.0
9
47
 
10
48
  ### Upgrade notes
@@ -42,7 +80,9 @@ version cannot always read it back.
42
80
  lab warns at start when one is on while it listens beyond this machine
43
81
  without `LAB_PASSWORD`. `SOURCE_LOCAL_ROOT=<dir>` lets File on disk read only
44
82
  under that directory. A command runs without a shell, in an empty
45
- directory, without the lab's own secrets.
83
+ directory, with the lab's own secrets out of its environment -- but it
84
+ can still read them from the lab's, so turn Command on only where
85
+ everyone who can reach the lab may see them.
46
86
  - Copy or move… copies or moves a pipeline, dataset, filter set, prompt or
47
87
  Source to another workspace, and says first what a move would leave broken.
48
88
  - A run's view opens on an Evals summary, and Show details shows what came
package/README.md CHANGED
@@ -111,9 +111,9 @@ evals-lab run <bundle-dir | pipeline.yaml> [--items <dir>] [--json <file|->]
111
111
  | `--min-pass` | an eval passes when this share of its items pass |
112
112
  | `--progress` | a line per item as it finishes |
113
113
 
114
- The bundle holds no key. Each Target profile's key comes from
114
+ The bundle holds no key. Each AI Connection's key comes from
115
115
  `EVALSLAB_API_KEY_` and its slug in Setup, in capitals with hyphens as
116
- underscores: a profile slugged `anthropic` reads `EVALSLAB_API_KEY_ANTHROPIC`,
116
+ underscores: a connection slugged `anthropic` reads `EVALSLAB_API_KEY_ANTHROPIC`,
117
117
  and `gpt-4o-mini` reads `EVALSLAB_API_KEY_GPT_4O_MINI`. A table of results
118
118
  goes to stderr.
119
119
 
@@ -237,6 +237,12 @@ npm install -g evals-lab@latest # upgrade
237
237
  npm uninstall -g evals-lab # remove; your data directory stays
238
238
  ```
239
239
 
240
+ ## Contact
241
+
242
+ [evals-lab@grainbox.com](mailto:evals-lab@grainbox.com) for questions,
243
+ problems and feedback. More at
244
+ <https://www.grainbox.com/products/evals-lab/>.
245
+
240
246
  ## Licence
241
247
 
242
248
  Copyright 2026 Grainbox Limited, under the
package/bin/run.js CHANGED
@@ -62,7 +62,7 @@ const USAGE = `Usage: evals-lab run <bundle-dir | pipeline.yaml> [options]
62
62
  --progress need it.
63
63
  --help this.
64
64
 
65
- A bundle's Target profile keys come from $EVALSLAB_API_KEY_<SLUG>. Exit
65
+ A bundle's connection keys come from $EVALSLAB_API_KEY_<SLUG>. Exit
66
66
  codes: 0 pass (or queued, without --wait), 1 an eval failed, 2 incomplete (an
67
67
  item missing, one that did not run, or the run cancelled), 3 broken or
68
68
  refused.`;
@@ -392,16 +392,20 @@ async function runOnLab(core, o, { env, stdout, say }) {
392
392
  }
393
393
  const call = labApi(base, env, workspace);
394
394
 
395
- // What the page reads before it submits: the lab's pipelines and Target
396
- // profiles, the Source the pipeline reads and the eval groups it may link.
395
+ // What the page reads before it submits: the lab's pipelines, AI
396
+ // Connections and Connection Profiles, the Source the pipeline reads and
397
+ // the eval groups it may link. Slugged, as RunsTab's submit has them, so
398
+ // the run carries the slug its keys are spelt from (#253).
397
399
  const docs = (await call("GET", "/api/state")).docs ?? {};
398
- const setup = docs["promptlab.profiles"]?.body ?? {};
399
- const profiles = Array.isArray(setup.list) ? setup.list : [];
400
+ const setup = docs["promptlab.connections"]?.body ?? {};
401
+ const connections = core.withSlugs(Array.isArray(setup.list) ? setup.list : []);
402
+ const held = docs["promptlab.connectionProfiles"]?.body?.list;
403
+ const answers = core.answersOf(connections, Array.isArray(held) ? held : []);
400
404
  const saved = docs["promptlab.workflows"]?.body?.list;
401
405
  const found = pipelineNamed(Array.isArray(saved) ? saved : [], o.pipeline, base);
402
- const types = new Map(profiles.map(p => [p.id, p.type]));
403
- const doc = core.upgradePipeline(found.work, { profileType: id => types.get(id) });
404
- const g = profiles.find(p => p.id === setup.grader);
406
+ const doc = core.upgradePipeline(found.work, { profileType: id => answers.connection(id)?.type,
407
+ connectionSplit: answers.split });
408
+ const g = answers.connection(setup.grader);
405
409
  const grader = g ? { id: g.id, name: g.name } : null;
406
410
  const content = core.contentOf(doc);
407
411
  let source = null;
@@ -411,18 +415,14 @@ async function runOnLab(core, o, { env, stdout, say }) {
411
415
  const files = source ? source.files.map(f => f.name) : undefined;
412
416
  const { datasets } = await call("GET", "/api/datasets");
413
417
  const problems = core.validatePipeline(doc, {
414
- profiles: id => profiles.find(p => p.id === id) ?? null,
418
+ profiles: answers.resolve, connections: answers.connection,
415
419
  sources: id => (source && id === content.ref.id ? { name: source.name, type: source.type, files } : null),
416
420
  grader, groups: datasets ?? [],
417
421
  });
418
422
  if (problems.length) throw new Refused(`${found.name} cannot run: ${problems.join("; ")}`);
419
- // As RunsTab's submit resolves it: slugged, so the run carries the slug
420
- // its keys are spelt from (#253), and keyless -- the lab fills each key in
423
+ // As RunsTab's submit resolves it: keyless -- the lab fills each key in
421
424
  // from its own store.
422
- const slugged = core.withSlugs(profiles);
423
- const run = core.resolvePipeline(doc, {
424
- profiles: id => slugged.find(p => p.id === id) ?? null, grader, ...(files ? { files } : {}),
425
- });
425
+ const run = core.resolvePipeline(doc, { profiles: answers.resolve, grader, ...(files ? { files } : {}) });
426
426
 
427
427
  const queued = (await call("POST", "/api/queue", run)).run;
428
428
  say(`evals-lab run: ${found.name} is queued on ${base} as run ${queued.id}`);
package/lab/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.0 (2026.10.09-614)
1
+ 0.11.0 (2026.10.11-671)
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": 15,
2
+ "version": 16,
3
3
  "id": "demo-1",
4
4
  "name": "Demo 1",
5
5
  "jobs": [
@@ -105,10 +105,11 @@
105
105
  {
106
106
  "id": "s1",
107
107
  "name": "",
108
- "profile": {
108
+ "connection": {
109
109
  "id": "vision",
110
110
  "name": "Vision"
111
111
  },
112
+ "profile": null,
112
113
  "steps": [
113
114
  {
114
115
  "type": "prompt",
@@ -119,10 +120,11 @@
119
120
  {
120
121
  "id": "s2",
121
122
  "name": "",
122
- "profile": {
123
+ "connection": {
123
124
  "id": "vision",
124
125
  "name": "Vision"
125
126
  },
127
+ "profile": null,
126
128
  "steps": [
127
129
  {
128
130
  "type": "prompt",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": 15,
2
+ "version": 16,
3
3
  "id": "demo-2",
4
4
  "name": "Demo 2",
5
5
  "jobs": [
@@ -105,10 +105,11 @@
105
105
  {
106
106
  "id": "s1",
107
107
  "name": "",
108
- "profile": {
108
+ "connection": {
109
109
  "id": "vision",
110
110
  "name": "Vision"
111
111
  },
112
+ "profile": null,
112
113
  "steps": [
113
114
  {
114
115
  "type": "prompt",
@@ -119,10 +120,11 @@
119
120
  {
120
121
  "id": "s2",
121
122
  "name": "",
122
- "profile": {
123
+ "connection": {
123
124
  "id": "vision",
124
125
  "name": "Vision"
125
126
  },
127
+ "profile": null,
126
128
  "steps": [
127
129
  {
128
130
  "type": "prompt",