@crowandraven/evalkeeper 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +6 -2
- package/bin/evalkeeper.mjs +9 -0
- package/dist/attention.d.ts +49 -0
- package/dist/attention.js +48 -0
- package/dist/attention.js.map +1 -0
- package/dist/board.d.ts +55 -0
- package/dist/board.js +2 -0
- package/dist/board.js.map +1 -0
- package/dist/cli/addPages.d.ts +79 -0
- package/dist/cli/addPages.js +181 -0
- package/dist/cli/addPages.js.map +1 -0
- package/dist/cli/main.d.ts +18 -0
- package/dist/cli/main.js +115 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/common.d.ts +72 -0
- package/dist/common.js +276 -0
- package/dist/common.js.map +1 -0
- package/dist/compare.d.ts +85 -0
- package/dist/compare.js +185 -0
- package/dist/compare.js.map +1 -0
- package/dist/decisions.d.ts +84 -0
- package/dist/decisions.js +146 -0
- package/dist/decisions.js.map +1 -0
- package/dist/feedback.d.ts +114 -0
- package/dist/feedback.js +116 -0
- package/dist/feedback.js.map +1 -0
- package/dist/format.d.ts +6 -0
- package/dist/format.js +22 -0
- package/dist/format.js.map +1 -0
- package/dist/glossary.d.ts +46 -0
- package/dist/glossary.js +45 -0
- package/dist/glossary.js.map +1 -0
- package/dist/health.d.ts +40 -0
- package/dist/health.js +101 -0
- package/dist/health.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/labels.d.ts +8 -0
- package/dist/labels.js +15 -0
- package/dist/labels.js.map +1 -0
- package/dist/legs.d.ts +23 -0
- package/dist/legs.js +22 -0
- package/dist/legs.js.map +1 -0
- package/dist/memoryStore.d.ts +21 -0
- package/dist/memoryStore.js +228 -0
- package/dist/memoryStore.js.map +1 -0
- package/dist/models.d.ts +50 -0
- package/dist/models.js +39 -0
- package/dist/models.js.map +1 -0
- package/dist/prune.d.ts +48 -0
- package/dist/prune.js +77 -0
- package/dist/prune.js.map +1 -0
- package/dist/quickSet.d.ts +26 -0
- package/dist/quickSet.js +44 -0
- package/dist/quickSet.js.map +1 -0
- package/dist/runner.d.ts +193 -0
- package/dist/runner.js +706 -0
- package/dist/runner.js.map +1 -0
- package/dist/settings.d.ts +157 -0
- package/dist/settings.js +183 -0
- package/dist/settings.js.map +1 -0
- package/dist/stableJson.d.ts +11 -0
- package/dist/stableJson.js +15 -0
- package/dist/stableJson.js.map +1 -0
- package/dist/stats.d.ts +98 -0
- package/dist/stats.js +119 -0
- package/dist/stats.js.map +1 -0
- package/dist/store.d.ts +146 -0
- package/dist/store.js +3 -0
- package/dist/store.js.map +1 -0
- package/dist/suggest.d.ts +75 -0
- package/dist/suggest.js +150 -0
- package/dist/suggest.js.map +1 -0
- package/dist/summary.d.ts +51 -0
- package/dist/summary.js +61 -0
- package/dist/summary.js.map +1 -0
- package/dist/testing.d.ts +26 -0
- package/dist/testing.js +341 -0
- package/dist/testing.js.map +1 -0
- package/dist/timeout.d.ts +2 -0
- package/dist/timeout.js +15 -0
- package/dist/timeout.js.map +1 -0
- package/dist/types.d.ts +219 -0
- package/dist/types.js +46 -0
- package/dist/types.js.map +1 -0
- package/dist/upkeepRules.d.ts +28 -0
- package/dist/upkeepRules.js +40 -0
- package/dist/upkeepRules.js.map +1 -0
- package/dist/util.d.ts +8 -0
- package/dist/util.js +18 -0
- package/dist/util.js.map +1 -0
- package/dist/weekly.d.ts +80 -0
- package/dist/weekly.js +111 -0
- package/dist/weekly.js.map +1 -0
- package/package.json +81 -4
- package/src/attention.ts +88 -0
- package/src/board.ts +56 -0
- package/src/cli/addPages.ts +256 -0
- package/src/cli/main.ts +125 -0
- package/src/common.ts +338 -0
- package/src/compare.ts +261 -0
- package/src/decisions.ts +230 -0
- package/src/feedback.ts +225 -0
- package/src/format.ts +19 -0
- package/src/glossary.ts +75 -0
- package/src/health.ts +147 -0
- package/src/index.ts +33 -0
- package/src/labels.ts +18 -0
- package/src/legs.ts +38 -0
- package/src/memoryStore.ts +268 -0
- package/src/models.ts +82 -0
- package/src/prune.ts +95 -0
- package/src/quickSet.ts +55 -0
- package/src/runner.ts +971 -0
- package/src/settings.ts +331 -0
- package/src/stableJson.ts +24 -0
- package/src/stats.ts +215 -0
- package/src/store.ts +167 -0
- package/src/suggest.ts +245 -0
- package/src/summary.ts +109 -0
- package/src/testing.ts +392 -0
- package/src/timeout.ts +14 -0
- package/src/types.ts +252 -0
- package/src/upkeepRules.ts +46 -0
- package/src/util.ts +17 -0
- package/src/weekly.ts +167 -0
- package/starter/index.ts +86 -0
- package/templates/app/api/cron/evals-weekly/route.ts +24 -0
- package/templates/app/evals/[suite]/page.tsx +449 -0
- package/templates/app/evals/[suite]/runs/[runId]/page.tsx +95 -0
- package/templates/app/evals/feedback/page.tsx +219 -0
- package/templates/app/evals/page.tsx +313 -0
- package/templates/components/evalkeeper/AddCase.tsx +62 -0
- package/templates/components/evalkeeper/AnswerKey.tsx +360 -0
- package/templates/components/evalkeeper/CaseMenu.tsx +122 -0
- package/templates/components/evalkeeper/Fields.tsx +110 -0
- package/templates/components/evalkeeper/RefreshWhile.tsx +25 -0
- package/templates/components/evalkeeper/SettingsHistory.tsx +191 -0
- package/templates/components/evalkeeper/Submit.tsx +58 -0
- package/templates/components/evalkeeper/Thumbs.tsx +117 -0
- package/templates/components/evalkeeper/Tip.tsx +98 -0
- package/templates/components/evalkeeper/WeeklyCheck.tsx +124 -0
- package/templates/components/evalkeeper/evalkeeper.css +71 -0
- package/templates/components/evalkeeper/ui.tsx +103 -0
- package/templates/lib/evalkeeper/actions.ts +263 -0
- package/templates/lib/evalkeeper/cronAuth.ts +18 -0
- package/templates/lib/evalkeeper/paths.ts +11 -0
- package/templates/lib/evalkeeper/time.ts +4 -0
- package/templates/lib/evalkeeper/weekly.ts +51 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AAmC5E,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAC1B,MAAoB,EACpB,KAAa;IAEb,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtD,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;QACxE,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;AACL,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,SAAS,CACvB,MAAoB,EACpB,KAAa,EACb,MAAyC;IAEzC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,WAAW,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5E,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC;AAC7E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,MAAoB,EACpB,KAAa,EACb,SAA6C;IAE7C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACvC,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC7C,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAClD,OAAO,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC"}
|
package/dist/prune.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeping an answer key useful: cases every model gets right (easy), cases
|
|
3
|
+
* models split on (worth a review), near-duplicates, drafts nobody
|
|
4
|
+
* reviewed, and how the tags spread. Pure; the suite page shows these and
|
|
5
|
+
* the person decides, except that stale drafts retire themselves weekly
|
|
6
|
+
* (retiring is reversible).
|
|
7
|
+
*/
|
|
8
|
+
/** Easy needs at least this many current models, all fully right. */
|
|
9
|
+
export declare const EASY_MIN_MODELS = 3;
|
|
10
|
+
/** Split: this many models or more, with 40–60% fully right. */
|
|
11
|
+
export declare const SPLIT_MIN_MODELS = 4;
|
|
12
|
+
/** Easy cases kept as a regression floor, as a share of cases in use. */
|
|
13
|
+
export declare const EASY_FLOOR = 0.1;
|
|
14
|
+
/** Drafts unreviewed this long retire. */
|
|
15
|
+
export declare const STALE_DRAFT_DAYS = 30;
|
|
16
|
+
/** Inputs this similar (shared words) are near-duplicates. */
|
|
17
|
+
export declare const DUPLICATE_SIMILARITY = 0.9;
|
|
18
|
+
/** How one case did across the models tested on it. */
|
|
19
|
+
export type CaseResult = {
|
|
20
|
+
passed: number;
|
|
21
|
+
models: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* A case every tested model got right (with enough models): it no longer tells them apart.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isEasy(r: CaseResult | undefined): boolean;
|
|
27
|
+
/** A case some models get right and others wrong: the most useful kind. */
|
|
28
|
+
export declare function isSplit(r: CaseResult | undefined): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Easy cases to retire, keeping a floor (smoke cases first, then the
|
|
31
|
+
* oldest) so the suite still catches a regression on the basics.
|
|
32
|
+
*/
|
|
33
|
+
export declare function easyToRetire<C extends {
|
|
34
|
+
id: string;
|
|
35
|
+
smoke: boolean;
|
|
36
|
+
createdAt: Date;
|
|
37
|
+
}>(easy: C[], inUse: number): C[];
|
|
38
|
+
/** Pairs of cases whose inputs share nearly all their words. */
|
|
39
|
+
export declare function nearDuplicates<C extends {
|
|
40
|
+
id: string;
|
|
41
|
+
input: unknown;
|
|
42
|
+
}>(cases: C[]): [C, C][];
|
|
43
|
+
/** A draft nobody has reviewed for long enough to retire. */
|
|
44
|
+
export declare function isStaleDraft(createdAt: Date, today: Date): boolean;
|
|
45
|
+
/** How many cases in use carry each tag, most first. */
|
|
46
|
+
export declare function tagCounts(cases: {
|
|
47
|
+
tags: string[];
|
|
48
|
+
}[]): [string, number][];
|
package/dist/prune.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { stableJson } from "./stableJson.js";
|
|
2
|
+
import { at } from "./util.js";
|
|
3
|
+
/**
|
|
4
|
+
* Keeping an answer key useful: cases every model gets right (easy), cases
|
|
5
|
+
* models split on (worth a review), near-duplicates, drafts nobody
|
|
6
|
+
* reviewed, and how the tags spread. Pure; the suite page shows these and
|
|
7
|
+
* the person decides, except that stale drafts retire themselves weekly
|
|
8
|
+
* (retiring is reversible).
|
|
9
|
+
*/
|
|
10
|
+
/** Easy needs at least this many current models, all fully right. */
|
|
11
|
+
export const EASY_MIN_MODELS = 3;
|
|
12
|
+
/** Split: this many models or more, with 40–60% fully right. */
|
|
13
|
+
export const SPLIT_MIN_MODELS = 4;
|
|
14
|
+
/** Easy cases kept as a regression floor, as a share of cases in use. */
|
|
15
|
+
export const EASY_FLOOR = 0.1;
|
|
16
|
+
/** Drafts unreviewed this long retire. */
|
|
17
|
+
export const STALE_DRAFT_DAYS = 30;
|
|
18
|
+
/** Inputs this similar (shared words) are near-duplicates. */
|
|
19
|
+
export const DUPLICATE_SIMILARITY = 0.9;
|
|
20
|
+
/**
|
|
21
|
+
* A case every tested model got right (with enough models): it no longer tells them apart.
|
|
22
|
+
*/
|
|
23
|
+
export function isEasy(r) {
|
|
24
|
+
return (r !== undefined && r.models >= EASY_MIN_MODELS && r.passed === r.models);
|
|
25
|
+
}
|
|
26
|
+
/** A case some models get right and others wrong: the most useful kind. */
|
|
27
|
+
export function isSplit(r) {
|
|
28
|
+
if (!r || r.models < SPLIT_MIN_MODELS)
|
|
29
|
+
return false;
|
|
30
|
+
const share = r.passed / r.models;
|
|
31
|
+
return share >= 0.4 && share <= 0.6;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Easy cases to retire, keeping a floor (smoke cases first, then the
|
|
35
|
+
* oldest) so the suite still catches a regression on the basics.
|
|
36
|
+
*/
|
|
37
|
+
export function easyToRetire(easy, inUse) {
|
|
38
|
+
const keep = Math.max(1, Math.ceil(inUse * EASY_FLOOR));
|
|
39
|
+
const ordered = [...easy].sort((a, b) => Number(b.smoke) - Number(a.smoke) || +a.createdAt - +b.createdAt);
|
|
40
|
+
return ordered.slice(keep);
|
|
41
|
+
}
|
|
42
|
+
const words = (input) => new Set(stableJson(input)
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.match(/[a-z0-9]{3,}/g) ?? []);
|
|
45
|
+
/** Pairs of cases whose inputs share nearly all their words. */
|
|
46
|
+
export function nearDuplicates(cases) {
|
|
47
|
+
const sets = cases.map((c) => ({ c, w: words(c.input) }));
|
|
48
|
+
const pairs = [];
|
|
49
|
+
for (let i = 0; i < sets.length; i++)
|
|
50
|
+
for (let j = i + 1; j < sets.length; j++) {
|
|
51
|
+
const x = at(sets, i);
|
|
52
|
+
const y = at(sets, j);
|
|
53
|
+
const a = x.w;
|
|
54
|
+
const b = y.w;
|
|
55
|
+
let shared = 0;
|
|
56
|
+
for (const w of a)
|
|
57
|
+
if (b.has(w))
|
|
58
|
+
shared++;
|
|
59
|
+
const union = a.size + b.size - shared;
|
|
60
|
+
if (union && shared / union >= DUPLICATE_SIMILARITY)
|
|
61
|
+
pairs.push([x.c, y.c]);
|
|
62
|
+
}
|
|
63
|
+
return pairs;
|
|
64
|
+
}
|
|
65
|
+
/** A draft nobody has reviewed for long enough to retire. */
|
|
66
|
+
export function isStaleDraft(createdAt, today) {
|
|
67
|
+
return +today - +createdAt > STALE_DRAFT_DAYS * 86_400_000;
|
|
68
|
+
}
|
|
69
|
+
/** How many cases in use carry each tag, most first. */
|
|
70
|
+
export function tagCounts(cases) {
|
|
71
|
+
const counts = new Map();
|
|
72
|
+
for (const c of cases)
|
|
73
|
+
for (const t of c.tags)
|
|
74
|
+
counts.set(t, (counts.get(t) ?? 0) + 1);
|
|
75
|
+
return [...counts].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.js","sourceRoot":"","sources":["../src/prune.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quick-check set (internally SMOKE): a few cases for a cheap first
|
|
3
|
+
* look, picked by the app rather than by hand. Greedy: confirmed cases
|
|
4
|
+
* first, each pick covering the most tags not yet covered, oldest first
|
|
5
|
+
* on ties, so the set is stable and spans what the dataset tests. Pure.
|
|
6
|
+
*/
|
|
7
|
+
/** How many cases the quick set holds. */
|
|
8
|
+
export declare const QUICK_SET_SIZE = 8;
|
|
9
|
+
/**
|
|
10
|
+
* Picks the quick set: confirmed cases first, each pick covering the most tags not yet
|
|
11
|
+
* covered, oldest first on ties, so the set is stable.
|
|
12
|
+
*/
|
|
13
|
+
export declare function pickQuickSet<T extends {
|
|
14
|
+
id: string;
|
|
15
|
+
tags: string[];
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
confirmed: boolean;
|
|
18
|
+
}>(cases: T[], size?: number): T[];
|
|
19
|
+
/**
|
|
20
|
+
* DEV or TEST for a new case: keeps about 40% of a suite's cases in TEST,
|
|
21
|
+
* the set decisions are made on and instructions are never tuned against.
|
|
22
|
+
*/
|
|
23
|
+
export declare function nextSplit(counts: {
|
|
24
|
+
DEV: number;
|
|
25
|
+
TEST: number;
|
|
26
|
+
}): "DEV" | "TEST";
|
package/dist/quickSet.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { at } from "./util.js";
|
|
2
|
+
/**
|
|
3
|
+
* The quick-check set (internally SMOKE): a few cases for a cheap first
|
|
4
|
+
* look, picked by the app rather than by hand. Greedy: confirmed cases
|
|
5
|
+
* first, each pick covering the most tags not yet covered, oldest first
|
|
6
|
+
* on ties, so the set is stable and spans what the dataset tests. Pure.
|
|
7
|
+
*/
|
|
8
|
+
/** How many cases the quick set holds. */
|
|
9
|
+
export const QUICK_SET_SIZE = 8;
|
|
10
|
+
/**
|
|
11
|
+
* Picks the quick set: confirmed cases first, each pick covering the most tags not yet
|
|
12
|
+
* covered, oldest first on ties, so the set is stable.
|
|
13
|
+
*/
|
|
14
|
+
export function pickQuickSet(cases, size = QUICK_SET_SIZE) {
|
|
15
|
+
const pool = [...cases].sort((a, b) => Number(b.confirmed) - Number(a.confirmed) ||
|
|
16
|
+
+a.createdAt - +b.createdAt ||
|
|
17
|
+
a.id.localeCompare(b.id));
|
|
18
|
+
const covered = new Set();
|
|
19
|
+
const picked = [];
|
|
20
|
+
while (picked.length < size && pool.length) {
|
|
21
|
+
let best = 0;
|
|
22
|
+
let bestGain = -1;
|
|
23
|
+
pool.forEach((c, i) => {
|
|
24
|
+
const gain = (c.confirmed ? 100 : 0) + c.tags.filter((t) => !covered.has(t)).length;
|
|
25
|
+
if (gain > bestGain) {
|
|
26
|
+
bestGain = gain;
|
|
27
|
+
best = i;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const c = at(pool.splice(best, 1), 0);
|
|
31
|
+
c.tags.forEach((t) => covered.add(t));
|
|
32
|
+
picked.push(c);
|
|
33
|
+
}
|
|
34
|
+
return picked;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* DEV or TEST for a new case: keeps about 40% of a suite's cases in TEST,
|
|
38
|
+
* the set decisions are made on and instructions are never tuned against.
|
|
39
|
+
*/
|
|
40
|
+
export function nextSplit(counts) {
|
|
41
|
+
const total = counts.DEV + counts.TEST;
|
|
42
|
+
return counts.TEST < Math.round((total + 1) * 0.4) ? "TEST" : "DEV";
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=quickSet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quickSet.js","sourceRoot":"","sources":["../src/quickSet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC/B;;;;;GAKG;AAEH,0CAA0C;AAC1C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC;;;GAGG;AACH,MAAM,UAAU,YAAY,CAE1B,KAAU,EAAE,IAAI,GAAG,cAAc;IACjC,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAC1B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QACzC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,SAAS;QAC3B,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAC3B,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,OAAO,MAAM,CAAC,MAAM,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAC3C,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACpB,MAAM,IAAI,GACR,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACzE,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;gBACpB,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,GAAG,CAAC,CAAC;YACX,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAGzB;IACC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC;IACvC,OAAO,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;AACtE,CAAC"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { type ModelCatalog, type ReasoningEffort } from "./models.js";
|
|
2
|
+
import type { BoardRow } from "./board.js";
|
|
3
|
+
import type { CaseRecord, EvalSet, EvalStore, RunRecord } from "./store.js";
|
|
4
|
+
import { confirmedLabel, isProvisional } from "./labels.js";
|
|
5
|
+
export { confirmedLabel, isProvisional };
|
|
6
|
+
import type { AnySuite, CheckResult } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Runs eval suites: every case in a set, a number of times, on one model,
|
|
9
|
+
* with the operation's current instructions. Outputs are stored, so a
|
|
10
|
+
* grading change rescores them without calling the model again, and an
|
|
11
|
+
* identical request from the last week is reused instead of paid for
|
|
12
|
+
* twice. Server-only (it hashes with node:crypto).
|
|
13
|
+
*/
|
|
14
|
+
/** An answer that takes longer counts as failed, unless a suite sets its own. */
|
|
15
|
+
export declare const ANSWER_TIMEOUT_MS = 60000;
|
|
16
|
+
/** A test with no new answer for this long was stopped by the server. */
|
|
17
|
+
export declare const STUCK_MS: number;
|
|
18
|
+
/** Identical requests within this many days are reused. */
|
|
19
|
+
export declare const REUSE_DAYS = 7;
|
|
20
|
+
/** A provider refusing calls for billing: every later call fails too. */
|
|
21
|
+
export declare const OUT_OF_CREDITS: RegExp;
|
|
22
|
+
/**
|
|
23
|
+
* What the runner needs from your app: storage, suites, models, instruction hashes, and,
|
|
24
|
+
* for the board and drift checks, routing.
|
|
25
|
+
*/
|
|
26
|
+
export type RunnerOptions = {
|
|
27
|
+
store: EvalStore;
|
|
28
|
+
suites: AnySuite[];
|
|
29
|
+
/** Your models, for providers, prices, and retirements. */
|
|
30
|
+
models: ModelCatalog;
|
|
31
|
+
/**
|
|
32
|
+
* The current instructions' hash for an operation (see instructionsHash
|
|
33
|
+
* in `@crowandraven/evalkeeper-ai-sdk`). A test records it, so editing the instructions
|
|
34
|
+
* marks older tests out of date.
|
|
35
|
+
*/
|
|
36
|
+
promptVersion: (operation: string) => string | Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* The live reasoning setting for an operation; null: the model's own.
|
|
39
|
+
* Defaults to the route's, when `route` is given.
|
|
40
|
+
*/
|
|
41
|
+
liveEffort?: (operation: string) => ReasoningEffort | null | Promise<ReasoningEffort | null>;
|
|
42
|
+
/** What one answer costs on a model, from your usage records; null: unknown. */
|
|
43
|
+
estimateCallCost?: (operation: string, model: string) => Promise<number | null>;
|
|
44
|
+
/**
|
|
45
|
+
* How long one request may run, in ms. A test answers what fits, then
|
|
46
|
+
* hands the rest to `continueRun` (a fresh request). Default: no limit.
|
|
47
|
+
*/
|
|
48
|
+
requestLimitMs?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Starts a fresh request that calls runEval(runId, opts). True when one
|
|
51
|
+
* took it; false (or absent) lets the test fail as out of time.
|
|
52
|
+
*/
|
|
53
|
+
continueRun?: (runId: string, opts: Required<RunEvalOptions>) => Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Which model each operation runs on now, its fallback, and its
|
|
56
|
+
* reasoning setting. Your app owns routing; the board reads it.
|
|
57
|
+
*/
|
|
58
|
+
route?: (operation: string) => Route | Promise<Route>;
|
|
59
|
+
/**
|
|
60
|
+
* The models an operation may use: `models` can be live; `fallbackModels`
|
|
61
|
+
* (default: the same) can stand in during an outage.
|
|
62
|
+
*/
|
|
63
|
+
candidates?: (operation: string) => {
|
|
64
|
+
models: string[];
|
|
65
|
+
fallbackModels?: string[];
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Which model a feature runs on now, its fallback, and its reasoning setting.
|
|
70
|
+
*/
|
|
71
|
+
export type Route = {
|
|
72
|
+
model: string;
|
|
73
|
+
fallbackModel: string | null;
|
|
74
|
+
reasoningEffort: ReasoningEffort | null;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* How to run a test: fresh (no reuse), and, for a test continued in a new request, its leg
|
|
78
|
+
* and the judge's spend so far.
|
|
79
|
+
*/
|
|
80
|
+
export type RunEvalOptions = {
|
|
81
|
+
/** Never reuse an earlier test's output. */
|
|
82
|
+
fresh?: boolean;
|
|
83
|
+
/** Which leg this is (1 for a new test). */
|
|
84
|
+
leg?: number;
|
|
85
|
+
/** What earlier legs' judge spent. */
|
|
86
|
+
judgeCostUsd?: number;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* What makes two requests identical, so the second can reuse the first's
|
|
90
|
+
* answer: the suite, model, reasoning setting, instructions, input, and
|
|
91
|
+
* repeat. One function, so tests and estimates always agree.
|
|
92
|
+
*/
|
|
93
|
+
export declare function requestHashFor(r: {
|
|
94
|
+
suite: string;
|
|
95
|
+
model: string;
|
|
96
|
+
effort: string | null;
|
|
97
|
+
prompt: string;
|
|
98
|
+
input: unknown;
|
|
99
|
+
repeat: number;
|
|
100
|
+
}): string;
|
|
101
|
+
/** Changes whenever a case or its answer in use does. */
|
|
102
|
+
export declare function datasetVersion(cases: CaseRecord[]): string;
|
|
103
|
+
/** The error most failed answers share, in a line; null when none failed. */
|
|
104
|
+
export declare function commonError(errors: (string | null)[]): string | null;
|
|
105
|
+
/** Runs fn over items with at most `limit` in flight, preserving order. */
|
|
106
|
+
export declare function mapLimit<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]>;
|
|
107
|
+
/**
|
|
108
|
+
* The runner: creates and runs tests, rescores them, compares them, and builds each
|
|
109
|
+
* suite's model board, on your store.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const runner = createRunner({
|
|
114
|
+
* store: prismaStore(prisma),
|
|
115
|
+
* suites: [ticketTriage],
|
|
116
|
+
* models: MODELS,
|
|
117
|
+
* promptVersion: (op) => PROMPT_VERSIONS[op] ?? "unregistered",
|
|
118
|
+
* route: settings.route,
|
|
119
|
+
* candidates: () => ({ models: Object.keys(MODELS) }),
|
|
120
|
+
* });
|
|
121
|
+
* const { run } = await runner.createRun({ suite: "ticket_triage", model: "claude-haiku-5-5" });
|
|
122
|
+
* await runner.runEval(run.id);
|
|
123
|
+
* const board = await runner.board("ticket_triage");
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export declare function createRunner(opts: RunnerOptions): {
|
|
127
|
+
startDriftCheck: (suiteKey: string) => Promise<string | null>;
|
|
128
|
+
runDriftCheck: (suiteKey: string) => Promise<string | null>;
|
|
129
|
+
board: (suiteKey: string) => Promise<{
|
|
130
|
+
suite: AnySuite;
|
|
131
|
+
route: Route;
|
|
132
|
+
cases: number;
|
|
133
|
+
caseResults: Record<string, {
|
|
134
|
+
passed: number;
|
|
135
|
+
models: number;
|
|
136
|
+
}>;
|
|
137
|
+
/** Cases in use on an answer nobody has reviewed yet. */
|
|
138
|
+
provisional: number;
|
|
139
|
+
rows: BoardRow[];
|
|
140
|
+
liveRunId: string | null;
|
|
141
|
+
}>;
|
|
142
|
+
suiteOrThrow: (key: string) => AnySuite;
|
|
143
|
+
casesFor: (suite: string, set: EvalSet) => Promise<CaseRecord[]>;
|
|
144
|
+
estimateRun: (suiteKey: string, model: string, set: EvalSet, repeats: number, o?: {
|
|
145
|
+
/** Never reuse: every answer is a new call. */
|
|
146
|
+
fresh?: boolean;
|
|
147
|
+
/** The setting to test; undefined: the operation's live one. */
|
|
148
|
+
reasoningEffort?: ReasoningEffort | null;
|
|
149
|
+
}) => Promise<{
|
|
150
|
+
cases: number;
|
|
151
|
+
/** Answers the test will need: cases times repeats. */
|
|
152
|
+
answers: number;
|
|
153
|
+
/** Of those, answers an identical earlier request will give for free. */
|
|
154
|
+
reused: number;
|
|
155
|
+
costUsd: number | null;
|
|
156
|
+
}>;
|
|
157
|
+
createRun: (args: {
|
|
158
|
+
suite: string;
|
|
159
|
+
model: string;
|
|
160
|
+
set?: EvalSet;
|
|
161
|
+
repeats?: number;
|
|
162
|
+
/** Who started it; null for a scheduled check. */
|
|
163
|
+
startedBy?: string | null;
|
|
164
|
+
/** A reasoning setting to test; undefined: the operation's own. */
|
|
165
|
+
reasoningEffort?: ReasoningEffort | null;
|
|
166
|
+
/** For the estimate: pass the same `fresh` to runEval. */
|
|
167
|
+
fresh?: boolean;
|
|
168
|
+
}) => Promise<{
|
|
169
|
+
run: RunRecord;
|
|
170
|
+
estimate: {
|
|
171
|
+
cases: number;
|
|
172
|
+
/** Answers the test will need: cases times repeats. */
|
|
173
|
+
answers: number;
|
|
174
|
+
/** Of those, answers an identical earlier request will give for free. */
|
|
175
|
+
reused: number;
|
|
176
|
+
costUsd: number | null;
|
|
177
|
+
};
|
|
178
|
+
}>;
|
|
179
|
+
runEval: (runId: string, o?: RunEvalOptions) => Promise<void>;
|
|
180
|
+
gradeOutput: (suite: AnySuite, c: CaseRecord, output: unknown, onCost?: (usd: number) => void) => Promise<CheckResult[]>;
|
|
181
|
+
rescoreRun: (runId: string) => Promise<void>;
|
|
182
|
+
rescoreRecent: (suiteKey: string, limit?: number) => Promise<void>;
|
|
183
|
+
stopStuckRuns: (suiteKey: string, now?: number) => Promise<void>;
|
|
184
|
+
compare: (suiteKey: string, a: string, b: string) => Promise<{
|
|
185
|
+
suite: AnySuite;
|
|
186
|
+
a: RunRecord;
|
|
187
|
+
b: RunRecord;
|
|
188
|
+
comparison: import("./compare.js").Comparison;
|
|
189
|
+
caveats: string[];
|
|
190
|
+
} | null>;
|
|
191
|
+
};
|
|
192
|
+
/** What createRunner returns. */
|
|
193
|
+
export type Runner = ReturnType<typeof createRunner>;
|