evals-lab 0.10.0 → 0.12.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +88 -1
  2. package/README.md +12 -6
  3. package/bin/evals-lab.js +9 -1
  4. package/bin/lab-file.js +97 -0
  5. package/bin/run.js +16 -16
  6. package/lab/VERSION +1 -1
  7. package/lab/demo/CREDITS.md +37 -3
  8. package/lab/demo/datasets/demo-1.json +2 -2
  9. package/lab/demo/datasets/demo-2.json +2 -2
  10. package/lab/demo/datasets/sentiment.json +383 -0
  11. package/lab/demo/datasets/weather-alerts.json +61 -0
  12. package/lab/demo/datasets/wordings.json +39 -0
  13. package/lab/demo/manifest.json +3 -3
  14. package/lab/demo/pipelines/demo-1.json +8 -6
  15. package/lab/demo/pipelines/demo-2.json +8 -6
  16. package/lab/demo/pipelines/sentiment.json +65 -0
  17. package/lab/demo/pipelines/weather-alerts.json +66 -0
  18. package/lab/demo/pipelines/wordings.json +104 -0
  19. package/lab/demo/sources/Demo 1/source.json +3 -0
  20. package/lab/demo/sources/Demo 2/source.json +3 -0
  21. package/lab/demo/sources/sentiment/bakery-croissant.txt +1 -0
  22. package/lab/demo/sources/sentiment/bus-timetable.txt +1 -0
  23. package/lab/demo/sources/sentiment/cafe-coffee.txt +1 -0
  24. package/lab/demo/sources/sentiment/concert-night.txt +1 -0
  25. package/lab/demo/sources/sentiment/film-sequel.txt +1 -0
  26. package/lab/demo/sources/sentiment/garden-tomatoes.txt +1 -0
  27. package/lab/demo/sources/sentiment/gym-contract.txt +1 -0
  28. package/lab/demo/sources/sentiment/headphones-review.txt +1 -0
  29. package/lab/demo/sources/sentiment/hotel-room.txt +1 -0
  30. package/lab/demo/sources/sentiment/library-hours.txt +1 -0
  31. package/lab/demo/sources/sentiment/meeting-minutes.txt +1 -0
  32. package/lab/demo/sources/sentiment/parcel-lost.txt +1 -0
  33. package/lab/demo/sources/sentiment/phone-update.txt +1 -0
  34. package/lab/demo/sources/sentiment/puppy-home.txt +1 -0
  35. package/lab/demo/sources/sentiment/recipe-note.txt +1 -0
  36. package/lab/demo/sources/sentiment/road-closure.txt +1 -0
  37. package/lab/demo/sources/sentiment/source.json +3 -0
  38. package/lab/demo/sources/sentiment/teacher-thanks.txt +1 -0
  39. package/lab/demo/sources/sentiment/train-delay.txt +1 -0
  40. package/lab/demo/sources/sentiment/volunteer-day.txt +1 -0
  41. package/lab/demo/sources/sentiment/weather-report.txt +1 -0
  42. package/lab/demo/sources/weather-alerts/https-api.weather.gov-alerts-types.txt +116 -0
  43. package/lab/demo/sources/weather-alerts/source.json +8 -0
  44. package/lab/evals-core.mjs +584 -167
  45. package/lab/flows/record.mjs +16 -4
  46. package/lab/kinds/list.mjs +32 -23
  47. package/lab/run-evals.js +3 -3
  48. package/lab/server.py +913 -119
  49. package/lab/web/dist/assets/gallery-CFntjQFG.js +5 -0
  50. package/lab/web/dist/assets/main-BlTG2i20.css +1 -0
  51. package/lab/web/dist/assets/main-D6MNWqak.js +17 -0
  52. package/lab/web/dist/assets/tokens-DwzzdSIF.css +1 -0
  53. package/lab/web/dist/assets/tokens-bvuNFuYg.js +60 -0
  54. package/lab/web/dist/gallery.html +3 -3
  55. package/lab/web/dist/index.html +4 -4
  56. package/package.json +10 -2
  57. package/lab/web/dist/assets/gallery-BcvmOPqb.js +0 -3
  58. package/lab/web/dist/assets/main-BBUxHZ-D.js +0 -16
  59. package/lab/web/dist/assets/main-CMoyjImZ.css +0 -1
  60. package/lab/web/dist/assets/tokens-B7N4NvmM.css +0 -1
  61. package/lab/web/dist/assets/tokens-CiJkwD3V.js +0 -60
package/CHANGELOG.md CHANGED
@@ -5,6 +5,91 @@ 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.12.0
9
+
10
+ ### Upgrade notes
11
+
12
+ - **Copy your data directory before upgrading** (see "User data" in the
13
+ README). A pipeline 0.12.0 saves is pipeline version 17, which 0.11.x
14
+ cannot read. To go back, reinstall 0.11.0 and restore the copy.
15
+ - An HTTP Request step no longer picks an API of its own: it uses its job's
16
+ flow step and connection. A step that named a different API keeps the
17
+ request it would have sent, and a request in a job with no flow step
18
+ becomes a Prompt keeping its words. Pipelines read as version 17, and are
19
+ saved as it at their next edit.
20
+
21
+ ### Added
22
+
23
+ - `evals-lab export --lab <url>` writes a lab's settings to a file:
24
+ workspaces, connections with who each is shared with, profiles and the
25
+ grader, never a key. `--workspace <slug>` exports one workspace's.
26
+ `evals-lab import <file> --lab <url>` adds what the lab lacks, giving each
27
+ connection the key in `$EVALSLAB_API_KEY_<SLUG>`, and sends keys only to
28
+ an https or loopback lab.
29
+ - `LAB_IMPORT=<file>` imports a lab file each time the lab starts.
30
+ - A new lab opens on five demos: describe images (twice), classify
31
+ sentiment, compare wordings, and sort weather alerts from a live HTTP GET
32
+ Source. A pack can now carry a dynamic Source.
33
+ - Create test… on a dynamic Source's Calls makes a dataset that is graded
34
+ and ready to run.
35
+
36
+ ### Changed
37
+
38
+ - A new lab starts in about half the time.
39
+ - Names, slugs and model ids get the full width of their field, the tables
40
+ in one dialog share one label column, and Undo is amber and labelled.
41
+ - A dynamic Source names its provider once, and the grader picker says
42
+ Connection.
43
+
44
+ ### Fixed
45
+
46
+ - An API call Source sends an HTTP endpoint connection's key in the header
47
+ the connection names, not always `Authorization: Bearer`.
48
+ - An API call Source can use only a connection shared with its workspace.
49
+ - A run is refused when two connections take their key from the same
50
+ variable, on a re-run too.
51
+ - Seeding from production reads a gateway flow's replies in its
52
+ connection's request format.
53
+ - The Runs stop keeps its icon on Home.
54
+
55
+ ## 0.11.0
56
+
57
+ ### Upgrade notes
58
+
59
+ - **Copy your data directory before upgrading** (see "User data" in the
60
+ README). A pipeline 0.11.0 saves is pipeline version 16, which 0.10.x
61
+ cannot read. To go back, reinstall 0.10.0 and restore the copy.
62
+ - On first start, each Target profile becomes an AI Connection (its type,
63
+ address, key and model), keeping its key and who it is shared with, plus a
64
+ Connection Profile where it changed any model or image setting. The old
65
+ profiles stay stored, unused.
66
+ - A pipeline's target names a Connection and a Profile. Pipelines read as
67
+ version 16, and are saved as it at their next edit; a run from before
68
+ shows its Target profile as its Profile.
69
+
70
+ ### Added
71
+
72
+ - Setup › Connections holds AI Connections and Connection Profiles: a
73
+ connection is where to send and with which key, and a profile is how to
74
+ ask. A target picks one of each, so one connection serves several
75
+ profiles.
76
+ - Every pipeline, prompt, run, profile and wizard has its own address, and
77
+ Copy link copies it.
78
+ - A run's Responses read as a flow, one ordered table a stage, and name the
79
+ stage and filter that discarded a reply. Move to filter set… and Move all
80
+ turn a filter into part of a filter set.
81
+ - A Call is viewed, edited, renamed and cloned in one dialog, and a
82
+ workflow or dynamic Source's settings are edited where they were made.
83
+ - The package page gives a contact address and links to its home page.
84
+
85
+ ### Fixed
86
+
87
+ - File on disk never reads `/proc`, and a Call holding the lab's own secret
88
+ is refused.
89
+ - Importing a version-15 YAML pipeline keeps each target's profile.
90
+ - A Target profile from before reply limits keeps its 160-token cap when it
91
+ becomes a connection.
92
+
8
93
  ## 0.10.0
9
94
 
10
95
  ### Upgrade notes
@@ -42,7 +127,9 @@ version cannot always read it back.
42
127
  lab warns at start when one is on while it listens beyond this machine
43
128
  without `LAB_PASSWORD`. `SOURCE_LOCAL_ROOT=<dir>` lets File on disk read only
44
129
  under that directory. A command runs without a shell, in an empty
45
- directory, without the lab's own secrets.
130
+ directory, with the lab's own secrets out of its environment -- but it
131
+ can still read them from the lab's, so turn Command on only where
132
+ everyone who can reach the lab may see them.
46
133
  - Copy or move… copies or moves a pipeline, dataset, filter set, prompt or
47
134
  Source to another workspace, and says first what a move would leave broken.
48
135
  - A run's view opens on an Evals summary, and Show details shows what came
package/README.md CHANGED
@@ -45,8 +45,8 @@ npm install -g evals-lab
45
45
  evals-lab
46
46
  ```
47
47
 
48
- Open <http://localhost:8080>. A new lab starts with two demo datasets and
49
- their pipelines. Note the first start takes longer while it installs them. Without
48
+ Open <http://localhost:8080>. A new lab starts with five demos, each a dataset
49
+ and its pipeline, which run once you add an AI Connection in Setup. Note the first start takes longer while it installs them. Without
50
50
  installing: `npx evals-lab`.
51
51
 
52
52
  | Needs | Windows | Linux |
@@ -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
 
@@ -164,7 +164,7 @@ CI can run a lab's own pipeline there instead, so the run shows in that lab's
164
164
  History and its keys never leave it:
165
165
 
166
166
  ```bash
167
- LAB_PASSWORD=... evals-lab run --lab https://lab.example --pipeline "Demo 1" --wait
167
+ LAB_PASSWORD=... evals-lab run --lab https://lab.example --pipeline "Demo: describe images" --wait
168
168
  ```
169
169
 
170
170
  ```text
@@ -237,11 +237,17 @@ 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
243
249
  [PolyForm Internal Use License 1.0.0](https://polyformproject.org/licenses/internal-use/1.0.0)
244
250
  (`LICENSE.md`): you and your company may use and change it for your own
245
251
  internal business, but not give it, or a changed copy, to anyone else, sell
246
- it, or offer it as a service. The demo images are public domain; each is
252
+ it, or offer it as a service. The demo files are public domain; each is
247
253
  credited in `lab/demo/CREDITS.md`.
package/bin/evals-lab.js CHANGED
@@ -30,6 +30,8 @@ const LOCAL_OLLAMA = "http://127.0.0.1:11434";
30
30
  const USAGE = `Usage: evals-lab [options]
31
31
  evals-lab run <bundle> [options] (evals-lab run --help)
32
32
  evals-lab run --lab <url> --pipeline <name> [--wait] [options]
33
+ evals-lab export --lab <url> [--workspace <slug>] [--out <file>]
34
+ evals-lab import <file> --lab <url> (evals-lab import --help)
33
35
 
34
36
  --port <n> the port to listen on. Default: $PORT, else 8080.
35
37
  --host <address> the address to listen on. Default: $LAB_HOST, else
@@ -43,7 +45,9 @@ Environment: LAB_PASSWORD puts a password in front of the lab, OLLAMA_URL is
43
45
  the connection a new lab starts with, and PYTHON names the Python to run.
44
46
  SOURCE_ALLOW_LOCAL_FILES=1 and SOURCE_ALLOW_LOCAL_COMMANDS=1 let a Source read a
45
47
  file or run a command on this machine; both are off unless set.
46
- SOURCE_LOCAL_ROOT=<dir> lets a file Source read only under that directory.`;
48
+ SOURCE_LOCAL_ROOT=<dir> lets a file Source read only under that directory.
49
+ LAB_IMPORT=<file> imports a lab file each time the lab starts, each
50
+ connection's key from $EVALSLAB_API_KEY_<SLUG>.`;
47
51
 
48
52
  // Where a lab keeps its store when nobody said: the platform's per-user data
49
53
  // directory, so it survives the package being upgraded or reinstalled.
@@ -159,6 +163,10 @@ function fail(message) {
159
163
  }
160
164
 
161
165
  async function main() {
166
+ if (process.argv[2] === "export" || process.argv[2] === "import") {
167
+ process.exitCode = await require("./lab-file.js").labFile(process.argv[2], process.argv.slice(3));
168
+ return;
169
+ }
162
170
  if (process.argv[2] === "run") {
163
171
  process.exitCode = await require("./run.js").run(process.argv.slice(3), { lab: LAB });
164
172
  return;
@@ -0,0 +1,97 @@
1
+ // `evals-lab export` and `evals-lab import` (docs/lab-export.md): a running
2
+ // lab's settings -- its AI Connections, Connection Profiles, grader and
3
+ // workspaces -- as a lab file, and a lab file into a lab, adding what it
4
+ // lacks. Setup › Workspaces › Data does the same through the same two routes.
5
+ //
6
+ // THE KEYS. A lab file holds none, and the lab serves none. `import` sends
7
+ // each connection's from $EVALSLAB_API_KEY_<SLUG>, as a CI run reads it, and
8
+ // only to an https or loopback lab: anything else would put the key on the
9
+ // wire in the clear.
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+ const { labApi, Refused } = require("./run.js");
13
+
14
+ const EXIT = { broken: 3 };
15
+
16
+ const USAGE = `Usage: evals-lab export --lab <url> [--workspace <slug>] [--out <file>]
17
+ evals-lab import <file> --lab <url>
18
+
19
+ --lab <url> the running lab. LAB_PASSWORD is sent when it is set.
20
+ --workspace <slug> export only the workspace at /w/<slug>. Default: every one.
21
+ --out <file> where the export goes. Default: stdout.
22
+
23
+ A lab file holds no key. import gives each connection the key in
24
+ $EVALSLAB_API_KEY_<SLUG> when it is set, and sends keys only to an https or
25
+ loopback lab. An import only adds: what the lab has already is left as it is.`;
26
+
27
+ function options(argv, verb) {
28
+ const o = {};
29
+ const value = { "--lab": "lab", "--workspace": "workspace", "--out": "out" };
30
+ for (let i = 0; i < argv.length; i++) {
31
+ const a = argv[i];
32
+ if (a === "--help" || a === "-h") o.help = true;
33
+ else if (value[a]) {
34
+ if (argv[i + 1] === undefined) throw new Refused(`${a} needs a value`, true);
35
+ o[value[a]] = argv[++i];
36
+ } else if (a.startsWith("-")) throw new Refused(`no option ${a}`, true);
37
+ else if (verb === "import" && o.file === undefined) o.file = a;
38
+ else throw new Refused(`one lab file, not ${a} as well`, true);
39
+ }
40
+ if (o.help) return o;
41
+ if (o.lab == null) throw new Refused("name the lab with --lab", true);
42
+ if (!/^https?:\/\/[^/]/i.test(o.lab)) throw new Refused(`--lab ${o.lab} is not an http or https address`, true);
43
+ if (verb === "import") {
44
+ if (o.file == null) throw new Refused("name the lab file to import", true);
45
+ if (o.workspace != null || o.out != null) throw new Refused("--workspace and --out are export's", true);
46
+ }
47
+ return o;
48
+ }
49
+
50
+ const keyVar = (id, slug) => "EVALSLAB_API_KEY_"
51
+ + String(slug || id).toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
52
+
53
+ // Loopback or https: where a key may go.
54
+ function keySafe(base) {
55
+ const u = new URL(base);
56
+ return u.protocol === "https:" || ["localhost", "127.0.0.1", "[::1]"].includes(u.hostname);
57
+ }
58
+
59
+ async function labFile(verb, argv, { env = process.env, stdout = process.stdout, stderr = process.stderr } = {}) {
60
+ const say = text => stderr.write(text + "\n");
61
+ try {
62
+ const o = options(argv, verb);
63
+ if (o.help) { stdout.write(USAGE + "\n"); return 0; }
64
+ const base = o.lab.replace(/\/+$/, "");
65
+ const api = labApi(base, env);
66
+ if (verb === "export") {
67
+ const out = await api("GET", "/api/lab-export" + (o.workspace ? `?workspace=${encodeURIComponent(o.workspace)}` : ""));
68
+ const text = JSON.stringify(out, null, 2) + "\n";
69
+ if (o.out) fs.writeFileSync(path.resolve(o.out), text);
70
+ else stdout.write(text);
71
+ if (o.out) say(`${out.connections.length} connections, ${out.workspaces.length} workspaces to ${o.out}`);
72
+ return 0;
73
+ }
74
+ let lab;
75
+ try { lab = JSON.parse(fs.readFileSync(path.resolve(o.file), "utf8")); }
76
+ catch (e) { throw new Refused(`${o.file} is not a lab file: ${e.message}`); }
77
+ const keys = {};
78
+ for (const c of Array.isArray(lab?.connections) ? lab.connections : []) {
79
+ const name = keyVar(c?.id, c?.slug);
80
+ if (env[name]) keys[name] = env[name];
81
+ }
82
+ if (Object.keys(keys).length && !keySafe(base)) {
83
+ throw new Refused(`${base} is neither https nor loopback, so no key goes to it: `
84
+ + `unset ${Object.keys(keys).join(", ")} to import without them`);
85
+ }
86
+ const { said } = await api("POST", "/api/lab-import", { lab, keys });
87
+ for (const line of said) say(line);
88
+ return 0;
89
+ } catch (e) {
90
+ if (!(e instanceof Refused)) throw e;
91
+ say(`evals-lab ${verb}: ${e.message}`);
92
+ if (e.usage) say(`\n${USAGE}`);
93
+ return EXIT.broken;
94
+ }
95
+ }
96
+
97
+ module.exports = { labFile, options, keyVar, keySafe, USAGE };
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}`);
@@ -573,4 +573,4 @@ async function run(argv, { lab, env = process.env, stdout = process.stdout, stde
573
573
  }
574
574
  }
575
575
 
576
- module.exports = { run, runOptions, USAGE };
576
+ module.exports = { run, runOptions, USAGE, labApi, Refused };
package/lab/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.0 (2026.10.09-614)
1
+ 0.12.0 (2026.10.12-718)
@@ -1,9 +1,13 @@
1
1
  # Credits
2
2
 
3
- Every file in `images/`, where it came from and under what terms. All
3
+ Every file in `sources/`, where it came from and under what terms. All
4
4
  need no attribution; the sources are listed so each one can be traced.
5
- Files were resized to about 3 megapixels and their metadata stripped;
6
- the panorama is a crop, and the PNG and TIFF are conversions.
5
+
6
+ ## Images
7
+
8
+ The images of `Demo 1/` and `Demo 2/`. Files were resized to about 3
9
+ megapixels and their metadata stripped; the panorama is a crop, and the PNG
10
+ and TIFF are conversions.
7
11
 
8
12
  | File | Source | Author | Licence |
9
13
  |---|---|---|---|
@@ -90,3 +94,33 @@ the panorama is a crop, and the PNG and TIFF are conversions.
90
94
  | `whiteboard-meeting-notes.jpg` | https://www.flickr.com/photo.gne?id=186873704 | | Public domain |
91
95
  | `whiteboard-messages.jpg` | https://www.flickr.com/photo.gne?id=23162308061 | | Public domain |
92
96
  | `wooden-house-fence.jpg` | https://www.flickr.com/photo.gne?id=26595748497 | | Public domain |
97
+
98
+ ## Texts
99
+
100
+ The short texts of `sentiment/`, written for this pack, and the alert
101
+ types of `weather-alerts/`, fetched from the National Weather Service's API
102
+ on 2026-10-11. A work of the US federal government is in the public domain.
103
+
104
+ | File | Source | Author | Licence |
105
+ |---|---|---|---|
106
+ | `bakery-croissant.txt` | Written for this pack | Evals Lab | Public domain |
107
+ | `bus-timetable.txt` | Written for this pack | Evals Lab | Public domain |
108
+ | `cafe-coffee.txt` | Written for this pack | Evals Lab | Public domain |
109
+ | `concert-night.txt` | Written for this pack | Evals Lab | Public domain |
110
+ | `film-sequel.txt` | Written for this pack | Evals Lab | Public domain |
111
+ | `garden-tomatoes.txt` | Written for this pack | Evals Lab | Public domain |
112
+ | `gym-contract.txt` | Written for this pack | Evals Lab | Public domain |
113
+ | `headphones-review.txt` | Written for this pack | Evals Lab | Public domain |
114
+ | `hotel-room.txt` | Written for this pack | Evals Lab | Public domain |
115
+ | `library-hours.txt` | Written for this pack | Evals Lab | Public domain |
116
+ | `meeting-minutes.txt` | Written for this pack | Evals Lab | Public domain |
117
+ | `parcel-lost.txt` | Written for this pack | Evals Lab | Public domain |
118
+ | `phone-update.txt` | Written for this pack | Evals Lab | Public domain |
119
+ | `puppy-home.txt` | Written for this pack | Evals Lab | Public domain |
120
+ | `recipe-note.txt` | Written for this pack | Evals Lab | Public domain |
121
+ | `road-closure.txt` | Written for this pack | Evals Lab | Public domain |
122
+ | `teacher-thanks.txt` | Written for this pack | Evals Lab | Public domain |
123
+ | `train-delay.txt` | Written for this pack | Evals Lab | Public domain |
124
+ | `volunteer-day.txt` | Written for this pack | Evals Lab | Public domain |
125
+ | `weather-report.txt` | Written for this pack | Evals Lab | Public domain |
126
+ | `https-api.weather.gov-alerts-types.txt` | https://api.weather.gov/alerts/types | National Weather Service | Public domain |
@@ -2,12 +2,12 @@
2
2
  "format": "evals-lab/dataset",
3
3
  "version": 6,
4
4
  "dataset": {
5
- "name": "Demo 1",
5
+ "name": "Demo: describe images",
6
6
  "body": {
7
7
  "version": 6,
8
8
  "source": {
9
9
  "id": "Demo 1",
10
- "name": "Demo 1"
10
+ "name": "Demo: describe images"
11
11
  },
12
12
  "cases": [
13
13
  {
@@ -2,12 +2,12 @@
2
2
  "format": "evals-lab/dataset",
3
3
  "version": 6,
4
4
  "dataset": {
5
- "name": "Demo 2",
5
+ "name": "Demo: describe images 2",
6
6
  "body": {
7
7
  "version": 6,
8
8
  "source": {
9
9
  "id": "Demo 2",
10
- "name": "Demo 2"
10
+ "name": "Demo: describe images 2"
11
11
  },
12
12
  "cases": [
13
13
  {