evals-lab 0.1.3 → 0.2.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 +95 -1
- package/bin/evals-lab.js +10 -2
- package/lab/VERSION +1 -1
- package/lab/demo/pipelines/demo-1.json +34 -24
- package/lab/demo/pipelines/demo-2.json +34 -24
- package/lab/evals-core.mjs +701 -284
- package/lab/run-evals.js +42 -16
- package/lab/server.py +339 -80
- package/lab/web/dist/assets/gallery-DsetJSXv.js +3 -0
- package/lab/web/dist/assets/{main-DjQQums6.css → main-B-VtDGxC.css} +1 -1
- package/lab/web/dist/assets/main-ERoD2Kll.js +19 -0
- package/lab/web/dist/assets/tokens-ClRQ7Mui.js +51 -0
- package/lab/web/dist/assets/tokens-D3C8O2Ib.css +1 -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-DipkRvqJ.js +0 -3
- package/lab/web/dist/assets/main-HJGRUBM-.js +0 -18
- package/lab/web/dist/assets/tokens-B9intIuT.js +0 -51
- package/lab/web/dist/assets/tokens-s6I-RMVq.css +0 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install -g evals-lab
|
|
|
13
13
|
evals-lab
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Then open <http://
|
|
16
|
+
Then open <http://localhost:8080>. A new lab opens on two demo datasets, each
|
|
17
17
|
with its images and a starter pipeline. The first start takes a little longer
|
|
18
18
|
than later ones while it installs them.
|
|
19
19
|
|
|
@@ -61,12 +61,106 @@ evals-lab [--port <n>] [--host <address>] [--data-dir <dir>]
|
|
|
61
61
|
| `--data-dir` | `DATA_DIR` | the directory above |
|
|
62
62
|
| | `LAB_PASSWORD` | none |
|
|
63
63
|
| | `OLLAMA_URL` | `http://127.0.0.1:11434` |
|
|
64
|
+
| | `M365_CLIENT_ID`, `M365_TENANT` | none; see Power Automate below |
|
|
64
65
|
| | `PYTHON` | `py -3`, `python` or `python3`, whichever is found |
|
|
65
66
|
|
|
66
67
|
`OLLAMA_URL` is only the connection a new lab starts with. Every other
|
|
67
68
|
connection — an OpenAI-compatible endpoint, Anthropic, llama.cpp — is a Setup
|
|
68
69
|
profile you add in the page.
|
|
69
70
|
|
|
71
|
+
## Power Automate (Microsoft 365)
|
|
72
|
+
|
|
73
|
+
The lab can read a Power Automate cloud flow, and its run history, from your
|
|
74
|
+
organisation's Microsoft 365 tenant, then grade the flow's HTTP step against
|
|
75
|
+
the same model and prompt. The sign-in stays in your browser tab: the lab's
|
|
76
|
+
server never receives a Microsoft token.
|
|
77
|
+
|
|
78
|
+
It needs a work or school account. Microsoft retired cloud flows for personal
|
|
79
|
+
accounts (outlook.com, hotmail.com) in July 2025, so a personal account cannot
|
|
80
|
+
be used. For a small business, a trial or a Microsoft 365 Developer Program
|
|
81
|
+
tenant, follow the same steps as a company.
|
|
82
|
+
|
|
83
|
+
### Register an app in your tenant
|
|
84
|
+
|
|
85
|
+
An administrator of the tenant registers it once, at
|
|
86
|
+
<https://entra.microsoft.com>:
|
|
87
|
+
|
|
88
|
+
1. **App registrations › New registration.**
|
|
89
|
+
- Supported account types: *Accounts in this organizational directory only*.
|
|
90
|
+
- Redirect URI: platform **Single-page application**, value `http://localhost/`.
|
|
91
|
+
Microsoft ignores the port on a `localhost` redirect, so this covers
|
|
92
|
+
`--port` too. A lab served on another address needs that address here
|
|
93
|
+
as well, over `https`.
|
|
94
|
+
2. **API permissions › Add a permission › APIs my organization uses.** Search
|
|
95
|
+
for *Microsoft Flow Service* and add the delegated permission
|
|
96
|
+
**Flows.Read.All**. If the service isn't listed, open
|
|
97
|
+
<https://make.powerautomate.com> once, then search again.
|
|
98
|
+
3. **Grant admin consent** for the tenant.
|
|
99
|
+
4. No client secret or certificate is needed: the lab signs in as a public
|
|
100
|
+
client.
|
|
101
|
+
|
|
102
|
+
### Tell the lab about it
|
|
103
|
+
|
|
104
|
+
In the lab, open **Setup › Connections**, then use the Microsoft 365 row's
|
|
105
|
+
menu › **Configure…**. Enter:
|
|
106
|
+
|
|
107
|
+
| | |
|
|
108
|
+
|---|---|
|
|
109
|
+
| Client ID | the app's *Application (client) ID* |
|
|
110
|
+
| Tenant | the *Directory (tenant) ID*, or one of the tenant's domains |
|
|
111
|
+
|
|
112
|
+
Fill in Tenant for an app registered as above. Left empty, it means any
|
|
113
|
+
organisation's tenant, which only a multitenant app accepts: Microsoft
|
|
114
|
+
refuses the sign-in of a single-tenant app with `AADSTS50194`.
|
|
115
|
+
|
|
116
|
+
Both are saved as you type, and neither is secret. **Sign in** from the same
|
|
117
|
+
menu, then add a Source in the Sources tab with **Add source › Power Automate
|
|
118
|
+
workflow**.
|
|
119
|
+
|
|
120
|
+
To set the app for the whole machine instead, start the lab with
|
|
121
|
+
`M365_CLIENT_ID` and `M365_TENANT` set. Setup then shows those values and
|
|
122
|
+
can't change them.
|
|
123
|
+
|
|
124
|
+
Open the lab at `localhost`, the address `evals-lab` prints. A sign-in begun
|
|
125
|
+
at `127.0.0.1` reloads the page at `localhost`, the address registered above,
|
|
126
|
+
and carries on from there. Anything open in the tab, such as an Add source
|
|
127
|
+
dialog, does not carry over.
|
|
128
|
+
|
|
129
|
+
## Google Drive
|
|
130
|
+
|
|
131
|
+
The lab can sign in to Google Drive, so a Source can read files from a Drive
|
|
132
|
+
folder. The sign-in is held by the lab's server, on your machine, and it can
|
|
133
|
+
reach only the files you pick: the lab asks for `drive.file` access, never
|
|
134
|
+
your whole Drive.
|
|
135
|
+
|
|
136
|
+
Open **Setup › Connections**, then use the Google Drive row's menu ›
|
|
137
|
+
**Sign in**. If the row says *Not Configured*, give the lab a Google app
|
|
138
|
+
first, once. These steps are provisional: they haven't yet been tried
|
|
139
|
+
against a real Google project, and the Drive Source that uses the sign-in
|
|
140
|
+
is still to come.
|
|
141
|
+
|
|
142
|
+
1. At <https://console.cloud.google.com>, make a project. Enable the
|
|
143
|
+
**Google Drive API** and the **Google Picker API**.
|
|
144
|
+
2. **OAuth consent screen:** choose *External*. Give the app a name and your
|
|
145
|
+
email, add the scope `.../auth/drive.file`, then **Publish app**. It needs
|
|
146
|
+
no review from Google, because that scope is non-sensitive. Left in
|
|
147
|
+
*Testing*, a sign-in lapses after 7 days.
|
|
148
|
+
3. **Credentials › Create credentials › OAuth client ID**, of type
|
|
149
|
+
**Desktop app**. Download its JSON file.
|
|
150
|
+
4. **Credentials › Create credentials › API key.** Restrict it to the Google
|
|
151
|
+
Picker API.
|
|
152
|
+
5. In the lab, use the Google Drive row's menu › **Configure…**, then
|
|
153
|
+
**Load client file…**, and choose the file from step 3. Paste the key
|
|
154
|
+
from step 4 into **API key**.
|
|
155
|
+
|
|
156
|
+
A Desktop app needs no redirect registered: Google sends the browser back to
|
|
157
|
+
any port on this machine. A lab you reach at its own address, from another
|
|
158
|
+
machine, needs a *Web application* client instead. Choose that type in
|
|
159
|
+
Configure…, then register the **Redirect URI** it shows with the client.
|
|
160
|
+
|
|
161
|
+
Everything saves as you type. The client's secret is kept by the lab and
|
|
162
|
+
never shown again. The window says only that one is saved.
|
|
163
|
+
|
|
70
164
|
## Reaching it from another machine
|
|
71
165
|
|
|
72
166
|
By default only your own machine can reach the lab. That is deliberate: the
|
package/bin/evals-lab.js
CHANGED
|
@@ -53,6 +53,13 @@ function isLoopback(host) {
|
|
|
53
53
|
return host === "localhost" || /^127(?:\.\d{1,3}){3}$/.test(host);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
// The address to open the lab at. Loopback reads as localhost: it is the one
|
|
57
|
+
// http address Entra takes as a sign-in redirect from its portal, on any port,
|
|
58
|
+
// so a Microsoft 365 sign-in works from the address printed (npm/README.md).
|
|
59
|
+
function advertised(host, port) {
|
|
60
|
+
return `http://${host === "127.0.0.1" ? "localhost" : host}:${port}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
// What the command line and the environment ask for, or an Error saying what
|
|
57
64
|
// is wrong with it.
|
|
58
65
|
function options(argv, env = process.env, platform = process.platform, home = os.homedir()) {
|
|
@@ -191,11 +198,12 @@ async function main() {
|
|
|
191
198
|
process.on("SIGTERM", () => { stopping = true; child.kill("SIGTERM"); });
|
|
192
199
|
|
|
193
200
|
const address = `http://${o.host}:${o.port}`;
|
|
201
|
+
const open = advertised(o.host, o.port);
|
|
194
202
|
// A first start installs the demo pack before it listens, which takes longer
|
|
195
203
|
// than any later one.
|
|
196
204
|
for (let i = 0; i < 1200; i++) {
|
|
197
205
|
if (await answers(`${address}/healthz`)) {
|
|
198
|
-
console.log(`\nevals-lab ${version()} is running at ${
|
|
206
|
+
console.log(`\nevals-lab ${version()} is running at ${open}\n data: ${o.dataDir}\n stop: Ctrl+C`);
|
|
199
207
|
return;
|
|
200
208
|
}
|
|
201
209
|
await new Promise(r => setTimeout(r, 250));
|
|
@@ -204,4 +212,4 @@ async function main() {
|
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
if (require.main === module) main();
|
|
207
|
-
module.exports = { dataDir, isLoopback, options, pythonCandidates, findPython, atLeast, PYTHON_FLOOR };
|
|
215
|
+
module.exports = { dataDir, isLoopback, advertised, options, pythonCandidates, findPython, atLeast, PYTHON_FLOOR };
|
package/lab/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.2.0 (2026.10.02-323)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 10,
|
|
3
3
|
"id": "demo-1",
|
|
4
4
|
"name": "Demo 1",
|
|
5
5
|
"jobs": [
|
|
@@ -21,38 +21,46 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
|
-
"type": "
|
|
25
|
-
"withImage": true,
|
|
26
|
-
"tokenMappings": []
|
|
24
|
+
"type": "attachImage"
|
|
27
25
|
},
|
|
28
26
|
{
|
|
29
27
|
"type": "readReply",
|
|
30
28
|
"out": {
|
|
31
29
|
"kind": "list",
|
|
32
30
|
"parse": "csv",
|
|
33
|
-
"preamble": false
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
"preamble": false
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "modifier",
|
|
36
|
+
"modifier": {
|
|
37
|
+
"type": "tidy"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "modifier",
|
|
42
|
+
"modifier": {
|
|
43
|
+
"type": "dedupe"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "modifier",
|
|
48
|
+
"modifier": {
|
|
49
|
+
"type": "keep-first",
|
|
50
|
+
"n": 12
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"type": "modifier",
|
|
55
|
+
"modifier": {
|
|
56
|
+
"type": "case",
|
|
57
|
+
"mode": "LOWER"
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
]
|
|
53
61
|
}
|
|
54
62
|
],
|
|
55
|
-
"
|
|
63
|
+
"targets": [
|
|
56
64
|
{
|
|
57
65
|
"id": "s1",
|
|
58
66
|
"name": "",
|
|
@@ -60,8 +68,9 @@
|
|
|
60
68
|
"id": "vision",
|
|
61
69
|
"name": "Vision"
|
|
62
70
|
},
|
|
63
|
-
"
|
|
71
|
+
"steps": [
|
|
64
72
|
{
|
|
73
|
+
"type": "prompt",
|
|
65
74
|
"prompt": "List the things you can see in this image, as a comma-separated list of short items (one to three words each), most important first. Up to 12 items."
|
|
66
75
|
}
|
|
67
76
|
]
|
|
@@ -73,8 +82,9 @@
|
|
|
73
82
|
"id": "vision",
|
|
74
83
|
"name": "Vision"
|
|
75
84
|
},
|
|
76
|
-
"
|
|
85
|
+
"steps": [
|
|
77
86
|
{
|
|
87
|
+
"type": "prompt",
|
|
78
88
|
"prompt": "Name what is in this image. Reply with a comma-separated list only: up to 12 short items, most important first."
|
|
79
89
|
}
|
|
80
90
|
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 10,
|
|
3
3
|
"id": "demo-2",
|
|
4
4
|
"name": "Demo 2",
|
|
5
5
|
"jobs": [
|
|
@@ -21,38 +21,46 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
|
-
"type": "
|
|
25
|
-
"withImage": true,
|
|
26
|
-
"tokenMappings": []
|
|
24
|
+
"type": "attachImage"
|
|
27
25
|
},
|
|
28
26
|
{
|
|
29
27
|
"type": "readReply",
|
|
30
28
|
"out": {
|
|
31
29
|
"kind": "list",
|
|
32
30
|
"parse": "csv",
|
|
33
|
-
"preamble": false
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
"preamble": false
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "modifier",
|
|
36
|
+
"modifier": {
|
|
37
|
+
"type": "tidy"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "modifier",
|
|
42
|
+
"modifier": {
|
|
43
|
+
"type": "dedupe"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "modifier",
|
|
48
|
+
"modifier": {
|
|
49
|
+
"type": "keep-first",
|
|
50
|
+
"n": 12
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"type": "modifier",
|
|
55
|
+
"modifier": {
|
|
56
|
+
"type": "case",
|
|
57
|
+
"mode": "LOWER"
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
]
|
|
53
61
|
}
|
|
54
62
|
],
|
|
55
|
-
"
|
|
63
|
+
"targets": [
|
|
56
64
|
{
|
|
57
65
|
"id": "s1",
|
|
58
66
|
"name": "",
|
|
@@ -60,8 +68,9 @@
|
|
|
60
68
|
"id": "vision",
|
|
61
69
|
"name": "Vision"
|
|
62
70
|
},
|
|
63
|
-
"
|
|
71
|
+
"steps": [
|
|
64
72
|
{
|
|
73
|
+
"type": "prompt",
|
|
65
74
|
"prompt": "List the things you can see in this image, as a comma-separated list of short items (one to three words each), most important first. Up to 12 items."
|
|
66
75
|
}
|
|
67
76
|
]
|
|
@@ -73,8 +82,9 @@
|
|
|
73
82
|
"id": "vision",
|
|
74
83
|
"name": "Vision"
|
|
75
84
|
},
|
|
76
|
-
"
|
|
85
|
+
"steps": [
|
|
77
86
|
{
|
|
87
|
+
"type": "prompt",
|
|
78
88
|
"prompt": "Name what is in this image. Reply with a comma-separated list only: up to 12 short items, most important first."
|
|
79
89
|
}
|
|
80
90
|
]
|