evals-lab 0.8.0 → 0.9.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 +42 -0
- package/README.md +1 -0
- package/lab/VERSION +1 -1
- package/lab/demo/pipelines/demo-1.json +1 -1
- package/lab/demo/pipelines/demo-2.json +1 -1
- package/lab/evals-core.mjs +245 -15
- package/lab/kinds/list.mjs +258 -14
- package/lab/run-evals.js +34 -3
- package/lab/server.py +613 -20
- package/lab/web/dist/assets/gallery-BpR9b4EM.js +3 -0
- package/lab/web/dist/assets/main-BMRmyJvo.css +1 -0
- package/lab/web/dist/assets/main-BfiFtaYW.js +19 -0
- package/lab/web/dist/assets/tokens-C67OCDd-.css +1 -0
- package/lab/web/dist/assets/tokens-DzZqM5IZ.js +59 -0
- package/lab/web/dist/gallery.html +3 -3
- package/lab/web/dist/index.html +4 -4
- package/package.json +15 -2
- package/lab/web/dist/assets/gallery-SnUhXRBn.js +0 -3
- package/lab/web/dist/assets/main-Ca7o-nM0.css +0 -1
- package/lab/web/dist/assets/main-Dru4_P5G.js +0 -20
- package/lab/web/dist/assets/tokens-0az9gfTq.js +0 -58
- package/lab/web/dist/assets/tokens-CqWJKhOx.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,48 @@ 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.9.0
|
|
9
|
+
|
|
10
|
+
### Upgrade notes
|
|
11
|
+
|
|
12
|
+
- **Copy your data directory before upgrading** (see "User data" in the
|
|
13
|
+
README). A pipeline 0.9.0 saves is pipeline version 14, which 0.8.x
|
|
14
|
+
cannot read. To go back, reinstall 0.8.0 and restore the copy.
|
|
15
|
+
- A job's Drop items and Reject the answer rules become one private filter
|
|
16
|
+
set in that job, which keeps and rejects exactly what they did. Pipelines
|
|
17
|
+
read as version 14, and are saved as it at their next edit.
|
|
18
|
+
- On first start, 0.9.0 adds the tables filter sets are kept in, and each
|
|
19
|
+
run keeps the filter sets it ran with.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Library › Filter sets: named, versioned lists of filters that clean a list
|
|
24
|
+
reply. A Drop item filter drops the items its conditions match; a Reject
|
|
25
|
+
reply filter rejects the whole reply. Filters run in their list's order.
|
|
26
|
+
- A job's Responses stage links filter sets, following each one's latest
|
|
27
|
+
version or pinned to one, or holds a private set of its own.
|
|
28
|
+
- Each Target profile shows its connection status: Not tested, Connected,
|
|
29
|
+
Key needed or Unreachable, checked quietly when Setup opens. The dot opens
|
|
30
|
+
the Test connection result.
|
|
31
|
+
- Target profiles list each profile's Model and what uses it, with quick
|
|
32
|
+
filters: All, Connected, Needs attention and Grader. A profile's edit
|
|
33
|
+
sheet links the pipelines and eval groups that use it.
|
|
34
|
+
- Selected profiles can be tested, or have their workspaces changed, all
|
|
35
|
+
at once.
|
|
36
|
+
- An HTTP Request target's prompt can be edited. Insert field… adds one of
|
|
37
|
+
the item's fields, and the preview highlights the words changed from the
|
|
38
|
+
recorded prompt.
|
|
39
|
+
- The Recorded target shows the flow's original prompt.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Target profiles no longer show Type, Address and Key columns, or the
|
|
44
|
+
Type, Key and Sort selects.
|
|
45
|
+
- The Edited target's request template is in Flow's request, folded until
|
|
46
|
+
opened.
|
|
47
|
+
- Drop items and Reject the answer are no longer offered under a job's
|
|
48
|
+
Responses: link a filter set instead.
|
|
49
|
+
|
|
8
50
|
## 0.8.0
|
|
9
51
|
|
|
10
52
|
### Upgrade notes
|
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ Runs are kept in history which can be exported.
|
|
|
11
11
|
Currently in early release. Runs locally and doesn't send your data anywhere else.
|
|
12
12
|
|
|
13
13
|
Example use-cases:
|
|
14
|
+
- Keywording images using a model's vision capability, and running evals on the tags it returns
|
|
14
15
|
- Providing images to an LLM and comparing responses for accuracy
|
|
15
16
|
- Checking which model can meet evals in a Power Automate workflow
|
|
16
17
|
- Determining which prompt gets the highest score in metrics
|
package/lab/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.9.0 (2026.10.07-516)
|
package/lab/evals-core.mjs
CHANGED
|
@@ -33,12 +33,14 @@
|
|
|
33
33
|
// and that is a measurement.
|
|
34
34
|
|
|
35
35
|
import yaml from "./js-yaml.mjs";
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
import { evaluate, asText, WdlError, } from "./flows/wdl.mjs";
|
|
38
38
|
import { stepsOf as flowStepsOf, actionsByName, readerFor, readsOf, } from "./flows/record.mjs";
|
|
39
39
|
import { flowApi, } from "./flows/flowApi.mjs";
|
|
40
40
|
import registerMetrics from "./metrics/builtin.mjs";
|
|
41
|
-
import list, { ECHO, legacyTagsOut, splitItems, tidy, tryExamples, itemRulesProblems, dropBy, rejectBy, matcher
|
|
41
|
+
import list, { ECHO, legacyTagsOut, splitItems, tidy, tryExamples, itemRulesProblems, dropBy, rejectBy, matcher,
|
|
42
|
+
FILTER_TYPES, FILTER_SEPARATORS, FILTER_MODIFIER, applyFilter, applyFilterSet, tryFilter, splitSample, filterProblems, filterFromItemRule,
|
|
43
|
+
} from "./kinds/list.mjs";
|
|
42
44
|
|
|
43
45
|
// ---- The types -------------------------------------------------------------
|
|
44
46
|
// docs/pipeline-model.md as types, and the shapes of the registries every
|
|
@@ -138,9 +140,19 @@ import list, { ECHO, legacyTagsOut, splitItems, tidy, tryExamples, itemRulesProb
|
|
|
138
140
|
|
|
139
141
|
|
|
140
142
|
|
|
143
|
+
/** The filter sets a job cleans and gates its reply by (#339): each a link to
|
|
144
|
+
a Library filter set (followed latest or pinned) or a private one held
|
|
145
|
+
inline. A Responses step beside the modifiers, at the same place, so its
|
|
146
|
+
sets' filters apply in document order with them -- what the Drop items and
|
|
147
|
+
Reject the answer modifiers did inline, lifted into linked sets. */
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
141
153
|
/** A job's steps: its Content stage, then its Responses (§16). */
|
|
142
154
|
|
|
143
|
-
|
|
155
|
+
|
|
144
156
|
|
|
145
157
|
/** A target step that prompts a model: its words, resolved under the job's
|
|
146
158
|
token mappings, and a profile of its own where it names one. */
|
|
@@ -316,6 +328,45 @@ import list, { ECHO, legacyTagsOut, splitItems, tidy, tryExamples, itemRulesProb
|
|
|
316
328
|
/** A pipeline's evals, in the order they read a run. */
|
|
317
329
|
|
|
318
330
|
|
|
331
|
+
/**
|
|
332
|
+
* A filter set (issue #335): a versioned Library body holding the filters
|
|
333
|
+
* that shape a list reply, mirroring DatasetBody / the eval group. The lab
|
|
334
|
+
* will keep each as a row and a run grade against the body kept with it, as a
|
|
335
|
+
* dataset does (#336); for now it is the format a filter set reads and writes
|
|
336
|
+
* at. `filters` are tried in order -- each drop-item narrows the items the
|
|
337
|
+
* next reads, and the first reject-reply that fires gates the answer.
|
|
338
|
+
*/
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
/** A Library filter set by reference; a run's copy says the version it used:
|
|
345
|
+
the body's fingerprint, and its number. Mirrors GroupRef. */
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
/** A private filter set: a filter-set body held in the pipeline rather than
|
|
349
|
+
the Library -- what Runs makes when a link is not to a Library set (#339),
|
|
350
|
+
as a private eval group (OwnGroup) is. A filter set has no cases, so this
|
|
351
|
+
is the body itself. */
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* A pipeline's link to a filter set in Responses (issue #335, mirrors
|
|
356
|
+
* GroupEval / OwnGroup): `set` names a Library set, followed at its newest
|
|
357
|
+
* version (`pin: null`) or pinned at one (`pin: n`); a private set is `own`,
|
|
358
|
+
* with `set: null`. From version 14 a job's Responses stage carries these on
|
|
359
|
+
* a `filters` step (#339); the upgrade from 13 migrates the inline Drop items
|
|
360
|
+
* / Reject rules into a private one, and the worker cleans and gates a run's
|
|
361
|
+
* replies against the kept bodies.
|
|
362
|
+
*/
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|
|
319
370
|
|
|
320
371
|
|
|
321
372
|
|
|
@@ -874,6 +925,11 @@ import list, { ECHO, legacyTagsOut, splitItems, tidy, tryExamples, itemRulesProb
|
|
|
874
925
|
|
|
875
926
|
|
|
876
927
|
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
|
|
877
933
|
|
|
878
934
|
|
|
879
935
|
|
|
@@ -1233,6 +1289,57 @@ function upgradeDatasetBody (body ) {
|
|
|
1233
1289
|
return recordedIdsV7(groupOfV6({ source: null, cases: raw.map(caseOfV4).map(caseOfV5) }));
|
|
1234
1290
|
}
|
|
1235
1291
|
|
|
1292
|
+
/** The filter-set body's version (issue #335). Version 1 is the first: the
|
|
1293
|
+
`{ version, filters }` body #334 introduces. A later format adds a step to
|
|
1294
|
+
`upgradeFilterSetBody`, as `upgradeDatasetBody` grew, with server.py's
|
|
1295
|
+
`upgrade_filter_set_body` its twin. */
|
|
1296
|
+
const FILTER_SET_VERSION = 1 ;
|
|
1297
|
+
|
|
1298
|
+
/** [body] as this version of a filter set (1), from any earlier one. There is
|
|
1299
|
+
no earlier version yet, so a version-1 body reads as it is and anything
|
|
1300
|
+
else comes back untouched for `filterSetBodyProblems` to refuse. Pure: the
|
|
1301
|
+
same body gives the same answer, and server.py's upgrade_filter_set_body is
|
|
1302
|
+
its twin. */
|
|
1303
|
+
function upgradeFilterSetBody (body ) {
|
|
1304
|
+
if (!isObj(body)) return body;
|
|
1305
|
+
if (body.version === FILTER_SET_VERSION) return body;
|
|
1306
|
+
return body;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/** A filter set with no filters, at this version: what New filter set starts
|
|
1310
|
+
from, and what a lab with none holds. */
|
|
1311
|
+
function blankFilterSetBody() {
|
|
1312
|
+
return { version: FILTER_SET_VERSION, filters: [] };
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
/** Why [body] is not a filter set the lab reads, one sentence each: an object
|
|
1316
|
+
at this version whose filters are a valid filter list (filterProblems). For
|
|
1317
|
+
the store (#336) and the pipeline link (#339). */
|
|
1318
|
+
function filterSetBodyProblems(body , at = "the filter set") {
|
|
1319
|
+
if (!isObj(body)) return [`${at} has to be a JSON object`];
|
|
1320
|
+
if (body.version !== FILTER_SET_VERSION) {
|
|
1321
|
+
return [`${at} is version ${JSON.stringify(body.version)}, and the lab reads version ${FILTER_SET_VERSION}`];
|
|
1322
|
+
}
|
|
1323
|
+
return filterProblems(body.filters, at);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/** Why [link] is not a filter-set link a run's Responses step may carry, one
|
|
1327
|
+
sentence each (#339): an id, exactly one of a Library `set` reference or a
|
|
1328
|
+
private `own` body (whose shape is the core's to judge), and a `pin` that
|
|
1329
|
+
is null or a whole number. A Library set is the server's to resolve, so its
|
|
1330
|
+
body is not checked here. */
|
|
1331
|
+
function filterSetLinkProblems(link , at = "a filter set") {
|
|
1332
|
+
if (!isObj(link)) return [`${at}: a filter-set link has to be a JSON object`];
|
|
1333
|
+
const bad = [];
|
|
1334
|
+
if (!isStr(link.id) || !link.id.trim()) bad.push(`${at}: a filter-set link has no id`);
|
|
1335
|
+
const hasSet = isObj(link.set) && isStr(link.set.id);
|
|
1336
|
+
const hasOwn = link.own != null;
|
|
1337
|
+
if (hasSet === hasOwn) bad.push(`${at}: a filter-set link names a Library set or holds a private one, not both`);
|
|
1338
|
+
if (hasOwn) bad.push(...filterSetBodyProblems(link.own, `${at}: the private filter set`));
|
|
1339
|
+
if (link.pin != null && !Number.isInteger(link.pin)) bad.push(`${at}: a pin is a whole number or none`);
|
|
1340
|
+
return bad;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1236
1343
|
/** A version-6 body as a version-8 eval group: scored All, the lab's
|
|
1237
1344
|
grader, and no metrics of its own for every item or the whole run --
|
|
1238
1345
|
what a Metrics eval naming the dataset with none of its own graded, which
|
|
@@ -2936,7 +3043,7 @@ function applyModifiers (list , kind ,
|
|
|
2936
3043
|
// 12: a Contains metric's Ignore case holds item by item too.
|
|
2937
3044
|
// 13: an eval is a link to an eval group, or a group of the pipeline's own,
|
|
2938
3045
|
// and the document has an overall pass rule (pipeline-model §17).
|
|
2939
|
-
const PIPELINE_VERSION =
|
|
3046
|
+
const PIPELINE_VERSION = 14 ;
|
|
2940
3047
|
|
|
2941
3048
|
// Plain objects, so an entry is added by assignment and a reader never needs
|
|
2942
3049
|
// to know which registered it.
|
|
@@ -4234,6 +4341,23 @@ STEP_TYPES.modifier = {
|
|
|
4234
4341
|
},
|
|
4235
4342
|
};
|
|
4236
4343
|
|
|
4344
|
+
// A filters step: the filter sets a job cleans and gates its reply by, each a
|
|
4345
|
+
// link or a private set (#339). It sits with the modifiers (rank 2), so its
|
|
4346
|
+
// sets apply in document order among them. A private set's body is the core's
|
|
4347
|
+
// to judge (filterSetBodyProblems); a link to a Library set is the server's to
|
|
4348
|
+
// resolve, so only its shape is checked here.
|
|
4349
|
+
STEP_TYPES.filters = {
|
|
4350
|
+
label: "Filter sets", slot: "responses", rank: 2, many: true,
|
|
4351
|
+
description: "Cleans and gates the reply with linked or private filter sets.",
|
|
4352
|
+
in: "value", out: "value",
|
|
4353
|
+
apply: "runPipeline",
|
|
4354
|
+
fields: ["type", "filterSets"],
|
|
4355
|
+
validate(step, _ctx, bad, at){
|
|
4356
|
+
if (!Array.isArray(step.filterSets)) return void bad.push(`${at}: filterSets has to be a list`);
|
|
4357
|
+
for (const link of step.filterSets ) bad.push(...filterSetLinkProblems(link, at));
|
|
4358
|
+
},
|
|
4359
|
+
};
|
|
4360
|
+
|
|
4237
4361
|
// ---- reading a job's steps, and a target's -----------------------------------
|
|
4238
4362
|
// Every reader asks these, never a step's position or the document's shape:
|
|
4239
4363
|
// which steps a job holds is its own, and a version that moves a field
|
|
@@ -4429,12 +4553,58 @@ function outOf(job ) {
|
|
|
4429
4553
|
return { ...read, modifiers } ;
|
|
4430
4554
|
}
|
|
4431
4555
|
|
|
4432
|
-
/** [job] reading its reply as [out]: its Format Validation and modifiers.
|
|
4556
|
+
/** [job] reading its reply as [out]: its Format Validation and modifiers.
|
|
4557
|
+
The modifiers replace the job's modifier steps in place, so a modifier step
|
|
4558
|
+
that interleaves with a filters step (#339) keeps its place among them --
|
|
4559
|
+
an edit to one modifier never reorders another step past it. Extra
|
|
4560
|
+
modifiers (one added) come after the last, and a step with no modifier left
|
|
4561
|
+
(one removed) drops out. */
|
|
4433
4562
|
function withOut(job , out ) {
|
|
4434
4563
|
const { modifiers, ...read } = out;
|
|
4435
|
-
const
|
|
4436
|
-
|
|
4437
|
-
|
|
4564
|
+
const mods = modifiers || [];
|
|
4565
|
+
let mi = 0;
|
|
4566
|
+
const steps = [];
|
|
4567
|
+
for (const st of job.steps) {
|
|
4568
|
+
if (st.type === "readReply") continue; // re-added below, in its place
|
|
4569
|
+
if (st.type === "modifier") { if (mi < mods.length) steps.push({ type: "modifier", modifier: mods[mi++] }); }
|
|
4570
|
+
else steps.push(st); // readAs, filters, content -- kept where they are
|
|
4571
|
+
}
|
|
4572
|
+
for (; mi < mods.length; mi++) steps.push({ type: "modifier", modifier: mods[mi] });
|
|
4573
|
+
return { ...job, steps: ordered([...steps, { type: "readReply", out: read }]) };
|
|
4574
|
+
}
|
|
4575
|
+
|
|
4576
|
+
/** The filter-set links a job's Responses stage carries, in document order
|
|
4577
|
+
(#339): read from its one filters step, or none. */
|
|
4578
|
+
function filterSetsOf(job ) {
|
|
4579
|
+
return (stepOf (job, "filters")?.filterSets ?? []).slice();
|
|
4580
|
+
}
|
|
4581
|
+
|
|
4582
|
+
/** [job] with its filter-set links set to [links]: its filters step added,
|
|
4583
|
+
replaced, or taken away when none are left. */
|
|
4584
|
+
function withFilterSets(job , links ) {
|
|
4585
|
+
return withStep(job, "filters", links.length ? { type: "filters", filterSets: [...links] } : null);
|
|
4586
|
+
}
|
|
4587
|
+
|
|
4588
|
+
/** A job's Responses as an ordered modifier list for a run (#339): each
|
|
4589
|
+
modifier step as it is, and each filter-set link as a synthetic modifier
|
|
4590
|
+
that applies the set's filters where the link sits -- so a linked or
|
|
4591
|
+
private set cleans and gates a list reply exactly as the Drop items /
|
|
4592
|
+
Reject modifiers it replaced did, in the same order. [resolve] gives a
|
|
4593
|
+
Library link's body (the worker's kept bodies); a private (`own`) link
|
|
4594
|
+
carries its own, and a Library link with no body resolves to nothing. */
|
|
4595
|
+
function stageModifiers(job , resolve ) {
|
|
4596
|
+
const out = [];
|
|
4597
|
+
for (const st of job?.steps || []) {
|
|
4598
|
+
if (!isObj(st)) continue;
|
|
4599
|
+
if (st.type === "modifier") out.push(st.modifier);
|
|
4600
|
+
else if (st.type === "filters") {
|
|
4601
|
+
for (const link of (st.filterSets ) || []) {
|
|
4602
|
+
const body = link.own ?? (link.set && resolve ? resolve(link.set) : null);
|
|
4603
|
+
out.push({ type: FILTER_MODIFIER, filters: body?.filters || [] });
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
return out;
|
|
4438
4608
|
}
|
|
4439
4609
|
|
|
4440
4610
|
/** A new id: random, so one minted in one browser never collides with one
|
|
@@ -4676,6 +4846,13 @@ function newLink(group , name = "") {
|
|
|
4676
4846
|
return { type: "group", id: newId(), name, continueOnFailure: true, group: { id: group.id, name: group.name }, pin: null };
|
|
4677
4847
|
}
|
|
4678
4848
|
|
|
4849
|
+
/** A new pipeline link to the Library filter set [set], followed at its newest
|
|
4850
|
+
version. Mirrors newLink; for Runs to use when it links a set in Responses
|
|
4851
|
+
(#339). */
|
|
4852
|
+
function newFilterLink(set ) {
|
|
4853
|
+
return { id: newId(), set: { id: set.id, name: set.name }, pin: null };
|
|
4854
|
+
}
|
|
4855
|
+
|
|
4679
4856
|
function jobsOf(next ) {
|
|
4680
4857
|
next.version = PIPELINE_VERSION;
|
|
4681
4858
|
if (Array.isArray(next.chains)) {
|
|
@@ -4822,16 +4999,62 @@ function upgradePipeline (doc , ctx = {}) {
|
|
|
4822
4999
|
}
|
|
4823
5000
|
return localSteps(out, ctx) ;
|
|
4824
5001
|
}
|
|
4825
|
-
if (!isObj(doc) || ![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].includes(doc.version )) return doc;
|
|
5002
|
+
if (!isObj(doc) || ![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].includes(doc.version )) return doc;
|
|
5003
|
+
// Version 13 reads as 14 by migrating a job's inline Drop items / Reject
|
|
5004
|
+
// rules into a private filter set (#339); nothing else about it changes.
|
|
5005
|
+
if (doc.version === 13) {
|
|
5006
|
+
return localSteps(filtersFromRules(clone(doc) ), ctx) ;
|
|
5007
|
+
}
|
|
4826
5008
|
if (doc.version === 11 || doc.version === 12) {
|
|
4827
|
-
return localSteps(groupsOf(withoutCaseMetric(caseAsWritten(cutRefs(clone(doc) )))), ctx) ;
|
|
5009
|
+
return localSteps(filtersFromRules(groupsOf(withoutCaseMetric(caseAsWritten(cutRefs(clone(doc) ))))), ctx) ;
|
|
4828
5010
|
}
|
|
4829
5011
|
// Every version before 10 reads as version 9 first, then as 10, then as 11,
|
|
4830
|
-
// 12 and
|
|
5012
|
+
// 12, 13 and 14.
|
|
4831
5013
|
// A version-10 document is cut as a current one was (profileRefs); the
|
|
4832
5014
|
// earlier ones are cut on their way through nineOf.
|
|
4833
5015
|
const ten = doc.version === 10 ? cutRefs(clone(doc) ) : targetsFromScenarios(nineOf(clone(doc) , ctx));
|
|
4834
|
-
return localSteps(groupsOf(withoutCaseMetric(caseAsWritten(evalsKey(ten)))), ctx) ;
|
|
5016
|
+
return localSteps(filtersFromRules(groupsOf(withoutCaseMetric(caseAsWritten(evalsKey(ten))))), ctx) ;
|
|
5017
|
+
}
|
|
5018
|
+
|
|
5019
|
+
/** [doc] with each job's inline Drop items / Reject modifier steps migrated
|
|
5020
|
+
into one private filter set on a `filters` step, version 13 to 14 (#339).
|
|
5021
|
+
Every drop/reject modifier's rules become filters in document order -- a
|
|
5022
|
+
`drop` modifier's rules drop-item filters, a `reject` modifier's
|
|
5023
|
+
reject-reply filters (filterFromItemRule, which carries the matcher
|
|
5024
|
+
unchanged) -- in one `own` set, and the step takes the place of the first
|
|
5025
|
+
of those modifiers, so the reply is cleaned and gated as it was. A job with
|
|
5026
|
+
no such modifier, or only empty ones, is left as it is; this is a no-op on
|
|
5027
|
+
every path that reaches version 14, so it is safe to run on all of them. */
|
|
5028
|
+
function filtersFromRules(next ) {
|
|
5029
|
+
next.version = PIPELINE_VERSION;
|
|
5030
|
+
if (!Array.isArray(next.jobs)) return next;
|
|
5031
|
+
const TYPE = { drop: "drop-item", reject: "reject-reply" };
|
|
5032
|
+
const isRule = (st ) =>
|
|
5033
|
+
isObj(st) && st.type === "modifier" && isObj(st.modifier) && (st.modifier.type ) in TYPE;
|
|
5034
|
+
next.jobs = next.jobs.map((job ) => {
|
|
5035
|
+
if (!isObj(job) || !Array.isArray(job.steps) || !job.steps.some(isRule)) return job;
|
|
5036
|
+
const filters = [];
|
|
5037
|
+
for (const st of job.steps ) {
|
|
5038
|
+
if (!isRule(st)) continue;
|
|
5039
|
+
const m = (st ).modifier ;
|
|
5040
|
+
for (const r of Array.isArray(m.rules) ? m.rules : []) {
|
|
5041
|
+
filters.push(filterFromItemRule(r, TYPE[m.type ] ));
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
// Empty Drop items / Reject modifiers held nothing, so they simply drop.
|
|
5045
|
+
// The link's id is the job's, so re-upgrading a document mints the same
|
|
5046
|
+
// one (the migration is idempotent) and a run keyed by it reads the same.
|
|
5047
|
+
const link = filters.length
|
|
5048
|
+
? { id: `filters-${isStr(job.id) ? job.id : "job"}`, set: null, pin: null, own: { version: FILTER_SET_VERSION, filters } } : null;
|
|
5049
|
+
let placed = false;
|
|
5050
|
+
const steps = [];
|
|
5051
|
+
for (const st of job.steps ) {
|
|
5052
|
+
if (!isRule(st)) { steps.push(st); continue; }
|
|
5053
|
+
if (link && !placed) { steps.push({ type: "filters", filterSets: [link] }); placed = true; }
|
|
5054
|
+
}
|
|
5055
|
+
return { ...job, steps };
|
|
5056
|
+
});
|
|
5057
|
+
return next;
|
|
4835
5058
|
}
|
|
4836
5059
|
|
|
4837
5060
|
/** [doc] with no Metrics eval holding the `case` metric: a dataset's cases
|
|
@@ -5636,13 +5859,16 @@ function readBundle(files , ctx = {})
|
|
|
5636
5859
|
* the job it sits in, and the connection it asks -- its own profile, or
|
|
5637
5860
|
* its scenario's.
|
|
5638
5861
|
*/
|
|
5639
|
-
function stagesFor(run , i )
|
|
5862
|
+
function stagesFor(run , i , resolveFilters )
|
|
5640
5863
|
|
|
5641
5864
|
{
|
|
5642
5865
|
const stages = run.jobs.map((ch, k) => {
|
|
5643
|
-
const { kind, modifiers, ...settings } = outOf(ch);
|
|
5866
|
+
const { kind, modifiers: _mods, ...settings } = outOf(ch);
|
|
5867
|
+
// The modifiers a run applies, with each linked or private filter set a
|
|
5868
|
+
// synthetic modifier where its link sits (#339); without a resolver, a
|
|
5869
|
+
// Library link reads nothing, a private one its own body.
|
|
5644
5870
|
return { text: targetStepOf(run, i, k) .prompt, kind, withImage: sendsImage(ch),
|
|
5645
|
-
verbatim: !!targetEntryOf(run, i, k)?.verbatim, modifiers:
|
|
5871
|
+
verbatim: !!targetEntryOf(run, i, k)?.verbatim, modifiers: stageModifiers(ch, resolveFilters), settings };
|
|
5646
5872
|
});
|
|
5647
5873
|
const connections = run.jobs.map((_, k) => {
|
|
5648
5874
|
// A step answered here asks nothing: the profile a run made before it
|
|
@@ -6039,9 +6265,13 @@ CONNECTION_FIELDS, SETTING_KEYS, OPTION_FIELDS, jobLabel, scenarioLabel, jobWord
|
|
|
6039
6265
|
contentOf, withContent, replyOf,
|
|
6040
6266
|
targetsOf, targetLetter, targetLabel, targetStepOf, targetEntryOf, jobTargetType, targetProfileOf, recordedTargetOf, withTarget,
|
|
6041
6267
|
tokensOf, withTokens, sendsImage, withImage, flowStepOf, readAsOf, outOf, withOut, slotEntry, SLOTS,
|
|
6268
|
+
filterSetsOf, withFilterSets, stageModifiers,
|
|
6042
6269
|
blankPipeline, upgradePipeline, fatProfileRef, newId, newEval, newLink, validatePipeline, resolvePipeline, pipelineOfRun, stagesFor, profileIds,
|
|
6043
6270
|
scenarioProfile, lastKind, lastModifier, modifierSummary, itemScores, scenarioEvals, scenarioPasses, groupPass, overallVerdict, passVerdict, isSkipped, failedScore,
|
|
6044
6271
|
evalLabel, evalsOf, evalsDataset, casesRef, groupOf, groupOfMetrics, pipelineWarnings, isWholeRun, EVAL_FIELDS,
|
|
6045
6272
|
pipelineToYaml, importPipeline, yamlToPipeline, exportBundle, readBundle, bundleProblems, junitXml,
|
|
6046
6273
|
pluginHost, legacyTagsOut, splitItems, tidy, tryExamples, itemRulesProblems, dropBy, rejectBy, matcher,
|
|
6274
|
+
FILTER_SET_VERSION, FILTER_TYPES, FILTER_SEPARATORS, applyFilter, applyFilterSet, tryFilter, splitSample,
|
|
6275
|
+
filterProblems, filterFromItemRule, upgradeFilterSetBody, blankFilterSetBody, filterSetBodyProblems, filterSetLinkProblems,
|
|
6276
|
+
newFilterLink, FILTER_MODIFIER,
|
|
6047
6277
|
};
|