jevkit-bench 0.1.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 +64 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +148 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/score.d.ts +105 -0
- package/dist/score.d.ts.map +1 -0
- package/dist/score.js +211 -0
- package/dist/score.js.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# jevkit-bench
|
|
2
|
+
|
|
3
|
+
Score a labeled Jev suite for accuracy **and** cost, and compare two runs.
|
|
4
|
+
|
|
5
|
+
Any one number alone is easy to win. Accuracy without cost hides that you spent
|
|
6
|
+
ten times the tokens; cost without accuracy hides that you broke the task. This
|
|
7
|
+
reports them together.
|
|
8
|
+
|
|
9
|
+
> Unofficial and unaffiliated with TypeSafe.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install jevkit-bench
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Use
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from jevkit_core import read_records
|
|
19
|
+
from jevkit_bench import compare_suites, score_records
|
|
20
|
+
|
|
21
|
+
suite = score_records(read_records("suite.jevl"))
|
|
22
|
+
print(suite.summary())
|
|
23
|
+
|
|
24
|
+
for failure in suite.failures()[:5]:
|
|
25
|
+
print(failure.question_id, failure.predicted, "should be", failure.label)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`failures()` sorts by probability descending, so the most confident wrong answers
|
|
29
|
+
come first. Those are the interesting bugs: a wrong answer at 0.35 is the model
|
|
30
|
+
telling you it was unsure, while a wrong answer at 0.98 is a question that needs
|
|
31
|
+
rewriting.
|
|
32
|
+
|
|
33
|
+
## Comparing runs
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
comparison = compare_suites(baseline, candidate)
|
|
37
|
+
print(comparison.summary())
|
|
38
|
+
print(comparison.regressions) # right before, wrong now
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Aggregate accuracy can hold perfectly steady while the set of things you get
|
|
42
|
+
right churns underneath. That matters when a specific case is the one you
|
|
43
|
+
promised someone would work, so regressions and fixes are tracked individually
|
|
44
|
+
rather than netted off.
|
|
45
|
+
|
|
46
|
+
## CLI
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
jevkit-bench suite.jevl
|
|
50
|
+
jevkit-bench suite.jevl --baseline last-week.jevl
|
|
51
|
+
jevkit-bench suite.jevl --min-accuracy 0.90 # CI gate
|
|
52
|
+
jevkit-bench suite.jevl --baseline last-week.jevl --max-regressions 0
|
|
53
|
+
jevkit-bench suite.jevl --show-failures 10
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Cost
|
|
57
|
+
|
|
58
|
+
Computed from input tokens at $0.042 per million, Jev's published price. Output
|
|
59
|
+
tokens are free on Jev, so they are reported but never billed. Override with
|
|
60
|
+
`--price-per-mtok` if your plan differs.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** `jevkit-bench` command line interface. */
|
|
3
|
+
export declare const EXIT_OK = 0;
|
|
4
|
+
export declare const EXIT_FAILED_GATE = 1;
|
|
5
|
+
export declare const EXIT_USAGE = 2;
|
|
6
|
+
export declare function main(argv?: string[]): number;
|
|
7
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,6CAA6C;AAM7C,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,UAAU,IAAI,CAAC;AAe5B,wBAAgB,IAAI,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,MAAM,CA+GnE"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** `jevkit-bench` command line interface. */
|
|
3
|
+
import { RecordFormatError, readRecords } from "jevkit-core";
|
|
4
|
+
import { PRICE_PER_MTOK, compareSuites, scoreRecords } from "./score.js";
|
|
5
|
+
export const EXIT_OK = 0;
|
|
6
|
+
export const EXIT_FAILED_GATE = 1;
|
|
7
|
+
export const EXIT_USAGE = 2;
|
|
8
|
+
const USAGE = `usage: jevkit-bench <suite.jevl> [options]
|
|
9
|
+
|
|
10
|
+
Score a labeled .jevl suite for accuracy and cost, and compare two runs of it.
|
|
11
|
+
Never calls the API.
|
|
12
|
+
|
|
13
|
+
--baseline FILE a previous run, to compare against
|
|
14
|
+
--question ID restrict to this question id (repeatable)
|
|
15
|
+
--price-per-mtok F input price per million tokens (default ${PRICE_PER_MTOK})
|
|
16
|
+
--min-accuracy F exit non-zero below this
|
|
17
|
+
--max-regressions N exit non-zero above this many (needs --baseline)
|
|
18
|
+
--show-failures N print the N most confident wrong answers
|
|
19
|
+
--format text|json`;
|
|
20
|
+
export function main(argv = process.argv.slice(2)) {
|
|
21
|
+
const files = [];
|
|
22
|
+
const questions = [];
|
|
23
|
+
let baselinePath = null;
|
|
24
|
+
let pricePerMtok = PRICE_PER_MTOK;
|
|
25
|
+
let minAccuracy = null;
|
|
26
|
+
let maxRegressions = null;
|
|
27
|
+
let showFailures = 0;
|
|
28
|
+
let format = "text";
|
|
29
|
+
for (let i = 0; i < argv.length; i++) {
|
|
30
|
+
const arg = argv[i];
|
|
31
|
+
switch (arg) {
|
|
32
|
+
case "--baseline":
|
|
33
|
+
baselinePath = String(argv[++i]);
|
|
34
|
+
break;
|
|
35
|
+
case "--question":
|
|
36
|
+
questions.push(String(argv[++i]));
|
|
37
|
+
break;
|
|
38
|
+
case "--price-per-mtok":
|
|
39
|
+
pricePerMtok = Number(argv[++i]);
|
|
40
|
+
break;
|
|
41
|
+
case "--min-accuracy":
|
|
42
|
+
minAccuracy = Number(argv[++i]);
|
|
43
|
+
break;
|
|
44
|
+
case "--max-regressions":
|
|
45
|
+
maxRegressions = Number(argv[++i]);
|
|
46
|
+
break;
|
|
47
|
+
case "--show-failures":
|
|
48
|
+
showFailures = Number(argv[++i]);
|
|
49
|
+
break;
|
|
50
|
+
case "--format": {
|
|
51
|
+
const v = argv[++i];
|
|
52
|
+
if (v !== "text" && v !== "json") {
|
|
53
|
+
console.error("jevkit-bench: --format expects text or json");
|
|
54
|
+
return EXIT_USAGE;
|
|
55
|
+
}
|
|
56
|
+
format = v;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "-h":
|
|
60
|
+
case "--help":
|
|
61
|
+
console.log(USAGE);
|
|
62
|
+
return EXIT_OK;
|
|
63
|
+
default:
|
|
64
|
+
if (arg.startsWith("--")) {
|
|
65
|
+
console.error(`jevkit-bench: unknown option ${arg}`);
|
|
66
|
+
return EXIT_USAGE;
|
|
67
|
+
}
|
|
68
|
+
files.push(arg);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (files.length !== 1) {
|
|
72
|
+
console.error(USAGE);
|
|
73
|
+
return EXIT_USAGE;
|
|
74
|
+
}
|
|
75
|
+
if (maxRegressions !== null && !baselinePath) {
|
|
76
|
+
console.error("jevkit-bench: --max-regressions needs --baseline");
|
|
77
|
+
return EXIT_USAGE;
|
|
78
|
+
}
|
|
79
|
+
const options = {
|
|
80
|
+
questionIds: questions.length ? questions : undefined,
|
|
81
|
+
pricePerMtok,
|
|
82
|
+
};
|
|
83
|
+
let suite, baseline;
|
|
84
|
+
try {
|
|
85
|
+
suite = scoreRecords(readRecords(files[0]), options);
|
|
86
|
+
baseline = baselinePath ? scoreRecords(readRecords(baselinePath), options) : null;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
const message = err instanceof RecordFormatError ? err.message : err.message;
|
|
90
|
+
console.error(`jevkit-bench: ${message}`);
|
|
91
|
+
return EXIT_USAGE;
|
|
92
|
+
}
|
|
93
|
+
if (!suite.count) {
|
|
94
|
+
console.error("jevkit-bench: nothing scored. Records need a 'label' object keyed by question id.");
|
|
95
|
+
return EXIT_USAGE;
|
|
96
|
+
}
|
|
97
|
+
const comparison = baseline ? compareSuites(baseline, suite) : null;
|
|
98
|
+
if (format === "json") {
|
|
99
|
+
const payload = { ...suite.toJSON() };
|
|
100
|
+
if (comparison) {
|
|
101
|
+
payload["comparison"] = {
|
|
102
|
+
baselineAccuracy: comparison.baseline.accuracy,
|
|
103
|
+
accuracyDelta: comparison.accuracyDelta,
|
|
104
|
+
costDelta: comparison.costDelta,
|
|
105
|
+
regressions: comparison.regressions,
|
|
106
|
+
fixes: comparison.fixes,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(suite.summary());
|
|
113
|
+
if (comparison) {
|
|
114
|
+
console.log();
|
|
115
|
+
console.log(comparison.summary());
|
|
116
|
+
if (comparison.regressions.length) {
|
|
117
|
+
console.log("\nregressions:");
|
|
118
|
+
for (const [rid, qid] of comparison.regressions.slice(0, 20)) {
|
|
119
|
+
console.log(` ${rid.slice(7, 19)} ${qid}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (showFailures) {
|
|
124
|
+
const failures = suite.failures().slice(0, showFailures);
|
|
125
|
+
if (failures.length) {
|
|
126
|
+
console.log(`\nmost confident wrong answers (${failures.length}):`);
|
|
127
|
+
for (const f of failures) {
|
|
128
|
+
console.log(` ${f.requestId.slice(7, 19)} ${f.questionId}: said ` +
|
|
129
|
+
`${JSON.stringify(f.predicted)}, label ${JSON.stringify(f.label)}, ` +
|
|
130
|
+
`p=${f.probability.toFixed(3)}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (minAccuracy !== null && suite.accuracy < minAccuracy) {
|
|
136
|
+
console.error(`jevkit-bench: accuracy ${suite.accuracy.toFixed(4)} below ${minAccuracy}`);
|
|
137
|
+
return EXIT_FAILED_GATE;
|
|
138
|
+
}
|
|
139
|
+
if (maxRegressions !== null && comparison && comparison.regressions.length > maxRegressions) {
|
|
140
|
+
console.error(`jevkit-bench: ${comparison.regressions.length} regressions exceed ${maxRegressions}`);
|
|
141
|
+
return EXIT_FAILED_GATE;
|
|
142
|
+
}
|
|
143
|
+
return EXIT_OK;
|
|
144
|
+
}
|
|
145
|
+
const invokedDirectly = process.argv[1] !== undefined && import.meta.url === `file://${process.argv[1]}`;
|
|
146
|
+
if (invokedDirectly)
|
|
147
|
+
process.exit(main());
|
|
148
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Score a labeled Jev suite for accuracy and cost, and compare runs.
|
|
3
|
+
*
|
|
4
|
+
* Answers the question you have to defend: for this task, on my data, is Jev
|
|
5
|
+
* good enough and what does it cost? Accuracy, tokens and dollars together,
|
|
6
|
+
* because any one of them alone is easy to win.
|
|
7
|
+
*/
|
|
8
|
+
export { PRICE_PER_MTOK, SuiteComparison, SuiteResult, compareSuites, scoreRecords, type QuestionResult, type ScoreOptions, } from "./score.js";
|
|
9
|
+
export declare const VERSION = "0.1.0";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EACzE,KAAK,cAAc,EAAE,KAAK,YAAY,GACvC,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Score a labeled Jev suite for accuracy and cost, and compare runs.
|
|
3
|
+
*
|
|
4
|
+
* Answers the question you have to defend: for this task, on my data, is Jev
|
|
5
|
+
* good enough and what does it cost? Accuracy, tokens and dollars together,
|
|
6
|
+
* because any one of them alone is easy to win.
|
|
7
|
+
*/
|
|
8
|
+
export { PRICE_PER_MTOK, SuiteComparison, SuiteResult, compareSuites, scoreRecords, } from "./score.js";
|
|
9
|
+
export const VERSION = "0.1.0";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,GAE1E,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
package/dist/score.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoring a labeled `.jevl` suite, and comparing two runs of it.
|
|
3
|
+
*
|
|
4
|
+
* The question this answers is the one you actually have to defend: for this
|
|
5
|
+
* task, on my data, is Jev good enough, and what does it cost? That needs
|
|
6
|
+
* accuracy and cost together, because any one of them alone is easy to win.
|
|
7
|
+
*
|
|
8
|
+
* Costs come from a price per million input tokens, defaulting to Jev's
|
|
9
|
+
* published $0.042. Output tokens are free on Jev and are reported but never
|
|
10
|
+
* billed.
|
|
11
|
+
*/
|
|
12
|
+
import { type Record as JevlRecord } from "jevkit-core";
|
|
13
|
+
export declare const PRICE_PER_MTOK = 0.042;
|
|
14
|
+
/** One scored question from one record. */
|
|
15
|
+
export interface QuestionResult {
|
|
16
|
+
requestId: string;
|
|
17
|
+
questionId: string;
|
|
18
|
+
type: string;
|
|
19
|
+
predicted: unknown;
|
|
20
|
+
label: unknown;
|
|
21
|
+
correct: boolean;
|
|
22
|
+
probability: number;
|
|
23
|
+
confidence: number | null;
|
|
24
|
+
tags: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
/** Everything scored from one run of a suite. */
|
|
27
|
+
export declare class SuiteResult {
|
|
28
|
+
readonly pricePerMtok: number;
|
|
29
|
+
results: QuestionResult[];
|
|
30
|
+
inputTokens: number;
|
|
31
|
+
outputTokens: number;
|
|
32
|
+
records: number;
|
|
33
|
+
unlabeled: number;
|
|
34
|
+
models: Set<string>;
|
|
35
|
+
constructor(pricePerMtok?: number);
|
|
36
|
+
get count(): number;
|
|
37
|
+
get correct(): number;
|
|
38
|
+
get accuracy(): number;
|
|
39
|
+
/** Input-token cost in dollars. Jev bills input only. */
|
|
40
|
+
get cost(): number;
|
|
41
|
+
get costPerQuestion(): number;
|
|
42
|
+
/** tag -> [n, accuracy]. A result with several tags counts under each. */
|
|
43
|
+
byTag(): Record<string, [number, number]>;
|
|
44
|
+
/** question id -> [n, accuracy]. Finds the one question dragging the suite. */
|
|
45
|
+
byQuestion(): Record<string, [number, number]>;
|
|
46
|
+
/** Wrong answers, most confident first: the most interesting bugs. */
|
|
47
|
+
failures(): QuestionResult[];
|
|
48
|
+
summary(): string;
|
|
49
|
+
toJSON(): {
|
|
50
|
+
records: number;
|
|
51
|
+
unlabeled: number;
|
|
52
|
+
scored: number;
|
|
53
|
+
models: string[];
|
|
54
|
+
accuracy: number;
|
|
55
|
+
correct: number;
|
|
56
|
+
inputTokens: number;
|
|
57
|
+
outputTokens: number;
|
|
58
|
+
cost: number;
|
|
59
|
+
byTag: {
|
|
60
|
+
[k: string]: {
|
|
61
|
+
n: number;
|
|
62
|
+
accuracy: number;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
byQuestion: {
|
|
66
|
+
[k: string]: {
|
|
67
|
+
n: number;
|
|
68
|
+
accuracy: number;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export interface ScoreOptions {
|
|
74
|
+
questionIds?: Iterable<string>;
|
|
75
|
+
pricePerMtok?: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Score every labeled answer in a suite.
|
|
79
|
+
*
|
|
80
|
+
* Records with no `label` are counted and skipped rather than silently dropped,
|
|
81
|
+
* so a suite that quietly lost its labels shows up in the summary instead of
|
|
82
|
+
* producing a suspiciously perfect score over three records.
|
|
83
|
+
*/
|
|
84
|
+
export declare function scoreRecords(records: Iterable<JevlRecord>, options?: ScoreOptions): SuiteResult;
|
|
85
|
+
/** Two runs of the same suite, side by side. */
|
|
86
|
+
export declare class SuiteComparison {
|
|
87
|
+
readonly baseline: SuiteResult;
|
|
88
|
+
readonly candidate: SuiteResult;
|
|
89
|
+
readonly regressions: Array<[string, string]>;
|
|
90
|
+
readonly fixes: Array<[string, string]>;
|
|
91
|
+
constructor(baseline: SuiteResult, candidate: SuiteResult, regressions?: Array<[string, string]>, fixes?: Array<[string, string]>);
|
|
92
|
+
get accuracyDelta(): number;
|
|
93
|
+
get costDelta(): number;
|
|
94
|
+
summary(): string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Compare two scored runs by (request, question).
|
|
98
|
+
*
|
|
99
|
+
* Aggregate accuracy can hold steady while the *set* of things you get right
|
|
100
|
+
* churns underneath, which matters when a specific case is the one you promised
|
|
101
|
+
* someone would work. Regressions and fixes are tracked individually for that
|
|
102
|
+
* reason.
|
|
103
|
+
*/
|
|
104
|
+
export declare function compareSuites(baseline: SuiteResult, candidate: SuiteResult): SuiteComparison;
|
|
105
|
+
//# sourceMappingURL=score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../src/score.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,MAAM,IAAI,UAAU,EAAgB,MAAM,aAAa,CAAC;AAEtE,eAAO,MAAM,cAAc,QAAQ,CAAC;AAEpC,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB;AAED,iDAAiD;AACjD,qBAAa,WAAW;IAQV,QAAQ,CAAC,YAAY;IAPjC,OAAO,EAAE,cAAc,EAAE,CAAM;IAC/B,WAAW,SAAK;IAChB,YAAY,SAAK;IACjB,OAAO,SAAK;IACZ,SAAS,SAAK;IACd,MAAM,cAAqB;gBAEN,YAAY,SAAiB;IAElD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,yDAAyD;IACzD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,0EAA0E;IAC1E,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAgBzC,+EAA+E;IAC/E,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAc9C,sEAAsE;IACtE,QAAQ,IAAI,cAAc,EAAE;IAI5B,OAAO,IAAI,MAAM;IA6BjB,MAAM;;;;;;;;;;;;;;;;;;;;;;;CAiBP;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC7B,OAAO,GAAE,YAAiB,GACzB,WAAW,CAmCb;AAED,gDAAgD;AAChD,qBAAa,eAAe;IAExB,QAAQ,CAAC,QAAQ,EAAE,WAAW;IAC9B,QAAQ,CAAC,SAAS,EAAE,WAAW;IAC/B,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAH9B,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,WAAW,EACtB,WAAW,GAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EACzC,KAAK,GAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAG9C,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,OAAO,IAAI,MAAM;CAWlB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,GAAG,eAAe,CAc5F"}
|
package/dist/score.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoring a labeled `.jevl` suite, and comparing two runs of it.
|
|
3
|
+
*
|
|
4
|
+
* The question this answers is the one you actually have to defend: for this
|
|
5
|
+
* task, on my data, is Jev good enough, and what does it cost? That needs
|
|
6
|
+
* accuracy and cost together, because any one of them alone is easy to win.
|
|
7
|
+
*
|
|
8
|
+
* Costs come from a price per million input tokens, defaulting to Jev's
|
|
9
|
+
* published $0.042. Output tokens are free on Jev and are reported but never
|
|
10
|
+
* billed.
|
|
11
|
+
*/
|
|
12
|
+
import { parseAnswers } from "jevkit-core";
|
|
13
|
+
export const PRICE_PER_MTOK = 0.042;
|
|
14
|
+
/** Everything scored from one run of a suite. */
|
|
15
|
+
export class SuiteResult {
|
|
16
|
+
pricePerMtok;
|
|
17
|
+
results = [];
|
|
18
|
+
inputTokens = 0;
|
|
19
|
+
outputTokens = 0;
|
|
20
|
+
records = 0;
|
|
21
|
+
unlabeled = 0;
|
|
22
|
+
models = new Set();
|
|
23
|
+
constructor(pricePerMtok = PRICE_PER_MTOK) {
|
|
24
|
+
this.pricePerMtok = pricePerMtok;
|
|
25
|
+
}
|
|
26
|
+
get count() {
|
|
27
|
+
return this.results.length;
|
|
28
|
+
}
|
|
29
|
+
get correct() {
|
|
30
|
+
return this.results.filter((r) => r.correct).length;
|
|
31
|
+
}
|
|
32
|
+
get accuracy() {
|
|
33
|
+
return this.count ? this.correct / this.count : 0;
|
|
34
|
+
}
|
|
35
|
+
/** Input-token cost in dollars. Jev bills input only. */
|
|
36
|
+
get cost() {
|
|
37
|
+
return (this.inputTokens / 1_000_000) * this.pricePerMtok;
|
|
38
|
+
}
|
|
39
|
+
get costPerQuestion() {
|
|
40
|
+
return this.count ? this.cost / this.count : 0;
|
|
41
|
+
}
|
|
42
|
+
/** tag -> [n, accuracy]. A result with several tags counts under each. */
|
|
43
|
+
byTag() {
|
|
44
|
+
const buckets = new Map();
|
|
45
|
+
for (const r of this.results) {
|
|
46
|
+
for (const tag of r.tags) {
|
|
47
|
+
if (!buckets.has(tag))
|
|
48
|
+
buckets.set(tag, []);
|
|
49
|
+
buckets.get(tag).push(r);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const out = {};
|
|
53
|
+
for (const tag of [...buckets.keys()].sort()) {
|
|
54
|
+
const rs = buckets.get(tag);
|
|
55
|
+
out[tag] = [rs.length, rs.filter((r) => r.correct).length / rs.length];
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/** question id -> [n, accuracy]. Finds the one question dragging the suite. */
|
|
60
|
+
byQuestion() {
|
|
61
|
+
const buckets = new Map();
|
|
62
|
+
for (const r of this.results) {
|
|
63
|
+
if (!buckets.has(r.questionId))
|
|
64
|
+
buckets.set(r.questionId, []);
|
|
65
|
+
buckets.get(r.questionId).push(r);
|
|
66
|
+
}
|
|
67
|
+
const out = {};
|
|
68
|
+
for (const qid of [...buckets.keys()].sort()) {
|
|
69
|
+
const rs = buckets.get(qid);
|
|
70
|
+
out[qid] = [rs.length, rs.filter((r) => r.correct).length / rs.length];
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
/** Wrong answers, most confident first: the most interesting bugs. */
|
|
75
|
+
failures() {
|
|
76
|
+
return this.results.filter((r) => !r.correct).sort((a, b) => b.probability - a.probability);
|
|
77
|
+
}
|
|
78
|
+
summary() {
|
|
79
|
+
const lines = [
|
|
80
|
+
`records: ${this.records}` +
|
|
81
|
+
(this.unlabeled ? ` (${this.unlabeled} unlabeled, skipped)` : ""),
|
|
82
|
+
`scored: ${this.count} question(s)`,
|
|
83
|
+
`model(s): ${[...this.models].sort().join(", ") || "?"}`,
|
|
84
|
+
`accuracy: ${this.accuracy.toFixed(4)} (${this.correct}/${this.count})`,
|
|
85
|
+
`tokens: ${this.inputTokens.toLocaleString("en-US")} in, ` +
|
|
86
|
+
`${this.outputTokens.toLocaleString("en-US")} out (out is free)`,
|
|
87
|
+
`cost: $${this.cost.toFixed(6)} ($${this.costPerQuestion.toFixed(8)}/question ` +
|
|
88
|
+
`at $${this.pricePerMtok}/Mtok)`,
|
|
89
|
+
];
|
|
90
|
+
const tags = this.byTag();
|
|
91
|
+
if (Object.keys(tags).length) {
|
|
92
|
+
lines.push("by tag:");
|
|
93
|
+
for (const [tag, [n, acc]] of Object.entries(tags)) {
|
|
94
|
+
lines.push(` ${tag.padEnd(24)} ${acc.toFixed(4)} (n=${n})`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const questions = this.byQuestion();
|
|
98
|
+
if (Object.keys(questions).length > 1) {
|
|
99
|
+
lines.push("by question:");
|
|
100
|
+
for (const [qid, [n, acc]] of Object.entries(questions)) {
|
|
101
|
+
lines.push(` ${qid.padEnd(24)} ${acc.toFixed(4)} (n=${n})`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return lines.join("\n");
|
|
105
|
+
}
|
|
106
|
+
toJSON() {
|
|
107
|
+
const mapped = (src) => Object.fromEntries(Object.entries(src).map(([k, [n, a]]) => [k, { n, accuracy: a }]));
|
|
108
|
+
return {
|
|
109
|
+
records: this.records,
|
|
110
|
+
unlabeled: this.unlabeled,
|
|
111
|
+
scored: this.count,
|
|
112
|
+
models: [...this.models].sort(),
|
|
113
|
+
accuracy: this.accuracy,
|
|
114
|
+
correct: this.correct,
|
|
115
|
+
inputTokens: this.inputTokens,
|
|
116
|
+
outputTokens: this.outputTokens,
|
|
117
|
+
cost: this.cost,
|
|
118
|
+
byTag: mapped(this.byTag()),
|
|
119
|
+
byQuestion: mapped(this.byQuestion()),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Score every labeled answer in a suite.
|
|
125
|
+
*
|
|
126
|
+
* Records with no `label` are counted and skipped rather than silently dropped,
|
|
127
|
+
* so a suite that quietly lost its labels shows up in the summary instead of
|
|
128
|
+
* producing a suspiciously perfect score over three records.
|
|
129
|
+
*/
|
|
130
|
+
export function scoreRecords(records, options = {}) {
|
|
131
|
+
const wanted = options.questionIds ? new Set(options.questionIds) : null;
|
|
132
|
+
const suite = new SuiteResult(options.pricePerMtok ?? PRICE_PER_MTOK);
|
|
133
|
+
for (const record of records) {
|
|
134
|
+
suite.records += 1;
|
|
135
|
+
suite.models.add(record.model);
|
|
136
|
+
const usage = (record.usage ?? {});
|
|
137
|
+
suite.inputTokens += Number(usage["input_tokens"] ?? 0);
|
|
138
|
+
suite.outputTokens += Number(usage["output_tokens"] ?? 0);
|
|
139
|
+
if (!record.label || !Object.keys(record.label).length) {
|
|
140
|
+
suite.unlabeled += 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const answers = parseAnswers(record.answers);
|
|
144
|
+
for (const [qid, label] of Object.entries(record.label)) {
|
|
145
|
+
if (wanted && !wanted.has(qid))
|
|
146
|
+
continue;
|
|
147
|
+
const answer = answers[qid];
|
|
148
|
+
if (!answer)
|
|
149
|
+
continue;
|
|
150
|
+
suite.results.push({
|
|
151
|
+
requestId: record.requestId,
|
|
152
|
+
questionId: qid,
|
|
153
|
+
type: answer.type,
|
|
154
|
+
predicted: answer.predicted(),
|
|
155
|
+
label,
|
|
156
|
+
correct: answer.isCorrect(label),
|
|
157
|
+
probability: answer.topProbability,
|
|
158
|
+
confidence: answer.confidence,
|
|
159
|
+
tags: [...record.tags],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return suite;
|
|
164
|
+
}
|
|
165
|
+
/** Two runs of the same suite, side by side. */
|
|
166
|
+
export class SuiteComparison {
|
|
167
|
+
baseline;
|
|
168
|
+
candidate;
|
|
169
|
+
regressions;
|
|
170
|
+
fixes;
|
|
171
|
+
constructor(baseline, candidate, regressions = [], fixes = []) {
|
|
172
|
+
this.baseline = baseline;
|
|
173
|
+
this.candidate = candidate;
|
|
174
|
+
this.regressions = regressions;
|
|
175
|
+
this.fixes = fixes;
|
|
176
|
+
}
|
|
177
|
+
get accuracyDelta() {
|
|
178
|
+
return this.candidate.accuracy - this.baseline.accuracy;
|
|
179
|
+
}
|
|
180
|
+
get costDelta() {
|
|
181
|
+
return this.candidate.cost - this.baseline.cost;
|
|
182
|
+
}
|
|
183
|
+
summary() {
|
|
184
|
+
const sign = (n, digits) => `${n >= 0 ? "+" : ""}${n.toFixed(digits)}`;
|
|
185
|
+
return [
|
|
186
|
+
`accuracy: ${this.baseline.accuracy.toFixed(4)} -> ` +
|
|
187
|
+
`${this.candidate.accuracy.toFixed(4)} (${sign(this.accuracyDelta, 4)})`,
|
|
188
|
+
`cost: $${this.baseline.cost.toFixed(6)} -> ` +
|
|
189
|
+
`$${this.candidate.cost.toFixed(6)} (${sign(this.costDelta, 6)})`,
|
|
190
|
+
`regressed: ${this.regressions.length} (right before, wrong now)`,
|
|
191
|
+
`fixed: ${this.fixes.length} (wrong before, right now)`,
|
|
192
|
+
].join("\n");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Compare two scored runs by (request, question).
|
|
197
|
+
*
|
|
198
|
+
* Aggregate accuracy can hold steady while the *set* of things you get right
|
|
199
|
+
* churns underneath, which matters when a specific case is the one you promised
|
|
200
|
+
* someone would work. Regressions and fixes are tracked individually for that
|
|
201
|
+
* reason.
|
|
202
|
+
*/
|
|
203
|
+
export function compareSuites(baseline, candidate) {
|
|
204
|
+
const index = (suite) => new Map(suite.results.map((r) => [`${r.requestId}\u0000${r.questionId}`, r]));
|
|
205
|
+
const before = index(baseline);
|
|
206
|
+
const after = index(candidate);
|
|
207
|
+
const shared = [...before.keys()].filter((k) => after.has(k));
|
|
208
|
+
const split = (k) => k.split("\u0000");
|
|
209
|
+
return new SuiteComparison(baseline, candidate, shared.filter((k) => before.get(k).correct && !after.get(k).correct).sort().map(split), shared.filter((k) => !before.get(k).correct && after.get(k).correct).sort().map(split));
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../src/score.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jevkit-bench",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Score a labeled TypeSafe Jev suite for accuracy and cost, and compare two runs. Catches regressions aggregate accuracy hides.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jev",
|
|
7
|
+
"typesafe",
|
|
8
|
+
"system-one",
|
|
9
|
+
"benchmark",
|
|
10
|
+
"evaluation",
|
|
11
|
+
"accuracy",
|
|
12
|
+
"cost"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"jevkit-bench": "./dist/cli.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/pjdurden/jevkit-js.git"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"jevkit-core": "^0.2.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc -b"
|
|
43
|
+
}
|
|
44
|
+
}
|