evals-lab 0.11.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.
- package/CHANGELOG.md +47 -0
- package/README.md +4 -4
- package/bin/evals-lab.js +9 -1
- package/bin/lab-file.js +97 -0
- package/bin/run.js +1 -1
- package/lab/VERSION +1 -1
- package/lab/demo/CREDITS.md +37 -3
- package/lab/demo/datasets/demo-1.json +2 -2
- package/lab/demo/datasets/demo-2.json +2 -2
- package/lab/demo/datasets/sentiment.json +383 -0
- package/lab/demo/datasets/weather-alerts.json +61 -0
- package/lab/demo/datasets/wordings.json +39 -0
- package/lab/demo/manifest.json +3 -3
- package/lab/demo/pipelines/demo-1.json +4 -4
- package/lab/demo/pipelines/demo-2.json +4 -4
- package/lab/demo/pipelines/sentiment.json +65 -0
- package/lab/demo/pipelines/weather-alerts.json +66 -0
- package/lab/demo/pipelines/wordings.json +104 -0
- package/lab/demo/sources/Demo 1/source.json +3 -0
- package/lab/demo/sources/Demo 2/source.json +3 -0
- package/lab/demo/sources/sentiment/bakery-croissant.txt +1 -0
- package/lab/demo/sources/sentiment/bus-timetable.txt +1 -0
- package/lab/demo/sources/sentiment/cafe-coffee.txt +1 -0
- package/lab/demo/sources/sentiment/concert-night.txt +1 -0
- package/lab/demo/sources/sentiment/film-sequel.txt +1 -0
- package/lab/demo/sources/sentiment/garden-tomatoes.txt +1 -0
- package/lab/demo/sources/sentiment/gym-contract.txt +1 -0
- package/lab/demo/sources/sentiment/headphones-review.txt +1 -0
- package/lab/demo/sources/sentiment/hotel-room.txt +1 -0
- package/lab/demo/sources/sentiment/library-hours.txt +1 -0
- package/lab/demo/sources/sentiment/meeting-minutes.txt +1 -0
- package/lab/demo/sources/sentiment/parcel-lost.txt +1 -0
- package/lab/demo/sources/sentiment/phone-update.txt +1 -0
- package/lab/demo/sources/sentiment/puppy-home.txt +1 -0
- package/lab/demo/sources/sentiment/recipe-note.txt +1 -0
- package/lab/demo/sources/sentiment/road-closure.txt +1 -0
- package/lab/demo/sources/sentiment/source.json +3 -0
- package/lab/demo/sources/sentiment/teacher-thanks.txt +1 -0
- package/lab/demo/sources/sentiment/train-delay.txt +1 -0
- package/lab/demo/sources/sentiment/volunteer-day.txt +1 -0
- package/lab/demo/sources/sentiment/weather-report.txt +1 -0
- package/lab/demo/sources/weather-alerts/https-api.weather.gov-alerts-types.txt +116 -0
- package/lab/demo/sources/weather-alerts/source.json +8 -0
- package/lab/evals-core.mjs +76 -16
- package/lab/server.py +484 -54
- package/lab/web/dist/assets/gallery-CFntjQFG.js +5 -0
- package/lab/web/dist/assets/main-BlTG2i20.css +1 -0
- package/lab/web/dist/assets/main-D6MNWqak.js +17 -0
- package/lab/web/dist/assets/tokens-DwzzdSIF.css +1 -0
- package/lab/web/dist/assets/tokens-bvuNFuYg.js +60 -0
- package/lab/web/dist/gallery.html +3 -3
- package/lab/web/dist/index.html +4 -4
- package/package.json +1 -1
- package/lab/web/dist/assets/gallery-DKy8QK3o.js +0 -5
- package/lab/web/dist/assets/main-BGmSu7aS.css +0 -1
- package/lab/web/dist/assets/main-DdGON-yl.js +0 -16
- package/lab/web/dist/assets/tokens-BmytrwxE.js +0 -60
- package/lab/web/dist/assets/tokens-DBN7I0ru.css +0 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 17,
|
|
3
|
+
"id": "wordings",
|
|
4
|
+
"name": "Demo: compare wordings",
|
|
5
|
+
"jobs": [
|
|
6
|
+
{
|
|
7
|
+
"type": "job",
|
|
8
|
+
"id": "j1",
|
|
9
|
+
"name": "",
|
|
10
|
+
"steps": [
|
|
11
|
+
{
|
|
12
|
+
"type": "attachContent",
|
|
13
|
+
"content": {
|
|
14
|
+
"type": "prompt"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"type": "readReply",
|
|
19
|
+
"out": {
|
|
20
|
+
"kind": "text"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"targets": [
|
|
27
|
+
{
|
|
28
|
+
"id": "s1",
|
|
29
|
+
"name": "",
|
|
30
|
+
"connection": {
|
|
31
|
+
"id": "model",
|
|
32
|
+
"name": "Model"
|
|
33
|
+
},
|
|
34
|
+
"profile": null,
|
|
35
|
+
"steps": [
|
|
36
|
+
{
|
|
37
|
+
"type": "prompt",
|
|
38
|
+
"prompt": "Which planet is closest to the Sun?"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "s2",
|
|
44
|
+
"name": "",
|
|
45
|
+
"connection": {
|
|
46
|
+
"id": "model",
|
|
47
|
+
"name": "Model"
|
|
48
|
+
},
|
|
49
|
+
"profile": null,
|
|
50
|
+
"steps": [
|
|
51
|
+
{
|
|
52
|
+
"type": "prompt",
|
|
53
|
+
"prompt": "Name the planet nearest the Sun. Reply with its name only."
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "s3",
|
|
59
|
+
"name": "",
|
|
60
|
+
"connection": {
|
|
61
|
+
"id": "model",
|
|
62
|
+
"name": "Model"
|
|
63
|
+
},
|
|
64
|
+
"profile": null,
|
|
65
|
+
"steps": [
|
|
66
|
+
{
|
|
67
|
+
"type": "prompt",
|
|
68
|
+
"prompt": "Of the eight planets in our solar system, which one orbits closest to the Sun? Answer in one word."
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"id": "s4",
|
|
74
|
+
"name": "",
|
|
75
|
+
"connection": {
|
|
76
|
+
"id": "model",
|
|
77
|
+
"name": "Model"
|
|
78
|
+
},
|
|
79
|
+
"profile": null,
|
|
80
|
+
"steps": [
|
|
81
|
+
{
|
|
82
|
+
"type": "prompt",
|
|
83
|
+
"prompt": "Quick question: what's the innermost planet?"
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"evals": [
|
|
89
|
+
{
|
|
90
|
+
"id": "t1",
|
|
91
|
+
"name": "",
|
|
92
|
+
"type": "group",
|
|
93
|
+
"continueOnFailure": true,
|
|
94
|
+
"group": {
|
|
95
|
+
"id": "wordings",
|
|
96
|
+
"name": "Demo: compare wordings"
|
|
97
|
+
},
|
|
98
|
+
"pin": null
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"pass": {
|
|
102
|
+
"mode": "all"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Stopped at the new bakery on Elm Street this morning. The croissant was still warm, flaky all the way through, and the woman at the till remembered my name from last week. I'll be back tomorrow.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Route 12 now leaves the station every twenty minutes between seven and seven, and every forty minutes in the evening. The last bus is at eleven fifteen.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Waited twenty-five minutes for a lukewarm flat white that tasted burnt. The staff were chatting the whole time and nobody apologised.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The band played for nearly three hours and the whole crowd sang the last song back to them. My voice is gone today and I would do it all again.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The sequel takes everything that made the first film charming and buries it under two and a half hours of noise. I checked my watch more than the screen.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Picked the first tomatoes of the summer today, and they were worth every evening of watering. Sweet, a little sharp, and still warm from the sun.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Cancelling my gym membership took four phone calls, a letter by recorded post and a fee nobody mentioned when I signed up. Avoid.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Six months in and these headphones still last two full days on a charge. The noise cancelling turns my open-plan office into a quiet room. Best thing I bought this year.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The website promised a sea view. Our window faced a car park and the air conditioner rattled all night. When we asked to move, the desk said nothing else was free.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
From the first of next month, the library will open at ten on weekdays instead of nine. Weekend hours stay the same. Returns can still be left in the box by the door at any time.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The committee met on Tuesday. The budget for the spring fair was approved as proposed. The next meeting is on the fourteenth, in the community hall.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The tracking says delivered, but nothing came. Customer service closed my complaint without reply, twice. Forty pounds gone and nobody will even answer.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Since last night's update my phone drains from full to empty by lunchtime, and half my apps crash on opening. I wish I had never pressed install.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
We brought our puppy home on Saturday. She has already learned to sit, chewed one slipper, and fallen asleep on my feet every evening. We adore her.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Preheat the oven to 180 degrees. Mix the flour, sugar and butter until it looks like breadcrumbs, then add the egg and press into the tin.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Mill Lane will be closed between the bridge and the school from Monday for two weeks while the water main is replaced. A diversion will be signed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Mr Okafor stayed after class every Thursday for a term to help me with algebra. I passed the exam last week with a grade I never thought I'd get. Thank you, sir.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Third cancelled train this week. No announcement, no replacement bus, just a blank board and a crowd of us standing in the rain. I was an hour late for work again.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Thirty of us turned out to clear the riverbank on Sunday. We filled forty bags, met neighbours we'd never spoken to, and finished with tea and cake. A lovely day.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Tomorrow will be cloudy in the morning with sunny spells later. Highs of seventeen degrees, with a light westerly wind.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [],
|
|
3
|
+
"eventTypes": [
|
|
4
|
+
"911 Telephone Outage",
|
|
5
|
+
"Administrative Message",
|
|
6
|
+
"Air Quality Alert",
|
|
7
|
+
"Air Stagnation Advisory",
|
|
8
|
+
"Ashfall Advisory",
|
|
9
|
+
"Ashfall Warning",
|
|
10
|
+
"Avalanche Advisory",
|
|
11
|
+
"Avalanche Warning",
|
|
12
|
+
"Avalanche Watch",
|
|
13
|
+
"Beach Hazards Statement",
|
|
14
|
+
"Blizzard Warning",
|
|
15
|
+
"Blowing Dust Advisory",
|
|
16
|
+
"Blowing Dust Warning",
|
|
17
|
+
"Blue Alert",
|
|
18
|
+
"Brisk Wind Advisory",
|
|
19
|
+
"Child Abduction Emergency",
|
|
20
|
+
"Civil Danger Warning",
|
|
21
|
+
"Civil Emergency Message",
|
|
22
|
+
"Coastal Flood Advisory",
|
|
23
|
+
"Coastal Flood Statement",
|
|
24
|
+
"Coastal Flood Warning",
|
|
25
|
+
"Coastal Flood Watch",
|
|
26
|
+
"Cold Weather Advisory",
|
|
27
|
+
"Dense Fog Advisory",
|
|
28
|
+
"Dense Smoke Advisory",
|
|
29
|
+
"Dust Advisory",
|
|
30
|
+
"Dust Storm Warning",
|
|
31
|
+
"Earthquake Warning",
|
|
32
|
+
"Evacuation Immediate",
|
|
33
|
+
"Extreme Heat Warning",
|
|
34
|
+
"Extreme Heat Watch",
|
|
35
|
+
"Extreme Cold Warning",
|
|
36
|
+
"Extreme Cold Watch",
|
|
37
|
+
"Extreme Fire Danger",
|
|
38
|
+
"Extreme Wind Warning",
|
|
39
|
+
"Fire Warning",
|
|
40
|
+
"Fire Weather Watch",
|
|
41
|
+
"Flash Flood Statement",
|
|
42
|
+
"Flash Flood Warning",
|
|
43
|
+
"Flash Flood Watch",
|
|
44
|
+
"Flood Advisory",
|
|
45
|
+
"Flood Statement",
|
|
46
|
+
"Flood Warning",
|
|
47
|
+
"Flood Watch",
|
|
48
|
+
"Freeze Warning",
|
|
49
|
+
"Freeze Watch",
|
|
50
|
+
"Freezing Fog Advisory",
|
|
51
|
+
"Freezing Spray Advisory",
|
|
52
|
+
"Frost Advisory",
|
|
53
|
+
"Gale Warning",
|
|
54
|
+
"Gale Watch",
|
|
55
|
+
"Hazardous Materials Warning",
|
|
56
|
+
"Hazardous Seas Warning",
|
|
57
|
+
"Hazardous Seas Watch",
|
|
58
|
+
"Hazardous Weather Outlook",
|
|
59
|
+
"Heat Advisory",
|
|
60
|
+
"Heavy Freezing Spray Warning",
|
|
61
|
+
"Heavy Freezing Spray Watch",
|
|
62
|
+
"High Surf Advisory",
|
|
63
|
+
"High Surf Warning",
|
|
64
|
+
"High Wind Warning",
|
|
65
|
+
"High Wind Watch",
|
|
66
|
+
"Hurricane Force Wind Warning",
|
|
67
|
+
"Hurricane Force Wind Watch",
|
|
68
|
+
"Hurricane Warning",
|
|
69
|
+
"Hurricane Watch",
|
|
70
|
+
"Hydrologic Outlook",
|
|
71
|
+
"Ice Storm Warning",
|
|
72
|
+
"Lake Effect Snow Warning",
|
|
73
|
+
"Lake Wind Advisory",
|
|
74
|
+
"Lakeshore Flood Advisory",
|
|
75
|
+
"Lakeshore Flood Statement",
|
|
76
|
+
"Lakeshore Flood Warning",
|
|
77
|
+
"Lakeshore Flood Watch",
|
|
78
|
+
"Law Enforcement Warning",
|
|
79
|
+
"Local Area Emergency",
|
|
80
|
+
"Low Water Advisory",
|
|
81
|
+
"Marine Weather Statement",
|
|
82
|
+
"Nuclear Power Plant Warning",
|
|
83
|
+
"Radiological Hazard Warning",
|
|
84
|
+
"Red Flag Warning",
|
|
85
|
+
"Rip Current Statement",
|
|
86
|
+
"Severe Thunderstorm Warning",
|
|
87
|
+
"Severe Thunderstorm Watch",
|
|
88
|
+
"Severe Weather Statement",
|
|
89
|
+
"Shelter In Place Warning",
|
|
90
|
+
"Short Term Forecast",
|
|
91
|
+
"Small Craft Advisory",
|
|
92
|
+
"Snow Squall Warning",
|
|
93
|
+
"Special Marine Warning",
|
|
94
|
+
"Special Weather Statement",
|
|
95
|
+
"Storm Surge Warning",
|
|
96
|
+
"Storm Surge Watch",
|
|
97
|
+
"Storm Warning",
|
|
98
|
+
"Storm Watch",
|
|
99
|
+
"Test",
|
|
100
|
+
"Tornado Warning",
|
|
101
|
+
"Tornado Watch",
|
|
102
|
+
"Tropical Cyclone Local Statement",
|
|
103
|
+
"Tropical Storm Warning",
|
|
104
|
+
"Tropical Storm Watch",
|
|
105
|
+
"Tsunami Advisory",
|
|
106
|
+
"Tsunami Warning",
|
|
107
|
+
"Tsunami Watch",
|
|
108
|
+
"Typhoon Warning",
|
|
109
|
+
"Typhoon Watch",
|
|
110
|
+
"Volcano Warning",
|
|
111
|
+
"Wind Advisory",
|
|
112
|
+
"Winter Storm Warning",
|
|
113
|
+
"Winter Storm Watch",
|
|
114
|
+
"Winter Weather Advisory"
|
|
115
|
+
]
|
|
116
|
+
}
|
package/lab/evals-core.mjs
CHANGED
|
@@ -166,13 +166,11 @@ import list, { ECHO, CASE_MODES, legacyTagsOut, legacyTagsModifiers, cleaningFil
|
|
|
166
166
|
/** A target step that sends a Power Automate step's request
|
|
167
167
|
(docs/power-automate.md § The HTTP Request step): the flow's own template
|
|
168
168
|
-- method, path, query and body, Workflow Definition Language expressions
|
|
169
|
-
and all -- with the target's words and fields placed in the body by
|
|
170
|
-
HTTP API, evaluated against each item's record.
|
|
171
|
-
address and the key. */
|
|
169
|
+
and all -- with the target's words and fields placed in the body by the
|
|
170
|
+
job's HTTP API (its flow step's), evaluated against each item's record.
|
|
171
|
+
The profile gives the address and the key. */
|
|
172
172
|
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
|
|
176
174
|
|
|
177
175
|
|
|
178
176
|
|
|
@@ -240,7 +238,7 @@ import list, { ECHO, CASE_MODES, legacyTagsOut, legacyTagsModifiers, cleaningFil
|
|
|
240
238
|
|
|
241
239
|
|
|
242
240
|
/** What a target sends in one job: one of the target step types. */
|
|
243
|
-
|
|
241
|
+
|
|
244
242
|
|
|
245
243
|
/** A target (pipeline-model §16): one column through every job, compared
|
|
246
244
|
side by side in Results -- what version 9 called a scenario. Its step in
|
|
@@ -1719,6 +1717,10 @@ function datasetRules(body ) {
|
|
|
1719
1717
|
|
|
1720
1718
|
|
|
1721
1719
|
|
|
1720
|
+
|
|
1721
|
+
|
|
1722
|
+
|
|
1723
|
+
|
|
1722
1724
|
|
|
1723
1725
|
|
|
1724
1726
|
|
|
@@ -2170,6 +2172,7 @@ CONNECTION_TYPES.anthropic = {
|
|
|
2170
2172
|
// Asked words (a Prompt step, a grader) or sent a flow's whole request (an
|
|
2171
2173
|
// HTTP Request step): one profile and one key do both.
|
|
2172
2174
|
answers: ["prompt", "request"],
|
|
2175
|
+
speaks: () => "anthropic",
|
|
2173
2176
|
// The Messages API: the x-api-key and anthropic-version headers, base64
|
|
2174
2177
|
// image content blocks, and /v1/models for the list.
|
|
2175
2178
|
request(conn, prompt, dataUrl, base, key) {
|
|
@@ -2501,8 +2504,17 @@ CONNECTION_TYPES.http = {
|
|
|
2501
2504
|
{ key: "headers", label: "Headers", control: "text", default: "", hint: "", scope: "connection" },
|
|
2502
2505
|
// What Test connection asks of it: a GET, keyed as a run is.
|
|
2503
2506
|
{ key: "testPath", label: "Test path", control: "text", default: "", hint: "", scope: "connection" },
|
|
2507
|
+
// Read off the address (httpApiFor); set by hand for one the lab cannot
|
|
2508
|
+
// tell, such as a gateway in front of a provider.
|
|
2509
|
+
{ key: "requestFormat", label: "Request format", control: "select", default: "", hint: "", scope: "connection",
|
|
2510
|
+
choices: [{ value: "", label: "From address" },
|
|
2511
|
+
...Object.entries(HTTP_APIS).map(([value, a]) => ({ value, label: a.label }))] },
|
|
2504
2512
|
],
|
|
2505
2513
|
answers: ["request"],
|
|
2514
|
+
speaks(conn) {
|
|
2515
|
+
const api = String(conn.requestFormat || "") || httpApiFor(originBase(conn.url));
|
|
2516
|
+
return HTTP_APIS[api] && api !== "raw" ? api : null;
|
|
2517
|
+
},
|
|
2506
2518
|
lists: false,
|
|
2507
2519
|
baseOf: originBase,
|
|
2508
2520
|
send(conn, req, base, key) {
|
|
@@ -3297,7 +3309,7 @@ function applyModifiers (list , kind ,
|
|
|
3297
3309
|
// 16: a target names an AI Connection and, optionally, a Connection Profile,
|
|
3298
3310
|
// where it named one Target profile; a run's table is keyed by the pair
|
|
3299
3311
|
// (answerKey) -- the Target profile split in the store as it opens (#445).
|
|
3300
|
-
const PIPELINE_VERSION =
|
|
3312
|
+
const PIPELINE_VERSION = 17 ;
|
|
3301
3313
|
|
|
3302
3314
|
// Plain objects, so an entry is added by assignment and a reader never needs
|
|
3303
3315
|
// to know which registered it.
|
|
@@ -4818,16 +4830,18 @@ STEP_TYPES.httpRequest = {
|
|
|
4818
4830
|
label: "HTTP Request", slot: "target", asks: "request", verbatim: true, modelFrom: "step",
|
|
4819
4831
|
description: "Sends a Power Automate step's own request, rebuilt from each record, with each target's words in it.",
|
|
4820
4832
|
firstJobOnly: "an HTTP Request is sent from its item's record, so it is job 1's",
|
|
4821
|
-
// A new target starts from the flow's own words and fields
|
|
4822
|
-
|
|
4833
|
+
// A new target starts from the flow's own words and fields, read by the
|
|
4834
|
+
// job's HTTP API ([step] carries it: requestStepOf).
|
|
4835
|
+
newStep: (step) => ({ type: "httpRequest", method: step.method, path: step.path,
|
|
4823
4836
|
query: clone(step.query), body: clone(step.body),
|
|
4824
4837
|
...(HTTP_APIS[step.api] ?? HTTP_APIS.raw ).cellOf(step.body) }),
|
|
4825
4838
|
in: "item", out: "text",
|
|
4826
4839
|
apply: "runPipeline",
|
|
4827
|
-
fields: ["type", "
|
|
4840
|
+
fields: ["type", "method", "path", "query", "body", "prompt", "connection", "profile", "from", "system", "model", "settings", "prefill"],
|
|
4841
|
+
// [step] carries its job's HTTP API, where the job has a flow step to say
|
|
4842
|
+
// it (validatePipeline); the flow step's own check names an unknown one.
|
|
4828
4843
|
validate(step, _ctx, bad, at){
|
|
4829
4844
|
const api = HTTP_APIS[step.api];
|
|
4830
|
-
if (!api) bad.push(`${at}: "${step.api}" is not an HTTP API this lab has`);
|
|
4831
4845
|
if (!HTTP_METHODS.includes(step.method)) bad.push(`${at}: the method is one of ${HTTP_METHODS.join(", ")}`);
|
|
4832
4846
|
if (!isStr(step.path) || !/^[/@]/.test(step.path)) bad.push(`${at}: the path starts with /`);
|
|
4833
4847
|
if (!isObj(step.query) || !Object.values(step.query).every(isStr)) bad.push(`${at}: the query is names to text`);
|
|
@@ -5104,7 +5118,7 @@ function requestStepOf(doc
|
|
|
5104
5118
|
|
|
5105
5119
|
/** What httpRequestOf builds a request from: a target's template, and the
|
|
5106
5120
|
loop its flow step sits in. */
|
|
5107
|
-
|
|
5121
|
+
|
|
5108
5122
|
/** What httpReplyOf reads a reply with: the flow step, its API, its reading. */
|
|
5109
5123
|
|
|
5110
5124
|
|
|
@@ -5706,10 +5720,14 @@ function upgradePipeline (doc , ctx = {}) {
|
|
|
5706
5720
|
}
|
|
5707
5721
|
return localSteps(ownBodiesNow(out), ctx) ;
|
|
5708
5722
|
}
|
|
5709
|
-
if (!isObj(doc) || ![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].includes(doc.version )) return doc;
|
|
5723
|
+
if (!isObj(doc) || ![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].includes(doc.version )) return doc;
|
|
5724
|
+
// Every version reads as 16 first, then as 17: its requests read by their
|
|
5725
|
+
// job's HTTP API (requestsOnJobApi).
|
|
5726
|
+
const seventeen = (d ) => localSteps(ownBodiesNow(requestsOnJobApi(d)), ctx) ;
|
|
5727
|
+
if (doc.version === 16) return seventeen(cutRefs(clone(doc) ));
|
|
5710
5728
|
// Every version reads as 15 first, then as 16: its Target profiles split
|
|
5711
5729
|
// into AI Connections and Connection Profiles (answersFromProfiles).
|
|
5712
|
-
const sixteen = (fifteen ) =>
|
|
5730
|
+
const sixteen = (fifteen ) => seventeen(answersFromProfiles(fifteen, ctx));
|
|
5713
5731
|
if (doc.version === 15) return sixteen(cutRefs(clone(doc) ));
|
|
5714
5732
|
// Versions 13 and 14 read as 15 by migrating a job's cleaning modifier steps
|
|
5715
5733
|
// and its List `preamble` setting into Pre-/Post-format filter sets
|
|
@@ -5854,6 +5872,37 @@ function localSteps(doc , ctx ) {
|
|
|
5854
5872
|
return next;
|
|
5855
5873
|
}
|
|
5856
5874
|
|
|
5875
|
+
/**
|
|
5876
|
+
* Version 16 to 17 (#477): a request step has no HTTP API of its own; its
|
|
5877
|
+
* job's flow step says it. A step that named another -- Any endpoint over an
|
|
5878
|
+
* Anthropic flow, say -- keeps its body as it would have sent it, read again
|
|
5879
|
+
* by the job's (its words and fields as that API holds them). A request in a
|
|
5880
|
+
* job with no flow step, which could never run, is a Prompt keeping its words.
|
|
5881
|
+
*/
|
|
5882
|
+
function requestsOnJobApi(next ) {
|
|
5883
|
+
next.version = PIPELINE_VERSION;
|
|
5884
|
+
const jobs = Array.isArray(next.jobs) ? next.jobs : [];
|
|
5885
|
+
const step = (st , k ) => {
|
|
5886
|
+
if (!isObj(st) || st.type !== "httpRequest") return st;
|
|
5887
|
+
const { api: own, ...rest } = st;
|
|
5888
|
+
const flow = isObj(jobs[k]) ? flowStepOf(jobs[k] ) : null;
|
|
5889
|
+
if (!flow) {
|
|
5890
|
+
return { type: "prompt", prompt: rest.prompt,
|
|
5891
|
+
...Object.fromEntries(["connection", "profile", "from"].filter(f => rest[f] != null).map(f => [f, rest[f]])) };
|
|
5892
|
+
}
|
|
5893
|
+
const was = HTTP_APIS[own ], now = HTTP_APIS[flow.api];
|
|
5894
|
+
if (!was || !now || own === flow.api) return rest;
|
|
5895
|
+
let body = rest.body;
|
|
5896
|
+
try { body = was.place(rest.body, rest ); } catch { /* words that never placed: the template stands */ }
|
|
5897
|
+
const { system: _s, model: _m, settings: _t, prefill: _p, ...kept } = rest;
|
|
5898
|
+
return { ...kept, body, ...now.cellOf(body) };
|
|
5899
|
+
};
|
|
5900
|
+
if (Array.isArray(next.targets)) {
|
|
5901
|
+
next.targets = next.targets.map((t ) => (isObj(t) && Array.isArray(t.steps) ? { ...t, steps: t.steps.map(step) } : t));
|
|
5902
|
+
}
|
|
5903
|
+
return next;
|
|
5904
|
+
}
|
|
5905
|
+
|
|
5857
5906
|
/** [next] as version 9 held it, from any version before 10. */
|
|
5858
5907
|
function nineOf(next , ctx ) {
|
|
5859
5908
|
if (next.version === 9) { profileRefs(next); return next; }
|
|
@@ -5975,7 +6024,9 @@ function validatePipeline(input , ctx = {}) {
|
|
|
5975
6024
|
if (entry.local && (st.profile != null || st.connection != null)) return void bad.push(`${where}: ${entry.label} asks no connection`);
|
|
5976
6025
|
if (k > 0 && entry.firstJobOnly) return void bad.push(`${where}: ${entry.firstJobOnly}`);
|
|
5977
6026
|
onlyFields(st, where, entry.fields ?? [], bad);
|
|
5978
|
-
|
|
6027
|
+
// A request is read and placed by its job's HTTP API.
|
|
6028
|
+
const flow = entry.asks === "request" ? flowStepOf(doc.jobs[k]) : null;
|
|
6029
|
+
entry.validate(flow ? { ...st, api: flow.api } : st, c, bad, where);
|
|
5979
6030
|
answerRefsProblems(st, where, bad);
|
|
5980
6031
|
if (st.from != null && !isRef(st.from)) bad.push(`${where} has to name the prompt it was picked from as { id, name }`);
|
|
5981
6032
|
// A request is a flow step's, rebuilt from its records, and its
|
|
@@ -6050,6 +6101,15 @@ function validatePipeline(input , ctx = {}) {
|
|
|
6050
6101
|
const type = CONNECTION_TYPES[typeOf(p )];
|
|
6051
6102
|
if (type && !(type.answers ?? ["prompt"]).includes(step.asks ?? "prompt")) {
|
|
6052
6103
|
bad.push(`${n > 1 ? `${jobLabel(doc, k)}, ` : ""}${targetLabel(doc, i)}: ${step.label ?? "this step"} needs a profile that answers it, and ${type.label} does not`);
|
|
6104
|
+
return;
|
|
6105
|
+
}
|
|
6106
|
+
// A request goes as the flow wrote it, so its connection has to speak
|
|
6107
|
+
// the job's HTTP API; one the lab cannot tell (Any endpoint) passes.
|
|
6108
|
+
const api = step.asks === "request" ? flowStepOf(doc.jobs[k])?.api : undefined;
|
|
6109
|
+
const speaks = api && api !== "raw" && type?.speaks ? type.speaks(p ) : null;
|
|
6110
|
+
if (api && speaks && speaks !== api) {
|
|
6111
|
+
const name = targetAnswerOf(doc, i, k)?.connection.name || type .label;
|
|
6112
|
+
bad.push(`${n > 1 ? `${jobLabel(doc, k)}, ` : ""}${targetLabel(doc, i)}: ${name} can't send an ${HTTP_APIS[api]?.label ?? api} request`);
|
|
6053
6113
|
}
|
|
6054
6114
|
});
|
|
6055
6115
|
// The model is the profile's unless the target's step carries it.
|
|
@@ -7119,7 +7179,7 @@ export {
|
|
|
7119
7179
|
CONNECTION_FIELDS, SETTING_KEYS, OPTION_FIELDS, jobLabel, scenarioLabel, jobWords, versionProblem,
|
|
7120
7180
|
contentOf, withContent, replyOf,
|
|
7121
7181
|
targetsOf, targetLetter, targetLabel, targetStepOf, targetEntryOf, jobTargetType, targetAnswerOf, targetAnswerKey, recordedTargetOf, withTarget,
|
|
7122
|
-
tokensOf, withTokens, sendsImage, withImage, flowStepOf, readAsOf, outOf, withOut, slotEntry, SLOTS,
|
|
7182
|
+
tokensOf, withTokens, sendsImage, withImage, flowStepOf, requestStepOf, readAsOf, outOf, withOut, slotEntry, SLOTS,
|
|
7123
7183
|
filterSetsOf, withFilterSets, stageModifiers, beforeFilters,
|
|
7124
7184
|
blankPipeline, upgradePipeline, fatProfileRef, newId, newEval, newLink, validatePipeline, resolvePipeline, pipelineOfRun, stagesFor, profileIds, answerKeys,
|
|
7125
7185
|
scenarioProfile, lastKind, lastModifier, modifierSummary, itemScores, scenarioEvals, scenarioPasses, groupPass, overallVerdict, passVerdict, isSkipped, failedScore,
|