katt 0.0.2 → 0.0.4
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 +6 -1
- package/dist/index.js +272 -249
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Katt is a lightweight testing framework for running AI Evals, inspired by [Jest](https://github.com/jestjs/jest).
|
|
4
4
|
|
|
5
|
-
<img src="docs/logo.png" alt="Katt logo" width="250" />
|
|
5
|
+
<img src="https://raw.githubusercontent.com/raphaelpor/katt/main/docs/logo.png" alt="Katt logo" width="250" />
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
@@ -37,6 +37,7 @@ describe("Greeting agent", () => {
|
|
|
37
37
|
- **Classification Matcher**: Built-in `toBeClassifiedAs()` matcher to grade a response against a target label on a 1-5 scale
|
|
38
38
|
- **Concurrent Execution**: Runs eval files concurrently for faster test execution
|
|
39
39
|
- **Model Selection**: Support for specifying custom AI models
|
|
40
|
+
- **Configurable Timeouts**: Override prompt wait time per test or via `katt.json`
|
|
40
41
|
|
|
41
42
|
## Usage
|
|
42
43
|
|
|
@@ -89,6 +90,9 @@ You can also set a default model for the project by adding a `katt.json` file in
|
|
|
89
90
|
{
|
|
90
91
|
"copilot": {
|
|
91
92
|
"model": "gpt-5-mini"
|
|
93
|
+
},
|
|
94
|
+
"prompt": {
|
|
95
|
+
"timeoutMs": 240000
|
|
92
96
|
}
|
|
93
97
|
}
|
|
94
98
|
```
|
|
@@ -97,6 +101,7 @@ When this file exists:
|
|
|
97
101
|
|
|
98
102
|
- `prompt("...")` and `promptFile("...")` use `copilot.model` by default
|
|
99
103
|
- `prompt("...", { model: "..." })` still overrides the config value
|
|
104
|
+
- `prompt.timeoutMs` sets the default wait timeout for long-running prompts
|
|
100
105
|
|
|
101
106
|
## Development
|
|
102
107
|
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { AsyncLocalStorage as
|
|
3
|
-
import { CopilotClient as
|
|
4
|
-
import { readFile as
|
|
5
|
-
import { resolve as
|
|
6
|
-
import { readFileSync as
|
|
7
|
-
import { fileURLToPath as
|
|
8
|
-
const
|
|
2
|
+
import { AsyncLocalStorage as z } from "node:async_hooks";
|
|
3
|
+
import { CopilotClient as it } from "@github/copilot-sdk";
|
|
4
|
+
import { readFile as X, readdir as st } from "node:fs/promises";
|
|
5
|
+
import { resolve as J, dirname as R, isAbsolute as rt, basename as ct, join as at } from "node:path";
|
|
6
|
+
import { readFileSync as V, writeFileSync as W, mkdirSync as lt } from "node:fs";
|
|
7
|
+
import { fileURLToPath as ut, pathToFileURL as dt } from "node:url";
|
|
8
|
+
const Y = new z(), ft = {
|
|
9
9
|
describeStack: [],
|
|
10
10
|
itStack: [],
|
|
11
11
|
tokenUsageStack: [],
|
|
12
12
|
modelStack: []
|
|
13
13
|
};
|
|
14
|
-
let
|
|
15
|
-
const w = [],
|
|
16
|
-
let
|
|
14
|
+
let Z = 0, E = 0;
|
|
15
|
+
const w = [], y = [];
|
|
16
|
+
let U = 0;
|
|
17
17
|
function u() {
|
|
18
|
-
return
|
|
18
|
+
return Y.getStore() ?? ft;
|
|
19
19
|
}
|
|
20
|
-
function
|
|
20
|
+
function Q(t) {
|
|
21
21
|
return {
|
|
22
22
|
describeStack: [...t.describeStack],
|
|
23
23
|
itStack: [...t.itStack],
|
|
@@ -25,83 +25,83 @@ function H(t) {
|
|
|
25
25
|
modelStack: [...t.modelStack]
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
function
|
|
29
|
-
return
|
|
28
|
+
function gt() {
|
|
29
|
+
return Z += 1, `d${Z}`;
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
return
|
|
31
|
+
function pt() {
|
|
32
|
+
return E += 1, `i${E}`;
|
|
33
33
|
}
|
|
34
|
-
function
|
|
35
|
-
const n = e ??
|
|
36
|
-
return
|
|
34
|
+
function K(t, e) {
|
|
35
|
+
const n = e ?? Q(u());
|
|
36
|
+
return Y.run(n, t);
|
|
37
37
|
}
|
|
38
|
-
function
|
|
39
|
-
return
|
|
38
|
+
function _() {
|
|
39
|
+
return Q(u());
|
|
40
40
|
}
|
|
41
|
-
function
|
|
42
|
-
u().describeStack.push({ id:
|
|
41
|
+
function ht(t) {
|
|
42
|
+
u().describeStack.push({ id: gt(), description: t });
|
|
43
43
|
}
|
|
44
|
-
function
|
|
44
|
+
function x() {
|
|
45
45
|
u().describeStack.pop();
|
|
46
46
|
}
|
|
47
|
-
function
|
|
47
|
+
function q() {
|
|
48
48
|
return u().describeStack.map((t) => t.description).join(" > ");
|
|
49
49
|
}
|
|
50
|
-
function
|
|
51
|
-
u().itStack.push({ id:
|
|
50
|
+
function It(t) {
|
|
51
|
+
u().itStack.push({ id: pt(), description: t }), u().tokenUsageStack.push(0), u().modelStack.push(void 0);
|
|
52
52
|
}
|
|
53
|
-
function
|
|
53
|
+
function F() {
|
|
54
54
|
u().itStack.pop(), u().tokenUsageStack.pop(), u().modelStack.pop();
|
|
55
55
|
}
|
|
56
|
-
function
|
|
56
|
+
function tt() {
|
|
57
57
|
return u().itStack.map((t) => t.description).join(" > ");
|
|
58
58
|
}
|
|
59
|
-
function
|
|
59
|
+
function mt(t) {
|
|
60
60
|
if (!Number.isFinite(t) || t <= 0)
|
|
61
61
|
return;
|
|
62
62
|
const e = u(), n = e.tokenUsageStack.length - 1;
|
|
63
63
|
n < 0 || (e.tokenUsageStack[n] += t);
|
|
64
64
|
}
|
|
65
|
-
function
|
|
65
|
+
function Ct() {
|
|
66
66
|
const t = u(), e = t.tokenUsageStack.length - 1;
|
|
67
67
|
return e < 0 ? 0 : t.tokenUsageStack[e] ?? 0;
|
|
68
68
|
}
|
|
69
|
-
function
|
|
69
|
+
function St(t) {
|
|
70
70
|
if (t.length === 0)
|
|
71
71
|
return;
|
|
72
72
|
const e = u(), n = e.modelStack.length - 1;
|
|
73
73
|
n < 0 || (e.modelStack[n] = t);
|
|
74
74
|
}
|
|
75
|
-
function
|
|
75
|
+
function At() {
|
|
76
76
|
const t = u(), e = t.modelStack.length - 1;
|
|
77
77
|
if (!(e < 0))
|
|
78
78
|
return t.modelStack[e];
|
|
79
79
|
}
|
|
80
|
-
function
|
|
80
|
+
function v(t) {
|
|
81
81
|
w.push(t);
|
|
82
82
|
}
|
|
83
|
-
function
|
|
84
|
-
|
|
83
|
+
function bt() {
|
|
84
|
+
U += 1;
|
|
85
85
|
}
|
|
86
|
-
function
|
|
87
|
-
return
|
|
86
|
+
function $t() {
|
|
87
|
+
return U;
|
|
88
88
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
89
|
+
function kt() {
|
|
90
|
+
U = 0;
|
|
91
91
|
}
|
|
92
|
-
function
|
|
93
|
-
|
|
92
|
+
function wt(t) {
|
|
93
|
+
y.push(t);
|
|
94
94
|
}
|
|
95
|
-
function
|
|
96
|
-
return [...
|
|
95
|
+
function yt() {
|
|
96
|
+
return [...y];
|
|
97
97
|
}
|
|
98
|
-
function
|
|
99
|
-
return
|
|
98
|
+
function O() {
|
|
99
|
+
return y.length;
|
|
100
100
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
101
|
+
function vt() {
|
|
102
|
+
y.length = 0;
|
|
103
103
|
}
|
|
104
|
-
async function
|
|
104
|
+
async function Lt() {
|
|
105
105
|
const t = [];
|
|
106
106
|
for (; w.length > 0; ) {
|
|
107
107
|
const e = w.splice(0, w.length), n = await Promise.allSettled(e);
|
|
@@ -109,41 +109,41 @@ async function yt() {
|
|
|
109
109
|
}
|
|
110
110
|
return t;
|
|
111
111
|
}
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
function jt(t, e) {
|
|
113
|
+
K(() => {
|
|
114
|
+
ht(t);
|
|
115
115
|
try {
|
|
116
116
|
const n = e();
|
|
117
117
|
if (n && typeof n.then == "function") {
|
|
118
|
-
|
|
118
|
+
v(
|
|
119
119
|
n.finally(() => {
|
|
120
|
-
|
|
120
|
+
x();
|
|
121
121
|
})
|
|
122
122
|
);
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
} catch (n) {
|
|
126
|
-
throw
|
|
126
|
+
throw x(), n;
|
|
127
127
|
}
|
|
128
|
-
|
|
129
|
-
},
|
|
128
|
+
x();
|
|
129
|
+
}, _());
|
|
130
130
|
}
|
|
131
|
-
const
|
|
131
|
+
const Tt = "\x1B[1;36m", xt = "\x1B[33m", Ft = "\x1B[38;5;208m", Mt = "\x1B[1;38;5;208m", L = "\x1B[0m";
|
|
132
132
|
function f(t) {
|
|
133
|
-
return `${
|
|
133
|
+
return `${Tt}${t}${L}`;
|
|
134
134
|
}
|
|
135
|
-
function
|
|
136
|
-
return `${
|
|
135
|
+
function $(t) {
|
|
136
|
+
return `${xt}${t}${L}`;
|
|
137
137
|
}
|
|
138
|
-
function
|
|
139
|
-
return `${
|
|
138
|
+
function D(t) {
|
|
139
|
+
return `${Ft}${t}${L}`;
|
|
140
140
|
}
|
|
141
|
-
function
|
|
142
|
-
return `${
|
|
141
|
+
function Nt(t) {
|
|
142
|
+
return `${Mt}${t}${L}`;
|
|
143
143
|
}
|
|
144
|
-
let
|
|
145
|
-
function
|
|
146
|
-
|
|
144
|
+
let B = "";
|
|
145
|
+
function Bt() {
|
|
146
|
+
B = "";
|
|
147
147
|
}
|
|
148
148
|
function Jt({
|
|
149
149
|
suitePath: t,
|
|
@@ -153,31 +153,31 @@ function Jt({
|
|
|
153
153
|
model: s,
|
|
154
154
|
tokenUsage: i
|
|
155
155
|
}) {
|
|
156
|
-
const r = t.length > 0 ? t : "(root)",
|
|
157
|
-
|
|
158
|
-
const l = n ? "✅ Passed in" : "❌ Failed in",
|
|
159
|
-
`Test "${f(
|
|
156
|
+
const r = t.length > 0 ? t : "(root)", c = e.length > 0 ? e : "(root)";
|
|
157
|
+
B !== r && (console.log(`Suite "${f(r)}"`), B = r);
|
|
158
|
+
const l = n ? "✅ Passed in" : "❌ Failed in", I = [
|
|
159
|
+
`Test "${f(c)}"`,
|
|
160
160
|
`- ${l} ${f(`${o}ms`)}`
|
|
161
161
|
];
|
|
162
|
-
s &&
|
|
162
|
+
s && I.push(`- Model ${f(s)}`), (i ?? 0) > 0 && I.push(`- Tokens used ${f(String(i))}`), I.push("---"), console.log(I.join(`
|
|
163
163
|
`));
|
|
164
164
|
}
|
|
165
|
-
function
|
|
166
|
-
const o =
|
|
165
|
+
function C(t, e, n = "(root)") {
|
|
166
|
+
const o = tt();
|
|
167
167
|
Jt({
|
|
168
|
-
suitePath:
|
|
168
|
+
suitePath: q(),
|
|
169
169
|
casePath: o.length > 0 ? o : n,
|
|
170
170
|
didPass: t,
|
|
171
171
|
durationMs: e,
|
|
172
|
-
model:
|
|
173
|
-
tokenUsage:
|
|
172
|
+
model: At(),
|
|
173
|
+
tokenUsage: Ct()
|
|
174
174
|
});
|
|
175
175
|
}
|
|
176
|
-
const
|
|
177
|
-
function
|
|
176
|
+
const G = new z();
|
|
177
|
+
function Rt(t, e) {
|
|
178
178
|
return typeof t == "object" && t !== null && "code" in t && t.code === e;
|
|
179
179
|
}
|
|
180
|
-
function
|
|
180
|
+
function Ut(t) {
|
|
181
181
|
try {
|
|
182
182
|
const e = JSON.parse(t);
|
|
183
183
|
return typeof e == "object" && e !== null ? e : void 0;
|
|
@@ -186,7 +186,19 @@ function Rt(t) {
|
|
|
186
186
|
return;
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
|
-
function
|
|
189
|
+
async function Gt() {
|
|
190
|
+
const t = J(process.cwd(), "katt.json");
|
|
191
|
+
try {
|
|
192
|
+
const e = await X(t, "utf8");
|
|
193
|
+
return Ut(e);
|
|
194
|
+
} catch (e) {
|
|
195
|
+
if (Rt(e, "ENOENT"))
|
|
196
|
+
return;
|
|
197
|
+
console.warn(`Failed to read katt.json: ${String(e)}`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function Wt(t) {
|
|
190
202
|
const e = t?.copilot;
|
|
191
203
|
if (typeof e != "object" || e === null || Array.isArray(e))
|
|
192
204
|
return;
|
|
@@ -195,83 +207,94 @@ function Zt(t) {
|
|
|
195
207
|
}, o = n.model;
|
|
196
208
|
return (typeof o != "string" || o.length === 0) && delete n.model, Object.keys(n).length > 0 ? n : void 0;
|
|
197
209
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
210
|
+
function Zt(t) {
|
|
211
|
+
if (!(typeof t != "number" || !Number.isFinite(t)) && !(t <= 0))
|
|
212
|
+
return Math.floor(t);
|
|
213
|
+
}
|
|
214
|
+
function Et(t) {
|
|
215
|
+
const e = t?.prompt;
|
|
216
|
+
if (!(typeof e != "object" || e === null || Array.isArray(e)))
|
|
217
|
+
return Zt(e.timeoutMs);
|
|
218
|
+
}
|
|
219
|
+
async function Ot() {
|
|
220
|
+
const t = await Gt();
|
|
221
|
+
return {
|
|
222
|
+
copilot: Wt(t),
|
|
223
|
+
promptTimeoutMs: Et(t)
|
|
224
|
+
};
|
|
209
225
|
}
|
|
210
|
-
|
|
226
|
+
const Dt = 6e5;
|
|
227
|
+
function et(t) {
|
|
211
228
|
return typeof t == "string" && t.length > 0 ? t : void 0;
|
|
212
229
|
}
|
|
213
|
-
function
|
|
230
|
+
function M(t) {
|
|
214
231
|
if (!t)
|
|
215
232
|
return;
|
|
216
233
|
const e = { ...t };
|
|
217
234
|
if (e.model !== void 0) {
|
|
218
|
-
const n =
|
|
235
|
+
const n = et(e.model);
|
|
219
236
|
n ? e.model = n : delete e.model;
|
|
220
237
|
}
|
|
221
238
|
return Object.keys(e).length > 0 ? e : void 0;
|
|
222
239
|
}
|
|
223
|
-
function
|
|
240
|
+
function H(t) {
|
|
241
|
+
if (!(typeof t != "number" || !Number.isFinite(t)) && !(t <= 0))
|
|
242
|
+
return Math.floor(t);
|
|
243
|
+
}
|
|
244
|
+
function k(t) {
|
|
224
245
|
return !Number.isFinite(t) || (t ?? 0) <= 0 ? 0 : Math.floor(t ?? 0);
|
|
225
246
|
}
|
|
226
|
-
function
|
|
227
|
-
return
|
|
228
|
-
}
|
|
229
|
-
async function
|
|
230
|
-
const n =
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
247
|
+
function Ht(t) {
|
|
248
|
+
return k(t.inputTokens) + k(t.outputTokens) + k(t.cacheReadTokens) + k(t.cacheWriteTokens);
|
|
249
|
+
}
|
|
250
|
+
async function j(t, e = {}) {
|
|
251
|
+
const { timeoutMs: n, ...o } = e, s = await Ot(), i = M(s.copilot), r = M(
|
|
252
|
+
o
|
|
253
|
+
), c = M({
|
|
254
|
+
...i ?? {},
|
|
255
|
+
...r ?? {}
|
|
256
|
+
}), l = H(s.promptTimeoutMs), T = H(n) ?? l ?? Dt, a = et(c?.model), d = new it({ useLoggedInUser: !0 });
|
|
257
|
+
let p, b, S = 0;
|
|
235
258
|
try {
|
|
236
|
-
await
|
|
237
|
-
|
|
259
|
+
await d.start(), p = await d.createSession(c), b = p.on("assistant.usage", (m) => {
|
|
260
|
+
S += Ht(m.data);
|
|
238
261
|
});
|
|
239
|
-
const
|
|
240
|
-
if (!
|
|
262
|
+
const h = await p.sendAndWait({ prompt: t }, T);
|
|
263
|
+
if (!h?.data?.content)
|
|
241
264
|
throw new Error("Copilot did not return a response.");
|
|
242
|
-
return
|
|
265
|
+
return a && St(a), h.data.content;
|
|
243
266
|
} finally {
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
267
|
+
const h = [];
|
|
268
|
+
if (b?.(), S > 0 && mt(S), p)
|
|
246
269
|
try {
|
|
247
|
-
await
|
|
248
|
-
} catch (
|
|
249
|
-
|
|
270
|
+
await p.destroy();
|
|
271
|
+
} catch (m) {
|
|
272
|
+
h.push(m);
|
|
250
273
|
}
|
|
251
274
|
try {
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
} catch (
|
|
255
|
-
|
|
275
|
+
const m = await d.stop();
|
|
276
|
+
h.push(...m);
|
|
277
|
+
} catch (m) {
|
|
278
|
+
h.push(m);
|
|
256
279
|
}
|
|
257
|
-
|
|
258
|
-
`Copilot cleanup encountered ${
|
|
280
|
+
h.length > 0 && console.error(
|
|
281
|
+
`Copilot cleanup encountered ${h.length} error(s).`
|
|
259
282
|
);
|
|
260
283
|
}
|
|
261
284
|
}
|
|
262
|
-
async function
|
|
263
|
-
const n =
|
|
264
|
-
return
|
|
285
|
+
async function Pt(t, e = {}) {
|
|
286
|
+
const n = G.getStore(), o = n?.evalFile ? R(n.evalFile) : process.cwd(), s = rt(t) ? t : J(o, t), i = await X(s, "utf8");
|
|
287
|
+
return j(i, e);
|
|
265
288
|
}
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
describePath:
|
|
269
|
-
itPath:
|
|
289
|
+
function g(t) {
|
|
290
|
+
wt({
|
|
291
|
+
describePath: q(),
|
|
292
|
+
itPath: tt(),
|
|
270
293
|
message: t
|
|
271
294
|
});
|
|
272
295
|
}
|
|
273
|
-
async function
|
|
274
|
-
const n = Date.now(), o = `expected '${t}' to satisfy '${e}'`, s =
|
|
296
|
+
async function zt(t, e) {
|
|
297
|
+
const n = Date.now(), o = `expected '${t}' to satisfy '${e}'`, s = j(`Evaluate if the expectation is fulfiled in by the input.
|
|
275
298
|
Expectation: "${e}".
|
|
276
299
|
Input:
|
|
277
300
|
---
|
|
@@ -279,17 +302,17 @@ async function Gt(t, e) {
|
|
|
279
302
|
---
|
|
280
303
|
Important: Answer with "Yes" or "No" only, without any additional text.
|
|
281
304
|
`).then((i) => {
|
|
282
|
-
i.includes("Yes") ?
|
|
305
|
+
i.includes("Yes") ? C(!0, Date.now() - n, "promptCheck") : i.includes("No") ? g(o) : g(`failed to evaluate expectation '${e}'`);
|
|
283
306
|
});
|
|
284
|
-
return
|
|
307
|
+
return v(s), s;
|
|
285
308
|
}
|
|
286
|
-
const
|
|
287
|
-
function
|
|
309
|
+
const Xt = 3;
|
|
310
|
+
function Vt(t) {
|
|
288
311
|
const e = t.match(/\b([1-5])\b/);
|
|
289
312
|
return !e || !e[1] ? null : Number(e[1]);
|
|
290
313
|
}
|
|
291
|
-
async function
|
|
292
|
-
const o = Date.now(), s = n?.threshold ??
|
|
314
|
+
async function Yt(t, e, n) {
|
|
315
|
+
const o = Date.now(), s = n?.threshold ?? Xt, i = j(
|
|
293
316
|
`Classify the input by how "${e}" it is on a scale of 1 to 5.
|
|
294
317
|
1 means "not ${e}" and 5 means "very ${e}".
|
|
295
318
|
Return only a single number: 1, 2, 3, 4, or 5.
|
|
@@ -300,88 +323,88 @@ ${t}
|
|
|
300
323
|
---`,
|
|
301
324
|
n?.model ? { model: n.model } : void 0
|
|
302
325
|
).then((r) => {
|
|
303
|
-
const
|
|
304
|
-
if (
|
|
305
|
-
|
|
326
|
+
const c = Vt(r);
|
|
327
|
+
if (c === null) {
|
|
328
|
+
g(
|
|
306
329
|
`failed to classify as '${e}'. Evaluator returned '${r}'`
|
|
307
330
|
);
|
|
308
331
|
return;
|
|
309
332
|
}
|
|
310
|
-
const l = `expected response to be classified as '${e}' with score >= ${s}, got ${
|
|
311
|
-
if (
|
|
312
|
-
|
|
333
|
+
const l = `expected response to be classified as '${e}' with score >= ${s}, got ${c}`;
|
|
334
|
+
if (c < s) {
|
|
335
|
+
g(l);
|
|
313
336
|
return;
|
|
314
337
|
}
|
|
315
|
-
|
|
338
|
+
C(
|
|
316
339
|
!0,
|
|
317
340
|
Date.now() - o,
|
|
318
341
|
"toBeClassifiedAs"
|
|
319
342
|
);
|
|
320
343
|
});
|
|
321
|
-
return
|
|
344
|
+
return v(i), i;
|
|
322
345
|
}
|
|
323
|
-
function
|
|
346
|
+
function Qt(t, e) {
|
|
324
347
|
const n = `expected '${t}' to include '${e}'`;
|
|
325
|
-
t.includes(e) ||
|
|
348
|
+
t.includes(e) || g(n);
|
|
326
349
|
}
|
|
327
|
-
let
|
|
328
|
-
function
|
|
329
|
-
|
|
350
|
+
let nt = !1;
|
|
351
|
+
function Kt(t) {
|
|
352
|
+
nt = t;
|
|
330
353
|
}
|
|
331
|
-
function
|
|
332
|
-
return
|
|
354
|
+
function _t() {
|
|
355
|
+
return nt;
|
|
333
356
|
}
|
|
334
|
-
function
|
|
335
|
-
const n =
|
|
336
|
-
return
|
|
337
|
-
|
|
357
|
+
function qt(t) {
|
|
358
|
+
const n = ct(t).replace(/\.eval\.[^./\\]+$/, "");
|
|
359
|
+
return at(
|
|
360
|
+
R(t),
|
|
338
361
|
"__snapshots__",
|
|
339
362
|
`${n}.snap.md`
|
|
340
363
|
);
|
|
341
364
|
}
|
|
342
|
-
function
|
|
365
|
+
function P(t) {
|
|
343
366
|
return t.split(/\r?\n/);
|
|
344
367
|
}
|
|
345
|
-
function
|
|
368
|
+
function te(t, e) {
|
|
346
369
|
if (t === e)
|
|
347
370
|
return " (no diff)";
|
|
348
|
-
const n =
|
|
371
|
+
const n = P(t), o = P(e), s = Math.max(n.length, o.length), i = [];
|
|
349
372
|
for (let r = 0; r < s; r += 1) {
|
|
350
|
-
const
|
|
351
|
-
if (
|
|
352
|
-
if (
|
|
373
|
+
const c = n[r], l = o[r];
|
|
374
|
+
if (c !== l) {
|
|
375
|
+
if (c === void 0 && l !== void 0) {
|
|
353
376
|
i.push(`+ ${l}`);
|
|
354
377
|
continue;
|
|
355
378
|
}
|
|
356
|
-
if (
|
|
357
|
-
i.push(`- ${
|
|
379
|
+
if (c !== void 0 && l === void 0) {
|
|
380
|
+
i.push(`- ${c}`);
|
|
358
381
|
continue;
|
|
359
382
|
}
|
|
360
|
-
i.push(`- ${
|
|
383
|
+
i.push(`- ${c ?? ""}`), i.push(`+ ${l ?? ""}`);
|
|
361
384
|
}
|
|
362
385
|
}
|
|
363
386
|
return i.join(`
|
|
364
387
|
`);
|
|
365
388
|
}
|
|
366
|
-
function
|
|
367
|
-
const e =
|
|
389
|
+
function ee(t) {
|
|
390
|
+
const e = G.getStore()?.evalFile;
|
|
368
391
|
if (!e) {
|
|
369
|
-
|
|
392
|
+
g(
|
|
370
393
|
"toMatchSnapshot can only be used while running an eval file."
|
|
371
394
|
);
|
|
372
395
|
return;
|
|
373
396
|
}
|
|
374
|
-
const n =
|
|
397
|
+
const n = qt(e);
|
|
375
398
|
try {
|
|
376
|
-
const o =
|
|
399
|
+
const o = V(n, "utf8");
|
|
377
400
|
if (o === t)
|
|
378
401
|
return;
|
|
379
|
-
if (
|
|
380
|
-
|
|
402
|
+
if (_t()) {
|
|
403
|
+
W(n, t, "utf8");
|
|
381
404
|
return;
|
|
382
405
|
}
|
|
383
|
-
const s =
|
|
384
|
-
|
|
406
|
+
const s = te(o, t);
|
|
407
|
+
g(
|
|
385
408
|
[
|
|
386
409
|
`Snapshot mismatch at ${n}`,
|
|
387
410
|
"",
|
|
@@ -394,173 +417,173 @@ function Pt(t) {
|
|
|
394
417
|
);
|
|
395
418
|
} catch (o) {
|
|
396
419
|
if (o.code !== "ENOENT") {
|
|
397
|
-
|
|
420
|
+
g(
|
|
398
421
|
`Failed to read snapshot at ${n}: ${String(o)}`
|
|
399
422
|
);
|
|
400
423
|
return;
|
|
401
424
|
}
|
|
402
425
|
try {
|
|
403
|
-
|
|
426
|
+
lt(R(n), { recursive: !0 }), W(n, t, "utf8");
|
|
404
427
|
} catch (i) {
|
|
405
|
-
|
|
428
|
+
g(
|
|
406
429
|
`Failed to write snapshot at ${n}: ${String(i)}`
|
|
407
430
|
);
|
|
408
431
|
}
|
|
409
432
|
}
|
|
410
433
|
}
|
|
411
|
-
function
|
|
434
|
+
function ne(t) {
|
|
412
435
|
return {
|
|
413
436
|
toContain: (e) => {
|
|
414
|
-
|
|
437
|
+
Qt(t, e);
|
|
415
438
|
},
|
|
416
439
|
toMatchSnapshot: () => {
|
|
417
|
-
|
|
440
|
+
ee(t);
|
|
418
441
|
},
|
|
419
442
|
promptCheck: async (e) => {
|
|
420
|
-
await
|
|
443
|
+
await zt(t, e);
|
|
421
444
|
},
|
|
422
445
|
toBeClassifiedAs: async (e, n) => {
|
|
423
|
-
await
|
|
446
|
+
await Yt(t, e, n);
|
|
424
447
|
}
|
|
425
448
|
};
|
|
426
449
|
}
|
|
427
|
-
function
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
const n =
|
|
450
|
+
function oe(t, e) {
|
|
451
|
+
K(() => {
|
|
452
|
+
bt(), It(t);
|
|
453
|
+
const n = O(), o = Date.now(), s = () => O() === n, i = () => Date.now() - o;
|
|
431
454
|
try {
|
|
432
455
|
const r = e();
|
|
433
456
|
if (r && typeof r.then == "function") {
|
|
434
|
-
|
|
457
|
+
v(
|
|
435
458
|
r.then(() => {
|
|
436
|
-
|
|
437
|
-
}).catch((
|
|
438
|
-
throw
|
|
459
|
+
C(!0, i());
|
|
460
|
+
}).catch((c) => {
|
|
461
|
+
throw C(!1, i()), c;
|
|
439
462
|
}).finally(() => {
|
|
440
|
-
|
|
463
|
+
F();
|
|
441
464
|
})
|
|
442
465
|
);
|
|
443
466
|
return;
|
|
444
467
|
}
|
|
445
468
|
} catch (r) {
|
|
446
|
-
throw
|
|
469
|
+
throw C(!1, i()), F(), r;
|
|
447
470
|
}
|
|
448
|
-
|
|
449
|
-
},
|
|
471
|
+
C(s(), i()), F();
|
|
472
|
+
}, _());
|
|
450
473
|
}
|
|
451
|
-
const
|
|
452
|
-
async function
|
|
453
|
-
const e = await
|
|
474
|
+
const ie = /\.eval\.(js|ts)$/, se = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
475
|
+
async function ot(t) {
|
|
476
|
+
const e = await st(t, { withFileTypes: !0 }), n = [];
|
|
454
477
|
return await Promise.all(
|
|
455
478
|
e.map(async (o) => {
|
|
456
|
-
const s =
|
|
479
|
+
const s = J(t, o.name);
|
|
457
480
|
if (o.isDirectory()) {
|
|
458
|
-
if (
|
|
481
|
+
if (se.has(o.name))
|
|
459
482
|
return;
|
|
460
|
-
n.push(...await
|
|
483
|
+
n.push(...await ot(s));
|
|
461
484
|
return;
|
|
462
485
|
}
|
|
463
|
-
o.isFile() &&
|
|
486
|
+
o.isFile() && ie.test(o.name) && n.push(s);
|
|
464
487
|
})
|
|
465
488
|
), n;
|
|
466
489
|
}
|
|
467
|
-
const
|
|
468
|
-
let
|
|
469
|
-
function
|
|
470
|
-
if (
|
|
471
|
-
return
|
|
490
|
+
const N = new URL("data:application/json;base64,ewogICJuYW1lIjogImthdHQiLAogICJ2ZXJzaW9uIjogIjAuMC4zIiwKICAiZGVzY3JpcHRpb24iOiAiQ0xJIHRvb2wgdGhhdCB0ZXN0cyB0aGUgb3V0cHV0IG9mIGFnZW50aWMgQUkgdG9vbHMiLAogICJrZXl3b3JkcyI6IFsKICAgICJjbGkiLAogICAgImFpIiwKICAgICJhZ2VudGljLWFpIiwKICAgICJ0ZXN0aW5nIiwKICAgICJldmFsdWF0aW9uIgogIF0sCiAgImF1dGhvciI6ICJSYXBoYWVsIFBvcnRvIChodHRwczovL2dpdGh1Yi5jb20vcmFwaGFlbHBvcikiLAogICJsaWNlbnNlIjogIk1JVCIsCiAgInR5cGUiOiAibW9kdWxlIiwKICAibWFpbiI6ICJkaXN0L2luZGV4LmpzIiwKICAiYmluIjogewogICAgImthdHQiOiAiZGlzdC9pbmRleC5qcyIKICB9LAogICJzY3JpcHRzIjogewogICAgImJ1aWxkIjogInZpdGUgYnVpbGQiLAogICAgImRldiI6ICJ0c3ggc3JjL2luZGV4LnRzIiwKICAgICJsaW50IjogImJpb21lIGxpbnQgLi9zcmMiLAogICAgImZvcm1hdCI6ICJiaW9tZSBmb3JtYXQgLS13cml0ZSAuL3NyYyIsCiAgICAidGVzdCI6ICJ2aXRlc3QiLAogICAgInR5cGVjaGVjayI6ICJ0c2MgLXAgdHNjb25maWcuanNvbiAtLW5vRW1pdCIsCiAgICAidGVzdDpidWlsZCI6ICJub2RlIC4vZGlzdC9pbmRleC5qcyIKICB9LAogICJ0eXBlcyI6ICJkaXN0L2luZGV4LmQudHMiLAogICJkZXZEZXBlbmRlbmNpZXMiOiB7CiAgICAiQGJpb21lanMvYmlvbWUiOiAiMS45LjQiLAogICAgIkB0eXBlcy9ub2RlIjogIjI1LjIuMCIsCiAgICAidHN4IjogIjQuMjEuMCIsCiAgICAidHlwZXNjcmlwdCI6ICI1LjguMiIsCiAgICAidml0ZSI6ICI3LjMuMSIsCiAgICAidml0ZS1wbHVnaW4tZHRzIjogIjQuNS40IiwKICAgICJ2aXRlc3QiOiAiMy4yLjQiLAogICAgInZzY29kZS1qc29ucnBjIjogIl44LjIuMSIKICB9LAogICJkZXBlbmRlbmNpZXMiOiB7CiAgICAiQGdpdGh1Yi9jb3BpbG90LXNkayI6ICJeMC4xLjIxIgogIH0sCiAgImJ1Z3MiOiB7CiAgICAidXJsIjogImh0dHBzOi8vZ2l0aHViLmNvbS9yYXBoYWVscG9yL2thdHQvaXNzdWVzIgogIH0sCiAgImhvbWVwYWdlIjogImh0dHBzOi8vZ2l0aHViLmNvbS9yYXBoYWVscG9yL2thdHQiCn0K", import.meta.url);
|
|
491
|
+
let A;
|
|
492
|
+
function re() {
|
|
493
|
+
if (A !== void 0)
|
|
494
|
+
return A;
|
|
472
495
|
try {
|
|
473
|
-
const t =
|
|
474
|
-
|
|
496
|
+
const t = N.protocol === "data:" ? ce(N) : V(ut(N), "utf8"), e = JSON.parse(t);
|
|
497
|
+
A = typeof e.version == "string" ? e.version : "unknown";
|
|
475
498
|
} catch {
|
|
476
|
-
|
|
499
|
+
A = "unknown";
|
|
477
500
|
}
|
|
478
|
-
return
|
|
501
|
+
return A;
|
|
479
502
|
}
|
|
480
|
-
function
|
|
503
|
+
function ce(t) {
|
|
481
504
|
const e = t.pathname.indexOf(",");
|
|
482
505
|
if (e < 0)
|
|
483
506
|
throw new Error("Invalid data URL.");
|
|
484
507
|
const n = t.pathname.slice(0, e), o = t.pathname.slice(e + 1);
|
|
485
508
|
return n.includes(";base64") ? Buffer.from(o, "base64").toString("utf8") : decodeURIComponent(o);
|
|
486
509
|
}
|
|
487
|
-
function
|
|
488
|
-
const t = " ██╗ ██╗ █████╗ ████████╗████████╗", e = " ██║ ██╔╝██╔══██╗╚══██╔══╝╚══██╔══╝", n = " █████╔╝ ███████║ ██║ ██║", o = " ██╔═██╗ ██╔══██║ ██║ ██║", s = " ██║ ██╗██║ ██║ ██║ ██║", i = " ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝", r = `v${
|
|
510
|
+
function ae() {
|
|
511
|
+
const t = " ██╗ ██╗ █████╗ ████████╗████████╗", e = " ██║ ██╔╝██╔══██╗╚══██╔══╝╚══██╔══╝", n = " █████╔╝ ███████║ ██║ ██║", o = " ██╔═██╗ ██╔══██║ ██║ ██║", s = " ██║ ██╗██║ ██║ ██║ ██║", i = " ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝", r = `v${re()}`, c = Math.max(
|
|
489
512
|
0,
|
|
490
513
|
Math.floor((t.length - r.length) / 2)
|
|
491
|
-
), l = `${" ".repeat(
|
|
514
|
+
), l = `${" ".repeat(c)}${r}`;
|
|
492
515
|
console.log(`
|
|
493
|
-
${
|
|
494
|
-
${
|
|
495
|
-
${
|
|
496
|
-
${
|
|
497
|
-
${
|
|
498
|
-
${
|
|
499
|
-
${
|
|
516
|
+
${$(t)}
|
|
517
|
+
${$(e)}
|
|
518
|
+
${$(n)}
|
|
519
|
+
${D(o)}
|
|
520
|
+
${D(s)}
|
|
521
|
+
${Nt(i)}
|
|
522
|
+
${$(l)}
|
|
500
523
|
`);
|
|
501
524
|
}
|
|
502
|
-
function
|
|
525
|
+
function le(t) {
|
|
503
526
|
const e = String(t.getHours()).padStart(2, "0"), n = String(t.getMinutes()).padStart(2, "0"), o = String(t.getSeconds()).padStart(2, "0");
|
|
504
527
|
return `${e}:${n}:${o}`;
|
|
505
528
|
}
|
|
506
|
-
async function
|
|
529
|
+
async function ue() {
|
|
507
530
|
const t = process.argv.slice(2), e = t.includes("--update-snapshots") || t.includes("-u");
|
|
508
|
-
|
|
531
|
+
Kt(e), ae();
|
|
509
532
|
const n = /* @__PURE__ */ new Date();
|
|
510
|
-
|
|
511
|
-
const o = await
|
|
533
|
+
Bt(), vt(), kt();
|
|
534
|
+
const o = await ot(process.cwd());
|
|
512
535
|
if (o.length === 0)
|
|
513
536
|
return console.log("No .eval.js or .eval.ts files found."), 1;
|
|
514
537
|
const i = (await Promise.allSettled(
|
|
515
538
|
o.map(
|
|
516
|
-
(
|
|
517
|
-
{ evalFile:
|
|
518
|
-
() => import(
|
|
539
|
+
(a) => G.run(
|
|
540
|
+
{ evalFile: a },
|
|
541
|
+
() => import(dt(a).href)
|
|
519
542
|
)
|
|
520
543
|
)
|
|
521
|
-
)).map((
|
|
544
|
+
)).map((a, d) => ({ result: a, file: o[d] })).filter(({ result: a }) => a.status === "rejected");
|
|
522
545
|
if (i.length > 0) {
|
|
523
|
-
for (const
|
|
524
|
-
const
|
|
525
|
-
console.error(`Error executing ${
|
|
546
|
+
for (const a of i) {
|
|
547
|
+
const d = a.result.status === "rejected" ? a.result.reason : void 0;
|
|
548
|
+
console.error(`Error executing ${a.file}: ${String(d)}`);
|
|
526
549
|
}
|
|
527
550
|
return 1;
|
|
528
551
|
}
|
|
529
|
-
const
|
|
530
|
-
(
|
|
552
|
+
const c = (await Lt()).filter(
|
|
553
|
+
(a) => a.status === "rejected"
|
|
531
554
|
);
|
|
532
|
-
if (
|
|
533
|
-
for (const
|
|
534
|
-
|
|
555
|
+
if (c.length > 0) {
|
|
556
|
+
for (const a of c)
|
|
557
|
+
a.status === "rejected" && console.error(`Error executing async test: ${String(a.reason)}`);
|
|
535
558
|
return 1;
|
|
536
559
|
}
|
|
537
|
-
const l =
|
|
560
|
+
const l = yt();
|
|
538
561
|
if (l.length > 0) {
|
|
539
562
|
console.error("❌ Failed tests:");
|
|
540
|
-
for (const [
|
|
541
|
-
const
|
|
542
|
-
console.error(`${
|
|
563
|
+
for (const [a, d] of l.entries()) {
|
|
564
|
+
const p = [d.describePath, d.itPath].filter((S) => S.length > 0).join(" > "), b = p.length > 0 ? `${p}: ` : "";
|
|
565
|
+
console.error(`${a + 1}. ${b}${d.message}`);
|
|
543
566
|
}
|
|
544
567
|
return 1;
|
|
545
568
|
}
|
|
546
|
-
const
|
|
569
|
+
const I = $t(), T = Date.now() - n.getTime();
|
|
547
570
|
return console.log(
|
|
548
571
|
[
|
|
549
572
|
"---",
|
|
550
573
|
`${f("Files")} ${o.length} passed`,
|
|
551
|
-
`${f("Evals")} ${
|
|
552
|
-
`${f("Start at")} ${
|
|
553
|
-
`${f("Duration")} ${
|
|
574
|
+
`${f("Evals")} ${I} passed`,
|
|
575
|
+
`${f("Start at")} ${le(n)}`,
|
|
576
|
+
`${f("Duration")} ${T}ms`
|
|
554
577
|
].join(`
|
|
555
578
|
`)
|
|
556
579
|
), 0;
|
|
557
580
|
}
|
|
558
|
-
Object.assign(globalThis, { describe:
|
|
559
|
-
|
|
581
|
+
Object.assign(globalThis, { describe: jt, it: oe, expect: ne, prompt: j, promptFile: Pt });
|
|
582
|
+
ue().then((t) => {
|
|
560
583
|
process.exit(t);
|
|
561
584
|
}).catch((t) => {
|
|
562
585
|
console.error(`Unexpected error: ${String(t)}`), process.exit(1);
|
|
563
586
|
});
|
|
564
587
|
export {
|
|
565
|
-
|
|
588
|
+
ue as runCli
|
|
566
589
|
};
|