@leostera/evalkit 0.5.0 → 0.6.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 +55 -1
- package/README.md +10 -8
- package/dist/cli.mjs +1227 -258
- package/dist/dashboard/assets/index-BK9x-zoi.css +2 -0
- package/dist/dashboard/assets/index-DzsDLGHx.js +32 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/files-C-U0-PWW.mjs +0 -2
- package/dist/index-db-vbqEGFaU.mjs +0 -2
- package/dist/index.d.mts +1344 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -4
- package/dist/runner.d.mts +807 -11
- package/dist/runner.d.mts.map +1 -1
- package/dist/runner.mjs +2 -2
- package/dist/share-worker.d.mts +76 -1
- package/dist/share-worker.d.mts.map +1 -1
- package/dist/share-worker.mjs +0 -2
- package/dist/share.d.mts +149 -6
- package/dist/share.d.mts.map +1 -1
- package/dist/share.mjs +1 -1
- package/dist/skills/evalkit/SKILL.md +32 -0
- package/dist/skills/evalkit-agents/SKILL.md +16 -0
- package/dist/skills/evalkit-datasets/SKILL.md +17 -0
- package/dist/skills/evalkit-evals/SKILL.md +19 -0
- package/dist/skills/evalkit-judges/SKILL.md +17 -0
- package/dist/skills/evalkit-matrices/SKILL.md +14 -0
- package/dist/skills/evalkit-results/SKILL.md +14 -0
- package/dist/{src-DoPs9Yug.mjs → src-CVSCn3bZ.mjs} +445 -115
- package/dist/{src-DzZueFn3.mjs → src-CZnDXP1K.mjs} +46 -10
- package/dist/{src-ButXPY8P.mjs → src-D4tCwxNb.mjs} +418 -94
- package/package.json +3 -2
- package/dist/cli.mjs.map +0 -1
- package/dist/dashboard/assets/index-D9NtA46z.css +0 -2
- package/dist/dashboard/assets/index-DrBwLKJD.js +0 -30
- package/dist/files-C-U0-PWW.mjs.map +0 -1
- package/dist/index-db-HdZXelOS.d.mts +0 -142
- package/dist/index-db-HdZXelOS.d.mts.map +0 -1
- package/dist/index-db-vbqEGFaU.mjs.map +0 -1
- package/dist/index-lOw0fmak.d.mts +0 -1028
- package/dist/index-lOw0fmak.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/share-worker.mjs.map +0 -1
- package/dist/src-ButXPY8P.mjs.map +0 -1
- package/dist/src-DoPs9Yug.mjs.map +0 -1
- package/dist/src-DzZueFn3.mjs.map +0 -1
|
@@ -31,6 +31,291 @@ function resourceUuid(value, expectedKind) {
|
|
|
31
31
|
return parseResourceUri(value, expectedKind).uuid;
|
|
32
32
|
}
|
|
33
33
|
//#endregion
|
|
34
|
+
//#region ../core/src/definition-site.ts
|
|
35
|
+
const SITE = Symbol.for("evalkit.definitionSite");
|
|
36
|
+
const internalFiles = /* @__PURE__ */ new Set();
|
|
37
|
+
/** Call from each module that defines helpers, so its frames are skipped. */
|
|
38
|
+
function internalModule(url) {
|
|
39
|
+
if (url) internalFiles.add(filePath(url));
|
|
40
|
+
}
|
|
41
|
+
internalModule(import.meta.url);
|
|
42
|
+
function markDefinitionSite(value) {
|
|
43
|
+
if (Object.hasOwn(value, SITE)) return value;
|
|
44
|
+
const site = callerSite();
|
|
45
|
+
if (site) Object.defineProperty(value, SITE, {
|
|
46
|
+
value: site,
|
|
47
|
+
enumerable: false
|
|
48
|
+
});
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
function definitionSite(value) {
|
|
52
|
+
return typeof value === "object" && value !== null ? value[SITE] ?? void 0 : void 0;
|
|
53
|
+
}
|
|
54
|
+
/** `file:line` relative to `root`, or undefined outside it (never an absolute path). */
|
|
55
|
+
function relativeSite(site, root) {
|
|
56
|
+
if (!site || !root) return void 0;
|
|
57
|
+
const base = root.replace(/[\\/]+$/, "") + "/";
|
|
58
|
+
const file = site.file.replace(/\\/g, "/");
|
|
59
|
+
const prefix = base.replace(/\\/g, "/");
|
|
60
|
+
if (!file.startsWith(prefix)) return void 0;
|
|
61
|
+
return `${file.slice(prefix.length)}:${site.line}`;
|
|
62
|
+
}
|
|
63
|
+
const FRAME = /\(?((?:file:\/\/)?(?:\/|[A-Za-z]:[\\/])[^():]*):(\d+):\d+\)?\s*$/;
|
|
64
|
+
function callerSite() {
|
|
65
|
+
const stack = (/* @__PURE__ */ new Error()).stack?.split("\n").slice(1) ?? [];
|
|
66
|
+
for (const frame of stack) {
|
|
67
|
+
const match = FRAME.exec(frame);
|
|
68
|
+
if (!match) continue;
|
|
69
|
+
const file = filePath(match[1]);
|
|
70
|
+
if (internalFiles.has(file) || file.includes("/node_modules/")) continue;
|
|
71
|
+
return {
|
|
72
|
+
file,
|
|
73
|
+
line: Number(match[2])
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function filePath(location) {
|
|
78
|
+
if (!location.startsWith("file://")) return location;
|
|
79
|
+
try {
|
|
80
|
+
return decodeURIComponent(new URL(location).pathname);
|
|
81
|
+
} catch {
|
|
82
|
+
return location.slice(7);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region ../core/src/dataset.ts
|
|
87
|
+
internalModule(import.meta.url);
|
|
88
|
+
/** Normalizes a relative POSIX path and rejects absolute or escaping ones. */
|
|
89
|
+
function relativeDatasetPath(value, label) {
|
|
90
|
+
const parts = [];
|
|
91
|
+
if (!value || value.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(value)) throw new Error(`${label} must be a non-empty relative path: ${value}`);
|
|
92
|
+
for (const part of value.replace(/\\/g, "/").split("/")) {
|
|
93
|
+
if (part === "" || part === ".") continue;
|
|
94
|
+
if (part === "..") {
|
|
95
|
+
if (!parts.length) throw new Error(`${label} escapes its workspace: ${value}`);
|
|
96
|
+
parts.pop();
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
parts.push(part);
|
|
100
|
+
}
|
|
101
|
+
return parts.length ? parts.join("/") : ".";
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Declares named data that scenarios use: files placed in each trial's
|
|
105
|
+
* workspace, a value for the agent and scorers, or both. Static datasets give
|
|
106
|
+
* `files`, `value` or `valueFile`; computed datasets give `onLoad` (cached per
|
|
107
|
+
* process) and optionally `onRun` (per trial).
|
|
108
|
+
*/
|
|
109
|
+
function defineDataset(definition) {
|
|
110
|
+
return markDefinitionSite(checkedDataset(definition));
|
|
111
|
+
}
|
|
112
|
+
function checkedDataset(definition) {
|
|
113
|
+
if ("uri" in definition || "uuid" in definition || "slug" in definition) throw new Error("Authored resources use id, not uri, uuid, or slug");
|
|
114
|
+
const id = authorId(definition.id);
|
|
115
|
+
const visibility = definition.visibility ?? "candidate";
|
|
116
|
+
if (visibility !== "candidate" && visibility !== "evaluator") throw new Error(`Dataset ${id} visibility must be candidate or evaluator; received ${String(visibility)}`);
|
|
117
|
+
const mount = relativeDatasetPath(definition.mount ?? id, `Dataset ${id} mount`);
|
|
118
|
+
const common = {
|
|
119
|
+
kind: "dataset",
|
|
120
|
+
id,
|
|
121
|
+
...definition.name ? { name: definition.name } : {},
|
|
122
|
+
visibility,
|
|
123
|
+
mount
|
|
124
|
+
};
|
|
125
|
+
if (definition.onLoad !== void 0) {
|
|
126
|
+
if (typeof definition.onLoad !== "function") throw new Error(`Dataset ${id} onLoad must be a function`);
|
|
127
|
+
if (definition.files !== void 0 || definition.value !== void 0 || definition.valueFile !== void 0) throw new Error(`Dataset ${id} is computed (onLoad); move files and values into what onLoad returns`);
|
|
128
|
+
if (definition.onRun !== void 0 && typeof definition.onRun !== "function") throw new Error(`Dataset ${id} onRun must be a function`);
|
|
129
|
+
return {
|
|
130
|
+
...common,
|
|
131
|
+
source: {
|
|
132
|
+
kind: "computed",
|
|
133
|
+
onLoad: definition.onLoad,
|
|
134
|
+
...definition.onRun ? { onRun: definition.onRun } : {}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (definition.onRun !== void 0) throw new Error(`Dataset ${id} has onRun without onLoad`);
|
|
139
|
+
if (definition.value !== void 0 && definition.valueFile !== void 0) throw new Error(`Dataset ${id} has both value and valueFile; give one`);
|
|
140
|
+
if (definition.files === void 0 && definition.value === void 0 && definition.valueFile === void 0) throw new Error(`Dataset ${id} is empty; give files, value, valueFile or onLoad`);
|
|
141
|
+
return {
|
|
142
|
+
...common,
|
|
143
|
+
source: {
|
|
144
|
+
kind: "static",
|
|
145
|
+
...definition.files !== void 0 ? { files: definition.files } : {},
|
|
146
|
+
...definition.value !== void 0 ? { value: definition.value } : {},
|
|
147
|
+
...definition.valueFile !== void 0 ? { valueFile: definition.valueFile } : {}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function isDataset(value) {
|
|
152
|
+
return typeof value === "object" && value !== null && value.kind === "dataset" && typeof value.source === "object";
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region ../core/src/json.ts
|
|
156
|
+
/** A value that cannot be stored in a report, with the path of the offending part. */
|
|
157
|
+
var NotJsonError = class extends Error {
|
|
158
|
+
path;
|
|
159
|
+
reason;
|
|
160
|
+
name = "NotJsonError";
|
|
161
|
+
constructor(path, reason) {
|
|
162
|
+
super(`${path}: ${reason}`);
|
|
163
|
+
this.path = path;
|
|
164
|
+
this.reason = reason;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Converts an author-supplied value to JSON once, where it enters EvalKit.
|
|
169
|
+
* Honours `toJSON()` (so `Date` becomes an ISO string) and drops `undefined`
|
|
170
|
+
* object fields. Rejects functions, symbols, bigints, non-finite numbers and
|
|
171
|
+
* cycles, naming the path of the bad value.
|
|
172
|
+
*/
|
|
173
|
+
function toJson(value, path = "value") {
|
|
174
|
+
return convert(value, path, /* @__PURE__ */ new Set());
|
|
175
|
+
}
|
|
176
|
+
function convert(value, path, seen) {
|
|
177
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
178
|
+
if (typeof value === "number") {
|
|
179
|
+
if (!Number.isFinite(value)) throw new NotJsonError(path, `${value} is not JSON`);
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
if (value === void 0) throw new NotJsonError(path, "undefined is not JSON");
|
|
183
|
+
if (typeof value !== "object") throw new NotJsonError(path, `${typeof value} is not JSON`);
|
|
184
|
+
if (seen.has(value)) throw new NotJsonError(path, "cycle is not JSON");
|
|
185
|
+
const withToJson = value;
|
|
186
|
+
if (typeof withToJson.toJSON === "function") return convert(withToJson.toJSON(), path, seen);
|
|
187
|
+
seen.add(value);
|
|
188
|
+
try {
|
|
189
|
+
if (Array.isArray(value)) return value.map((item, index) => convert(item === void 0 ? null : item, `${path}[${index}]`, seen));
|
|
190
|
+
if (value instanceof Map || value instanceof Set) throw new NotJsonError(path, `${value.constructor.name} is not JSON`);
|
|
191
|
+
const result = {};
|
|
192
|
+
for (const [key, field] of Object.entries(value)) {
|
|
193
|
+
if (field === void 0) continue;
|
|
194
|
+
result[key] = convert(field, `${path}.${key}`, seen);
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
} finally {
|
|
198
|
+
seen.delete(value);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** `toJson` for values that must be objects, such as metadata. */
|
|
202
|
+
function toJsonObject(value, path) {
|
|
203
|
+
const converted = toJson(value, path);
|
|
204
|
+
if (converted === null || typeof converted !== "object" || Array.isArray(converted)) throw new NotJsonError(path, "must be a JSON object");
|
|
205
|
+
return converted;
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region ../core/src/catalog.ts
|
|
209
|
+
function catalogEntry(evaluation, suite, options = {}) {
|
|
210
|
+
const at = (value) => relativeSite(definitionSite(value), options.root);
|
|
211
|
+
const definedAt = at(evaluation);
|
|
212
|
+
const judgeAgent = evaluation.judge ? catalogAgent(evaluation.judge, at) : void 0;
|
|
213
|
+
const policy = evaluation.policy ?? {};
|
|
214
|
+
return {
|
|
215
|
+
id: evaluation.id,
|
|
216
|
+
path: suite ? `${suite.id}#${evaluation.id}` : evaluation.id,
|
|
217
|
+
...evaluation.name ? { name: evaluation.name } : {},
|
|
218
|
+
...suite ? { suiteId: authoringId(suite) } : {},
|
|
219
|
+
agent: catalogAgent(evaluation.agent, at),
|
|
220
|
+
...judgeAgent ? { judgeAgent } : {},
|
|
221
|
+
datasets: (evaluation.datasets ?? []).map((dataset) => catalogDataset(dataset, at)),
|
|
222
|
+
trialCount: policy.trials ?? 1,
|
|
223
|
+
policy: {
|
|
224
|
+
trials: policy.trials ?? 1,
|
|
225
|
+
...policy.timeoutMs !== void 0 ? { timeoutMs: policy.timeoutMs } : {},
|
|
226
|
+
...policy.failfast !== void 0 ? { failfast: policy.failfast } : {}
|
|
227
|
+
},
|
|
228
|
+
transcript: evaluation.transcript.map((step) => catalogStep(step, at)),
|
|
229
|
+
scorers: evaluation.scoring.map((rule) => catalogScorer(rule, at)),
|
|
230
|
+
...evaluation.metadata !== void 0 ? { metadata: toJson(evaluation.metadata, `${evaluation.id}.metadata`) } : {},
|
|
231
|
+
...definedAt ? { definedAt } : {}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function catalogAgent(agent, at) {
|
|
235
|
+
const identity = agent.identity;
|
|
236
|
+
const definedAt = at(agent);
|
|
237
|
+
return {
|
|
238
|
+
...identity?.name ? { name: identity.name } : {},
|
|
239
|
+
kind: identity?.kind ?? "adapter",
|
|
240
|
+
...identity?.id ? { id: identity.id } : {},
|
|
241
|
+
...identity?.version ? { version: identity.version } : {},
|
|
242
|
+
runtimes: Object.entries(agentRuntimes(agent)).map(([name, runtime]) => ({
|
|
243
|
+
name,
|
|
244
|
+
kind: runtime.kind
|
|
245
|
+
})),
|
|
246
|
+
...definedAt ? { definedAt } : {}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function catalogDataset(dataset, at) {
|
|
250
|
+
const definedAt = at(dataset);
|
|
251
|
+
const source = dataset.source;
|
|
252
|
+
const base = {
|
|
253
|
+
id: dataset.id,
|
|
254
|
+
...dataset.name ? { name: dataset.name } : {},
|
|
255
|
+
visibility: dataset.visibility,
|
|
256
|
+
source: source.kind,
|
|
257
|
+
mount: dataset.mount,
|
|
258
|
+
...definedAt ? { definedAt } : {}
|
|
259
|
+
};
|
|
260
|
+
if (source.kind === "computed") return {
|
|
261
|
+
...base,
|
|
262
|
+
hooks: {
|
|
263
|
+
onLoad: true,
|
|
264
|
+
onRun: !!source.onRun
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
return {
|
|
268
|
+
...base,
|
|
269
|
+
...source.files === void 0 ? {} : typeof source.files === "string" ? { files: { directory: source.files } } : { files: { paths: Object.keys(source.files).sort() } },
|
|
270
|
+
...source.valueFile !== void 0 ? { value: { file: source.valueFile } } : source.value !== void 0 ? { value: { shape: shapeOf(source.value) } } : {}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function catalogScorer(rule, at) {
|
|
274
|
+
const definedAt = at(rule);
|
|
275
|
+
return {
|
|
276
|
+
name: rule.name,
|
|
277
|
+
kind: rule.kind,
|
|
278
|
+
...rule.kind === "predicate" && rule.passAt !== void 0 ? { passAt: rule.passAt } : {},
|
|
279
|
+
...rule.supportsPartial !== void 0 ? { supportsPartial: rule.supportsPartial } : {},
|
|
280
|
+
...rule.kind === "judge" ? { rubric: rule.rubric } : {},
|
|
281
|
+
...definedAt ? { definedAt } : {}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function catalogStep(step, at) {
|
|
285
|
+
switch (step.kind) {
|
|
286
|
+
case "user": return {
|
|
287
|
+
kind: "user",
|
|
288
|
+
message: step.message
|
|
289
|
+
};
|
|
290
|
+
case "agent": return {
|
|
291
|
+
kind: "agent",
|
|
292
|
+
expectation: step.expectation
|
|
293
|
+
};
|
|
294
|
+
case "expect-tool-call": return {
|
|
295
|
+
kind: "expect-tool-call",
|
|
296
|
+
name: step.expected.name,
|
|
297
|
+
...step.expected.arguments !== void 0 ? { arguments: step.expected.arguments } : {}
|
|
298
|
+
};
|
|
299
|
+
default: return {
|
|
300
|
+
kind: "checkpoint",
|
|
301
|
+
scorer: catalogScorer(step, at)
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function shapeOf(value) {
|
|
306
|
+
const json = toJson(value, "dataset value");
|
|
307
|
+
if (Array.isArray(json)) return {
|
|
308
|
+
type: "array",
|
|
309
|
+
items: json.length
|
|
310
|
+
};
|
|
311
|
+
if (json === null) return { type: "null" };
|
|
312
|
+
if (typeof json === "object") return {
|
|
313
|
+
type: "object",
|
|
314
|
+
keys: Object.keys(json).sort()
|
|
315
|
+
};
|
|
316
|
+
return { type: typeof json };
|
|
317
|
+
}
|
|
318
|
+
//#endregion
|
|
34
319
|
//#region ../core/src/canonical-json.ts
|
|
35
320
|
/** Canonical JSON: keys sorted recursively, array order preserved. Rejects non-JSON inputs. */
|
|
36
321
|
function canonicalParameters(value) {
|
|
@@ -176,6 +461,83 @@ function defineEvalMatrix(definition) {
|
|
|
176
461
|
};
|
|
177
462
|
}
|
|
178
463
|
//#endregion
|
|
464
|
+
//#region ../core/src/aut-events.ts
|
|
465
|
+
/** Converts an adapter event into a recorded `AutEvent`. */
|
|
466
|
+
function normalizeAutEvent(input, now) {
|
|
467
|
+
if (typeof input !== "object" || input === null) throw new Error("AUT events must be objects");
|
|
468
|
+
const timestamp = input.timestamp ?? now().toISOString();
|
|
469
|
+
switch (input.kind) {
|
|
470
|
+
case "message": return {
|
|
471
|
+
kind: "message",
|
|
472
|
+
role: input.role,
|
|
473
|
+
content: toJson(input.content, "event.content"),
|
|
474
|
+
timestamp
|
|
475
|
+
};
|
|
476
|
+
case "tool-call": return {
|
|
477
|
+
kind: "tool-call",
|
|
478
|
+
id: input.id,
|
|
479
|
+
name: input.name,
|
|
480
|
+
arguments: input.arguments === void 0 ? {} : toJson(input.arguments, "event.arguments"),
|
|
481
|
+
timestamp
|
|
482
|
+
};
|
|
483
|
+
case "tool-result": return {
|
|
484
|
+
kind: "tool-result",
|
|
485
|
+
id: input.id,
|
|
486
|
+
...input.name === void 0 ? {} : { name: input.name },
|
|
487
|
+
result: input.result === void 0 ? null : toJson(input.result, "event.result"),
|
|
488
|
+
timestamp
|
|
489
|
+
};
|
|
490
|
+
case "completed": return {
|
|
491
|
+
kind: "completed",
|
|
492
|
+
...input.output === void 0 ? {} : { output: toJson(input.output, "event.output") },
|
|
493
|
+
timestamp
|
|
494
|
+
};
|
|
495
|
+
case "started":
|
|
496
|
+
case "turn-started":
|
|
497
|
+
case "turn-completed":
|
|
498
|
+
case "error": return {
|
|
499
|
+
...input,
|
|
500
|
+
timestamp
|
|
501
|
+
};
|
|
502
|
+
default: throw new Error(`Unknown AUT event kind: ${String(input.kind)}`);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* The events a returned turn result stands for: each tool call (and its
|
|
507
|
+
* result), then the reply, then the usage as a `turn-completed` event.
|
|
508
|
+
*/
|
|
509
|
+
function turnResultEvents(result, turn, now) {
|
|
510
|
+
if (typeof result === "string") result = { reply: result };
|
|
511
|
+
if (typeof result !== "object" || result === null) throw new Error("send() must return nothing, a string, or a turn result");
|
|
512
|
+
const events = [];
|
|
513
|
+
for (const [index, call] of (result.toolCalls ?? []).entries()) {
|
|
514
|
+
const id = call.id ?? `turn-${turn}-call-${index + 1}`;
|
|
515
|
+
events.push(normalizeAutEvent({
|
|
516
|
+
kind: "tool-call",
|
|
517
|
+
id,
|
|
518
|
+
name: call.name,
|
|
519
|
+
arguments: call.arguments
|
|
520
|
+
}, now));
|
|
521
|
+
if (call.result !== void 0) events.push(normalizeAutEvent({
|
|
522
|
+
kind: "tool-result",
|
|
523
|
+
id,
|
|
524
|
+
name: call.name,
|
|
525
|
+
result: call.result
|
|
526
|
+
}, now));
|
|
527
|
+
}
|
|
528
|
+
if (result.reply !== void 0) events.push(normalizeAutEvent({
|
|
529
|
+
kind: "message",
|
|
530
|
+
role: "assistant",
|
|
531
|
+
content: result.reply
|
|
532
|
+
}, now));
|
|
533
|
+
if (result.usage) events.push(normalizeAutEvent({
|
|
534
|
+
kind: "turn-completed",
|
|
535
|
+
turn,
|
|
536
|
+
usage: result.usage
|
|
537
|
+
}, now));
|
|
538
|
+
return events;
|
|
539
|
+
}
|
|
540
|
+
//#endregion
|
|
179
541
|
//#region ../core/src/schema.ts
|
|
180
542
|
/** Runtime schemas for serialized EvalKit report and API boundaries. */
|
|
181
543
|
const RunStatusSchema = Schema.Literal("running", "completed", "failed", "cancelled");
|
|
@@ -217,7 +579,7 @@ const RunSourceSchema = Schema.Struct({
|
|
|
217
579
|
}))
|
|
218
580
|
});
|
|
219
581
|
const RunMetadataSchema = Schema.Struct({
|
|
220
|
-
schemaVersion: Schema.Literal(2, 3, 4),
|
|
582
|
+
schemaVersion: Schema.Literal(2, 3, 4, 5),
|
|
221
583
|
runUri: resourceUriSchema("run"),
|
|
222
584
|
evalId: Schema.String,
|
|
223
585
|
suiteId: Schema.optional(Schema.String),
|
|
@@ -225,10 +587,14 @@ const RunMetadataSchema = Schema.Struct({
|
|
|
225
587
|
matrix: Schema.optional(MatrixCellSchema),
|
|
226
588
|
aut: Schema.optional(AutIdentitySchema),
|
|
227
589
|
startedAt: Schema.String,
|
|
228
|
-
source: Schema.optional(RunSourceSchema)
|
|
590
|
+
source: Schema.optional(RunSourceSchema),
|
|
591
|
+
process: Schema.optional(Schema.Struct({
|
|
592
|
+
pid: Schema.Number,
|
|
593
|
+
hostname: Schema.String
|
|
594
|
+
}))
|
|
229
595
|
});
|
|
230
596
|
const TrialMetadataSchema = Schema.Struct({
|
|
231
|
-
schemaVersion: Schema.Literal(2, 3, 4),
|
|
597
|
+
schemaVersion: Schema.Literal(2, 3, 4, 5),
|
|
232
598
|
runUri: resourceUriSchema("run"),
|
|
233
599
|
trialUri: resourceUriSchema("trial"),
|
|
234
600
|
trialIndex: Schema.Number,
|
|
@@ -236,7 +602,13 @@ const TrialMetadataSchema = Schema.Struct({
|
|
|
236
602
|
parameters: Schema.optional(ParametersSchema),
|
|
237
603
|
matrix: Schema.optional(MatrixCellSchema),
|
|
238
604
|
aut: Schema.optional(AutIdentitySchema),
|
|
239
|
-
startedAt: Schema.String
|
|
605
|
+
startedAt: Schema.String,
|
|
606
|
+
datasets: Schema.optional(Schema.mutable(Schema.Array(Schema.Struct({
|
|
607
|
+
id: Schema.String,
|
|
608
|
+
visibility: Schema.Literal("candidate", "evaluator"),
|
|
609
|
+
source: Schema.Literal("static", "computed"),
|
|
610
|
+
digest: Schema.String
|
|
611
|
+
}))))
|
|
240
612
|
});
|
|
241
613
|
/** On-disk manifests include a lifecycle status; older finalized manifests may still say running. */
|
|
242
614
|
const RunManifestSchema = Schema.Struct({
|
|
@@ -271,7 +643,8 @@ const ScoreResultSchema = Schema.Struct({
|
|
|
271
643
|
evidence: Schema.optional(JsonValueSchema),
|
|
272
644
|
judge: Schema.optional(JudgeRunInfoSchema),
|
|
273
645
|
durationMs: Schema.Number,
|
|
274
|
-
error: Schema.optional(RecordedErrorSchema)
|
|
646
|
+
error: Schema.optional(RecordedErrorSchema),
|
|
647
|
+
skipped: Schema.optional(Schema.Literal(true))
|
|
275
648
|
});
|
|
276
649
|
const CheckpointResultSchema = Schema.Struct({
|
|
277
650
|
step: Schema.Number,
|
|
@@ -317,6 +690,7 @@ const RunSummarySchema = Schema.Struct({
|
|
|
317
690
|
trialCount: Schema.Number,
|
|
318
691
|
passed: Schema.Number,
|
|
319
692
|
failed: Schema.Number,
|
|
693
|
+
cancelled: Schema.optional(Schema.Number),
|
|
320
694
|
error: Schema.optional(RecordedErrorSchema)
|
|
321
695
|
});
|
|
322
696
|
const autEvent = {
|
|
@@ -410,7 +784,7 @@ const runnerEvent = {
|
|
|
410
784
|
source: Schema.Literal("runner"),
|
|
411
785
|
kind: Schema.Literal("checkpoint-completed"),
|
|
412
786
|
step: Schema.Number,
|
|
413
|
-
status: Schema.Literal("passed", "failed"),
|
|
787
|
+
status: Schema.Literal("passed", "failed", "skipped"),
|
|
414
788
|
timestamp: Schema.String
|
|
415
789
|
}),
|
|
416
790
|
checkpointError: Schema.Struct({
|
|
@@ -430,7 +804,8 @@ const runnerEvent = {
|
|
|
430
804
|
source: Schema.Literal("runner"),
|
|
431
805
|
kind: Schema.Literal("scorer-completed"),
|
|
432
806
|
scorer: Schema.String,
|
|
433
|
-
value: Schema.Number,
|
|
807
|
+
value: Schema.optional(Schema.Number),
|
|
808
|
+
skipped: Schema.optional(Schema.Literal(true)),
|
|
434
809
|
timestamp: Schema.String
|
|
435
810
|
}),
|
|
436
811
|
scorerFailed: Schema.Struct({
|
|
@@ -474,52 +849,20 @@ function parseTrajectoryJsonl(contents) {
|
|
|
474
849
|
}
|
|
475
850
|
//#endregion
|
|
476
851
|
//#region ../core/src/index.ts
|
|
852
|
+
internalModule(import.meta.url);
|
|
853
|
+
/** Used when an agent declares no runtimes. */
|
|
854
|
+
const DEFAULT_AGENT_RUNTIMES = { local: { kind: "in-process" } };
|
|
855
|
+
/** The runtimes an agent can run in, applying the default. */
|
|
856
|
+
function agentRuntimes(agent) {
|
|
857
|
+
return agent.runtimes && Object.keys(agent.runtimes).length ? agent.runtimes : DEFAULT_AGENT_RUNTIMES;
|
|
858
|
+
}
|
|
477
859
|
/**
|
|
478
860
|
* Defines an Agent Under Test without coupling it to a runtime or transport.
|
|
479
861
|
* It preserves the adapter's concrete type for application-specific helpers.
|
|
480
862
|
*/
|
|
481
863
|
function defineAgent(agent) {
|
|
482
864
|
if (agent.identity) authoringId(agent.identity);
|
|
483
|
-
return agent;
|
|
484
|
-
}
|
|
485
|
-
function defaultFixtureDestination(src) {
|
|
486
|
-
const normalized = src.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
487
|
-
const destination = normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
488
|
-
if (!destination || destination === "." || destination === "..") throw new Error(`Fixture source needs a basename: ${src}`);
|
|
489
|
-
return destination;
|
|
490
|
-
}
|
|
491
|
-
/**
|
|
492
|
-
* Declares a directory relative to the invoking project's working directory.
|
|
493
|
-
* The short form is candidate-visible and copies beneath the source basename.
|
|
494
|
-
*/
|
|
495
|
-
function directory(src, options = {}) {
|
|
496
|
-
return {
|
|
497
|
-
kind: "directory",
|
|
498
|
-
src,
|
|
499
|
-
dst: options.dst ?? defaultFixtureDestination(src),
|
|
500
|
-
visibility: options.visibility ?? "candidate"
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
function file(src, options) {
|
|
504
|
-
return {
|
|
505
|
-
kind: "file",
|
|
506
|
-
src,
|
|
507
|
-
...options
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
function inlineFile(file, data, visibility) {
|
|
511
|
-
return {
|
|
512
|
-
kind: "inline",
|
|
513
|
-
file,
|
|
514
|
-
data,
|
|
515
|
-
visibility
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
function dynamic(create) {
|
|
519
|
-
return {
|
|
520
|
-
kind: "dynamic",
|
|
521
|
-
create
|
|
522
|
-
};
|
|
865
|
+
return markDefinitionSite(agent);
|
|
523
866
|
}
|
|
524
867
|
/** Observes a tool call in the preceding turn; never invokes a tool. */
|
|
525
868
|
function expectToolCall(expected) {
|
|
@@ -542,30 +885,51 @@ function agent(expectation) {
|
|
|
542
885
|
expectation
|
|
543
886
|
};
|
|
544
887
|
}
|
|
545
|
-
|
|
888
|
+
/**
|
|
889
|
+
* Marks a scorer as not applicable to this trial. A skipped result neither
|
|
890
|
+
* passes nor fails the trial and is left out of score averages.
|
|
891
|
+
*/
|
|
892
|
+
function skip(reason) {
|
|
893
|
+
if (!reason.trim()) throw new Error("skip() needs a reason");
|
|
546
894
|
return {
|
|
895
|
+
skipped: true,
|
|
896
|
+
reason
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function predicate(name, run, options = {}) {
|
|
900
|
+
if (!name.trim()) throw new Error("Predicate name must not be empty");
|
|
901
|
+
if (options.passAt !== void 0 && !(Number.isFinite(options.passAt) && options.passAt >= 0 && options.passAt <= 1)) throw new Error(`Predicate ${name} passAt must be a number from 0 to 1; received ${options.passAt}`);
|
|
902
|
+
return markDefinitionSite({
|
|
547
903
|
kind: "predicate",
|
|
548
904
|
name,
|
|
549
905
|
run,
|
|
550
906
|
...options
|
|
551
|
-
};
|
|
907
|
+
});
|
|
552
908
|
}
|
|
553
909
|
function judge(name, options) {
|
|
554
910
|
if (!name.trim() || !options.rubric.trim()) throw new Error("Judge name and rubric must not be empty");
|
|
555
|
-
return {
|
|
911
|
+
return markDefinitionSite({
|
|
556
912
|
kind: "judge",
|
|
557
913
|
name,
|
|
558
914
|
...options
|
|
559
|
-
};
|
|
915
|
+
});
|
|
560
916
|
}
|
|
561
917
|
function defineEval(definition) {
|
|
562
918
|
authoringId(definition);
|
|
919
|
+
if ("fixtures" in definition) throw new Error(`Eval ${definition.id} uses fixtures, which were replaced by datasets: [defineDataset(...)]`);
|
|
920
|
+
if (definition.metadata !== void 0) toJsonObject(definition.metadata, `${definition.id}.metadata`);
|
|
921
|
+
const datasetIds = /* @__PURE__ */ new Set();
|
|
922
|
+
for (const [index, dataset] of (definition.datasets ?? []).entries()) {
|
|
923
|
+
if (!isDataset(dataset)) throw new Error(`Eval ${definition.id} datasets[${index}] is not a dataset; create it with defineDataset(...)`);
|
|
924
|
+
if (datasetIds.has(dataset.id)) throw new Error(`Eval ${definition.id} uses dataset ${dataset.id} more than once`);
|
|
925
|
+
datasetIds.add(dataset.id);
|
|
926
|
+
}
|
|
563
927
|
let hasUser = false;
|
|
564
928
|
for (const step of definition.transcript) if (step.kind === "user") hasUser = true;
|
|
565
929
|
else if (step.kind !== "agent" && !hasUser) throw new Error("Checkpoint requires a preceding user step");
|
|
566
930
|
if ((definition.transcript.some((step) => step.kind === "judge") || definition.scoring.some((rule) => rule.kind === "judge")) && !definition.judge) throw new Error("Judge rules require an eval judge agent");
|
|
567
931
|
if (definition.judge && definition.judge === definition.agent) throw new Error("Judge agent must be distinct from the agent under test");
|
|
568
|
-
return definition;
|
|
932
|
+
return markDefinitionSite(definition);
|
|
569
933
|
}
|
|
570
934
|
function defineSuite(suite) {
|
|
571
935
|
authoringId(suite);
|
|
@@ -633,45 +997,7 @@ function registerEvals(registrations) {
|
|
|
633
997
|
evalIds: suite.evals.map(authoringId),
|
|
634
998
|
...suite.name ? { name: suite.name } : {}
|
|
635
999
|
})),
|
|
636
|
-
catalog: () => evals.map((evaluation) =>
|
|
637
|
-
const suite = suiteIdByEvalId.get(authoringId(evaluation));
|
|
638
|
-
const identity = evaluation.agent.identity;
|
|
639
|
-
return {
|
|
640
|
-
id: evaluation.id,
|
|
641
|
-
path: suite ? `${suite.id}#${evaluation.id}` : evaluation.id,
|
|
642
|
-
...evaluation.name ? { name: evaluation.name } : {},
|
|
643
|
-
...suite ? { suiteId: suite.id } : {},
|
|
644
|
-
agent: {
|
|
645
|
-
...identity?.name ? { name: identity.name } : {},
|
|
646
|
-
kind: identity?.kind ?? "adapter",
|
|
647
|
-
...identity?.id ? { id: identity.id } : {},
|
|
648
|
-
...identity?.version ? { version: identity.version } : {},
|
|
649
|
-
runtimes: Object.entries(evaluation.agent.runtimes ?? {}).map(([name, runtime]) => ({
|
|
650
|
-
name,
|
|
651
|
-
kind: runtime.kind
|
|
652
|
-
}))
|
|
653
|
-
},
|
|
654
|
-
fixtures: (evaluation.fixtures ?? []).map((fixture) => fixture.kind === "dynamic" ? {
|
|
655
|
-
kind: "dynamic",
|
|
656
|
-
source: "dynamic"
|
|
657
|
-
} : fixture.kind === "inline" ? {
|
|
658
|
-
kind: "inline",
|
|
659
|
-
source: fixture.file,
|
|
660
|
-
destination: fixture.file,
|
|
661
|
-
visibility: fixture.visibility
|
|
662
|
-
} : {
|
|
663
|
-
kind: fixture.kind,
|
|
664
|
-
source: fixture.src,
|
|
665
|
-
destination: fixture.dst,
|
|
666
|
-
visibility: fixture.visibility
|
|
667
|
-
}),
|
|
668
|
-
trialCount: evaluation.policy?.trials ?? 1,
|
|
669
|
-
scorers: evaluation.scoring.map(({ name, kind }) => ({
|
|
670
|
-
name,
|
|
671
|
-
kind
|
|
672
|
-
}))
|
|
673
|
-
};
|
|
674
|
-
})
|
|
1000
|
+
catalog: (options) => evals.map((evaluation) => catalogEntry(evaluation, suiteIdByEvalId.get(authoringId(evaluation)), options))
|
|
675
1001
|
};
|
|
676
1002
|
}
|
|
677
1003
|
function recordError(error) {
|
|
@@ -686,6 +1012,4 @@ function recordError(error) {
|
|
|
686
1012
|
};
|
|
687
1013
|
}
|
|
688
1014
|
//#endregion
|
|
689
|
-
export {
|
|
690
|
-
|
|
691
|
-
//# sourceMappingURL=src-ButXPY8P.mjs.map
|
|
1015
|
+
export { resourceUriSchema as $, TrialManifestSchema as A, NotJsonError as B, RunManifestSchema as C, RunSummarySchema as D, RunStatusSchema as E, normalizeAutEvent as F, relativeDatasetPath as G, toJsonObject as H, turnResultEvents as I, ResourceUriSchema as J, definitionSite as K, defineEvalMatrix as L, TrialScoringSchema as M, TrialSummarySchema as N, ScoreResultSchema as O, UsageSchema as P, resourceUri as Q, canonicalParameters as R, RecordedErrorSchema as S, RunSourceSchema as T, defineDataset as U, toJson as V, isDataset as W, authorId as X, UuidSchema as Y, parseResourceUri as Z, ArtifactEntrySchema as _, defineAgent as a, JsonValueSchema as b, expectToolCall as c, recordError as d, resourceUuid as et, registerEvals as f, ApiErrorSchema as g, parseTrajectoryJsonl as h, authoringId as i, TrialMetadataSchema as j, TrajectoryEventSchema as k, judge as l, user as m, agent as n, defineEval as o, skip as p, ResourceKindSchema as q, agentRuntimes as r, defineSuite as s, DEFAULT_AGENT_RUNTIMES as t, predicate as u, AutIdentitySchema as v, RunMetadataSchema as w, JudgeRunInfoSchema as x, CheckpointResultSchema as y, catalogEntry as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leostera/evalkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Author and run agent evals as code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"effect": "^3.22.2",
|
|
50
|
-
"hono": "^4.11.6"
|
|
50
|
+
"hono": "^4.11.6",
|
|
51
|
+
"jiti": "^2.7.0"
|
|
51
52
|
},
|
|
52
53
|
"scripts": {
|
|
53
54
|
"build": "bun run --filter './packages/*' build && bun scripts/sync-package.ts",
|