oh-my-knowledge 0.18.0 → 0.20.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 +596 -326
- package/README.zh.md +917 -0
- package/dist/src/analysis/failure-clusterer.d.ts +96 -0
- package/dist/src/analysis/failure-clusterer.d.ts.map +1 -0
- package/dist/src/analysis/failure-clusterer.js +298 -0
- package/dist/src/analysis/failure-clusterer.js.map +1 -0
- package/dist/src/analysis/sample-diagnostics.d.ts +78 -0
- package/dist/src/analysis/sample-diagnostics.d.ts.map +1 -0
- package/dist/src/analysis/sample-diagnostics.js +259 -0
- package/dist/src/analysis/sample-diagnostics.js.map +1 -0
- package/dist/src/analysis/saturation.d.ts +85 -0
- package/dist/src/analysis/saturation.d.ts.map +1 -0
- package/dist/src/analysis/saturation.js +174 -0
- package/dist/src/analysis/saturation.js.map +1 -0
- package/dist/src/cli.js +772 -18
- package/dist/src/cli.js.map +1 -1
- package/dist/src/eval-core/bootstrap.d.ts +72 -0
- package/dist/src/eval-core/bootstrap.d.ts.map +1 -0
- package/dist/src/eval-core/bootstrap.js +174 -0
- package/dist/src/eval-core/bootstrap.js.map +1 -0
- package/dist/src/eval-core/evaluation-execution.d.ts +15 -1
- package/dist/src/eval-core/evaluation-execution.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-execution.js +37 -3
- package/dist/src/eval-core/evaluation-execution.js.map +1 -1
- package/dist/src/eval-core/evaluation-job.d.ts +10 -2
- package/dist/src/eval-core/evaluation-job.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-job.js +9 -1
- package/dist/src/eval-core/evaluation-job.js.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.js +90 -0
- package/dist/src/eval-core/evaluation-reporting.js.map +1 -1
- package/dist/src/eval-core/schema.d.ts.map +1 -1
- package/dist/src/eval-core/schema.js +69 -0
- package/dist/src/eval-core/schema.js.map +1 -1
- package/dist/src/eval-core/verdict.d.ts +74 -0
- package/dist/src/eval-core/verdict.d.ts.map +1 -0
- package/dist/src/eval-core/verdict.js +283 -0
- package/dist/src/eval-core/verdict.js.map +1 -0
- package/dist/src/eval-workflows/each-evaluation-workflow.d.ts +16 -3
- package/dist/src/eval-workflows/each-evaluation-workflow.d.ts.map +1 -1
- package/dist/src/eval-workflows/each-evaluation-workflow.js +10 -2
- package/dist/src/eval-workflows/each-evaluation-workflow.js.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts +17 -1
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.js +45 -3
- package/dist/src/eval-workflows/evaluation-pipeline.js.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.d.ts +23 -2
- package/dist/src/eval-workflows/run-evaluation.d.ts.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.js +104 -4
- package/dist/src/eval-workflows/run-evaluation.js.map +1 -1
- package/dist/src/grading/assertions.d.ts +16 -0
- package/dist/src/grading/assertions.d.ts.map +1 -1
- package/dist/src/grading/assertions.js +385 -111
- package/dist/src/grading/assertions.js.map +1 -1
- package/dist/src/grading/debias-validate.d.ts +84 -0
- package/dist/src/grading/debias-validate.d.ts.map +1 -0
- package/dist/src/grading/debias-validate.js +173 -0
- package/dist/src/grading/debias-validate.js.map +1 -0
- package/dist/src/grading/gold-cli.d.ts +88 -0
- package/dist/src/grading/gold-cli.d.ts.map +1 -0
- package/dist/src/grading/gold-cli.js +251 -0
- package/dist/src/grading/gold-cli.js.map +1 -0
- package/dist/src/grading/gold-dataset.d.ts +73 -0
- package/dist/src/grading/gold-dataset.d.ts.map +1 -0
- package/dist/src/grading/gold-dataset.js +161 -0
- package/dist/src/grading/gold-dataset.js.map +1 -0
- package/dist/src/grading/human-gold.d.ts +102 -0
- package/dist/src/grading/human-gold.d.ts.map +1 -0
- package/dist/src/grading/human-gold.js +188 -0
- package/dist/src/grading/human-gold.js.map +1 -0
- package/dist/src/grading/index.d.ts +27 -2
- package/dist/src/grading/index.d.ts.map +1 -1
- package/dist/src/grading/index.js +36 -18
- package/dist/src/grading/index.js.map +1 -1
- package/dist/src/grading/judge.d.ts +65 -2
- package/dist/src/grading/judge.d.ts.map +1 -1
- package/dist/src/grading/judge.js +280 -23
- package/dist/src/grading/judge.js.map +1 -1
- package/dist/src/inputs/eval-config.js +19 -0
- package/dist/src/inputs/eval-config.js.map +1 -1
- package/dist/src/observability/{production-analyzer.d.ts → skill-health-analyzer.d.ts} +24 -2
- package/dist/src/observability/skill-health-analyzer.d.ts.map +1 -0
- package/dist/src/observability/{production-analyzer.js → skill-health-analyzer.js} +61 -6
- package/dist/src/observability/skill-health-analyzer.js.map +1 -0
- package/dist/src/observability/trace-adapter.d.ts.map +1 -1
- package/dist/src/observability/trace-adapter.js +27 -1
- package/dist/src/observability/trace-adapter.js.map +1 -1
- package/dist/src/renderer/html-renderer.d.ts.map +1 -1
- package/dist/src/renderer/html-renderer.js +40 -6
- package/dist/src/renderer/html-renderer.js.map +1 -1
- package/dist/src/renderer/layout.d.ts.map +1 -1
- package/dist/src/renderer/layout.js +138 -4
- package/dist/src/renderer/layout.js.map +1 -1
- package/dist/src/renderer/skill-health-renderer.d.ts +2 -2
- package/dist/src/renderer/skill-health-renderer.d.ts.map +1 -1
- package/dist/src/renderer/skill-health-renderer.js +39 -4
- package/dist/src/renderer/skill-health-renderer.js.map +1 -1
- package/dist/src/renderer/summary.d.ts +28 -1
- package/dist/src/renderer/summary.d.ts.map +1 -1
- package/dist/src/renderer/summary.js +322 -8
- package/dist/src/renderer/summary.js.map +1 -1
- package/dist/src/renderer/table.d.ts.map +1 -1
- package/dist/src/renderer/table.js +63 -2
- package/dist/src/renderer/table.js.map +1 -1
- package/dist/src/server/report-server.d.ts +2 -1
- package/dist/src/server/report-server.d.ts.map +1 -1
- package/dist/src/server/report-server.js +397 -2
- package/dist/src/server/report-server.js.map +1 -1
- package/dist/src/types.d.ts +247 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +24 -6
- package/dist/src/observability/production-analyzer.d.ts.map +0 -1
- package/dist/src/observability/production-analyzer.js.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gold dataset loader and validator.
|
|
3
|
+
*
|
|
4
|
+
* The "gold" file is a YAML document containing human (or stronger-model)
|
|
5
|
+
* annotations for a fixed set of sample_ids. omk loads it and compares the
|
|
6
|
+
* annotations against the judge's scores from a finished evaluation report
|
|
7
|
+
* to compute α / κ / Pearson agreement (see ./human-gold.ts).
|
|
8
|
+
*
|
|
9
|
+
* Schema is deliberately small:
|
|
10
|
+
*
|
|
11
|
+
* metadata:
|
|
12
|
+
* annotator: <model id or person identifier> # required, used for contamination check
|
|
13
|
+
* annotatedAt: <YYYY-MM-DD> # required
|
|
14
|
+
* version: <string> # required
|
|
15
|
+
* scale: { min: 1, max: 5 } # optional, default {1,5}
|
|
16
|
+
* notes: <free text> # optional
|
|
17
|
+
*
|
|
18
|
+
* annotations:
|
|
19
|
+
* - sample_id: <must match a sample_id in the eval samples file>
|
|
20
|
+
* score: <number on the scale> # required
|
|
21
|
+
* reason: <free text> # optional, kept for audit only
|
|
22
|
+
*
|
|
23
|
+
* v0.21 supports single-score gold only. Multi-dimensional gold (per-rubric
|
|
24
|
+
* scores) is a candidate for v0.22 once we have real users asking for it —
|
|
25
|
+
* the schema is forwards-compatible (we can add a `dimensions` field next to
|
|
26
|
+
* `score` later without breaking existing files).
|
|
27
|
+
*/
|
|
28
|
+
export interface GoldMetadata {
|
|
29
|
+
/** Identifier of who/what produced the annotations — model id, person handle, or team name. */
|
|
30
|
+
annotator: string;
|
|
31
|
+
annotatedAt: string;
|
|
32
|
+
version: string;
|
|
33
|
+
scale?: {
|
|
34
|
+
min: number;
|
|
35
|
+
max: number;
|
|
36
|
+
};
|
|
37
|
+
notes?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface GoldAnnotation {
|
|
40
|
+
sample_id: string;
|
|
41
|
+
score: number;
|
|
42
|
+
reason?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface GoldDataset {
|
|
45
|
+
metadata: GoldMetadata;
|
|
46
|
+
annotations: GoldAnnotation[];
|
|
47
|
+
/** Source file paths — surfaced for diagnostics (e.g. error messages). */
|
|
48
|
+
sourcePaths: string[];
|
|
49
|
+
}
|
|
50
|
+
export interface ValidationIssue {
|
|
51
|
+
/** File the issue came from — empty when issue is structural (e.g. missing metadata). */
|
|
52
|
+
path: string;
|
|
53
|
+
/** Annotation index within the file, when applicable. */
|
|
54
|
+
index?: number;
|
|
55
|
+
message: string;
|
|
56
|
+
}
|
|
57
|
+
export interface LoadResult {
|
|
58
|
+
dataset?: GoldDataset;
|
|
59
|
+
issues: ValidationIssue[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Load a gold dataset from a directory.
|
|
63
|
+
*
|
|
64
|
+
* Convention: the directory contains exactly one `metadata.yaml` and one or
|
|
65
|
+
* more `*.yaml` annotation files. Annotation files may declare top-level
|
|
66
|
+
* `annotations: [...]` — multiple files are concatenated. This shape lets
|
|
67
|
+
* users split annotations by topic (code.yaml, writing.yaml, ...) without
|
|
68
|
+
* fighting one giant file in code review.
|
|
69
|
+
*/
|
|
70
|
+
export declare function loadGoldDataset(dir: string): LoadResult;
|
|
71
|
+
/** Re-export YAML serializer so the `gold init` CLI can write a template. */
|
|
72
|
+
export declare function dumpYaml(value: unknown): string;
|
|
73
|
+
//# sourceMappingURL=gold-dataset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gold-dataset.d.ts","sourceRoot":"","sources":["../../../src/grading/gold-dataset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAOH,MAAM,WAAW,YAAY;IAC3B,+FAA+F;IAC/F,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,0EAA0E;IAC1E,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAyFvD;AAmCD,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE/C"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gold dataset loader and validator.
|
|
3
|
+
*
|
|
4
|
+
* The "gold" file is a YAML document containing human (or stronger-model)
|
|
5
|
+
* annotations for a fixed set of sample_ids. omk loads it and compares the
|
|
6
|
+
* annotations against the judge's scores from a finished evaluation report
|
|
7
|
+
* to compute α / κ / Pearson agreement (see ./human-gold.ts).
|
|
8
|
+
*
|
|
9
|
+
* Schema is deliberately small:
|
|
10
|
+
*
|
|
11
|
+
* metadata:
|
|
12
|
+
* annotator: <model id or person identifier> # required, used for contamination check
|
|
13
|
+
* annotatedAt: <YYYY-MM-DD> # required
|
|
14
|
+
* version: <string> # required
|
|
15
|
+
* scale: { min: 1, max: 5 } # optional, default {1,5}
|
|
16
|
+
* notes: <free text> # optional
|
|
17
|
+
*
|
|
18
|
+
* annotations:
|
|
19
|
+
* - sample_id: <must match a sample_id in the eval samples file>
|
|
20
|
+
* score: <number on the scale> # required
|
|
21
|
+
* reason: <free text> # optional, kept for audit only
|
|
22
|
+
*
|
|
23
|
+
* v0.21 supports single-score gold only. Multi-dimensional gold (per-rubric
|
|
24
|
+
* scores) is a candidate for v0.22 once we have real users asking for it —
|
|
25
|
+
* the schema is forwards-compatible (we can add a `dimensions` field next to
|
|
26
|
+
* `score` later without breaking existing files).
|
|
27
|
+
*/
|
|
28
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
29
|
+
import { resolve, join } from 'node:path';
|
|
30
|
+
import yaml from 'js-yaml';
|
|
31
|
+
import { parseYaml } from '../inputs/load-samples.js';
|
|
32
|
+
/**
|
|
33
|
+
* Load a gold dataset from a directory.
|
|
34
|
+
*
|
|
35
|
+
* Convention: the directory contains exactly one `metadata.yaml` and one or
|
|
36
|
+
* more `*.yaml` annotation files. Annotation files may declare top-level
|
|
37
|
+
* `annotations: [...]` — multiple files are concatenated. This shape lets
|
|
38
|
+
* users split annotations by topic (code.yaml, writing.yaml, ...) without
|
|
39
|
+
* fighting one giant file in code review.
|
|
40
|
+
*/
|
|
41
|
+
export function loadGoldDataset(dir) {
|
|
42
|
+
const issues = [];
|
|
43
|
+
const absDir = resolve(dir);
|
|
44
|
+
let entries;
|
|
45
|
+
try {
|
|
46
|
+
entries = readdirSync(absDir);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
issues.push({
|
|
50
|
+
path: absDir,
|
|
51
|
+
message: `gold dataset directory not found or unreadable: ${err.message}`,
|
|
52
|
+
});
|
|
53
|
+
return { issues };
|
|
54
|
+
}
|
|
55
|
+
const yamlFiles = entries.filter((f) => f.endsWith('.yaml') || f.endsWith('.yml')).sort();
|
|
56
|
+
if (yamlFiles.length === 0) {
|
|
57
|
+
issues.push({ path: absDir, message: 'no .yaml files found in gold dataset directory' });
|
|
58
|
+
return { issues };
|
|
59
|
+
}
|
|
60
|
+
let metadata;
|
|
61
|
+
const annotations = [];
|
|
62
|
+
const sourcePaths = [];
|
|
63
|
+
const seenIds = new Set();
|
|
64
|
+
for (const fname of yamlFiles) {
|
|
65
|
+
const fpath = join(absDir, fname);
|
|
66
|
+
sourcePaths.push(fpath);
|
|
67
|
+
let parsed;
|
|
68
|
+
try {
|
|
69
|
+
parsed = parseYaml(readFileSync(fpath, 'utf-8'));
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
issues.push({ path: fpath, message: err.message });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
76
|
+
issues.push({ path: fpath, message: 'top-level YAML must be an object' });
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const obj = parsed;
|
|
80
|
+
if (obj.metadata) {
|
|
81
|
+
const metaIssue = validateMetadata(obj.metadata, fpath);
|
|
82
|
+
if (metaIssue)
|
|
83
|
+
issues.push(metaIssue);
|
|
84
|
+
else if (metadata) {
|
|
85
|
+
issues.push({ path: fpath, message: 'metadata declared in multiple files; keep it in one place' });
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
metadata = obj.metadata;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(obj.annotations)) {
|
|
92
|
+
for (const [i, raw] of obj.annotations.entries()) {
|
|
93
|
+
const annoIssue = validateAnnotation(raw, fpath, i);
|
|
94
|
+
if (annoIssue) {
|
|
95
|
+
issues.push(annoIssue);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const anno = raw;
|
|
99
|
+
if (seenIds.has(anno.sample_id)) {
|
|
100
|
+
issues.push({
|
|
101
|
+
path: fpath,
|
|
102
|
+
index: i,
|
|
103
|
+
message: `duplicate sample_id "${anno.sample_id}" — each gold annotation must be unique`,
|
|
104
|
+
});
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
seenIds.add(anno.sample_id);
|
|
108
|
+
annotations.push(anno);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!metadata) {
|
|
113
|
+
issues.push({ path: absDir, message: 'no metadata block found in any file (need annotator/annotatedAt/version)' });
|
|
114
|
+
}
|
|
115
|
+
if (annotations.length === 0) {
|
|
116
|
+
issues.push({ path: absDir, message: 'no annotations found' });
|
|
117
|
+
}
|
|
118
|
+
if (!metadata || annotations.length === 0) {
|
|
119
|
+
return { issues };
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
dataset: { metadata, annotations, sourcePaths },
|
|
123
|
+
issues,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function validateMetadata(raw, path) {
|
|
127
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
128
|
+
return { path, message: 'metadata must be an object' };
|
|
129
|
+
}
|
|
130
|
+
const m = raw;
|
|
131
|
+
for (const k of ['annotator', 'annotatedAt', 'version']) {
|
|
132
|
+
if (typeof m[k] !== 'string' || !m[k]) {
|
|
133
|
+
return { path, message: `metadata.${k} is required and must be a non-empty string` };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (m.scale !== undefined) {
|
|
137
|
+
const s = m.scale;
|
|
138
|
+
if (typeof s !== 'object' || typeof s.min !== 'number' || typeof s.max !== 'number' || s.max <= s.min) {
|
|
139
|
+
return { path, message: 'metadata.scale must be { min: number, max: number } with max > min' };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
function validateAnnotation(raw, path, index) {
|
|
145
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
146
|
+
return { path, index, message: 'annotation must be an object' };
|
|
147
|
+
}
|
|
148
|
+
const a = raw;
|
|
149
|
+
if (typeof a.sample_id !== 'string' || !a.sample_id) {
|
|
150
|
+
return { path, index, message: 'sample_id is required and must be a non-empty string' };
|
|
151
|
+
}
|
|
152
|
+
if (typeof a.score !== 'number' || !Number.isFinite(a.score)) {
|
|
153
|
+
return { path, index, message: 'score is required and must be a finite number' };
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
/** Re-export YAML serializer so the `gold init` CLI can write a template. */
|
|
158
|
+
export function dumpYaml(value) {
|
|
159
|
+
return yaml.dump(value, { lineWidth: 100, noRefs: true });
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=gold-dataset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gold-dataset.js","sourceRoot":"","sources":["../../../src/grading/gold-dataset.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human gold dataset agreement metrics.
|
|
3
|
+
*
|
|
4
|
+
* Why this module exists
|
|
5
|
+
* ----------------------
|
|
6
|
+
* Bootstrap CI (Phase 1) gives us *precision* — how stable the judge is across
|
|
7
|
+
* resampled evaluations. It does not give us *validity* — whether the judge is
|
|
8
|
+
* scoring the right thing at all. A judge can be extremely consistent (CI very
|
|
9
|
+
* narrow) and yet systematically biased; that's an undetected failure mode.
|
|
10
|
+
*
|
|
11
|
+
* Human gold (or stronger-model gold as a proxy) provides an external anchor.
|
|
12
|
+
* We compute agreement between the LLM judge and the gold annotator, and report
|
|
13
|
+
* the result alongside the Bootstrap CI. Both numbers must be acceptable for a
|
|
14
|
+
* conclusion to be trustworthy.
|
|
15
|
+
*
|
|
16
|
+
* Three metrics are exported:
|
|
17
|
+
*
|
|
18
|
+
* - **Krippendorff's α (interval)** — primary. Distribution-free, supports
|
|
19
|
+
* ordinal/interval scales naturally, doesn't assume coders are exchangeable
|
|
20
|
+
* (good fit when one "coder" is a model and the other a human annotator).
|
|
21
|
+
* - **Quadratic-weighted Cohen's κ** — secondary. Familiar to many readers,
|
|
22
|
+
* useful as a sanity check. Reports lower than α when marginals diverge.
|
|
23
|
+
* - **Pearson r** — tertiary. Captures rank-order agreement only; doesn't
|
|
24
|
+
* penalize systematic offset (a judge that always scores 1 lower than gold
|
|
25
|
+
* has Pearson 1 but α < 1). Listed for completeness.
|
|
26
|
+
*
|
|
27
|
+
* All three are wrapped with a bootstrap CI on α (the primary) so the user can
|
|
28
|
+
* see uncertainty on the agreement number itself when N is small.
|
|
29
|
+
*/
|
|
30
|
+
import { type BootstrapCI } from '../eval-core/bootstrap.js';
|
|
31
|
+
export interface RatingPair {
|
|
32
|
+
/** Per-sample identifier; used only for diagnostics. */
|
|
33
|
+
unitId: string;
|
|
34
|
+
/** Score from coder A — convention: gold annotator goes here. */
|
|
35
|
+
coderA: number;
|
|
36
|
+
/** Score from coder B — convention: LLM judge goes here. */
|
|
37
|
+
coderB: number;
|
|
38
|
+
}
|
|
39
|
+
export interface AgreementResult {
|
|
40
|
+
/** Krippendorff α (interval weights). 1 = perfect, 0 = chance, < 0 = worse than chance. */
|
|
41
|
+
alpha: number;
|
|
42
|
+
/** 95% bootstrap CI on α — width signals uncertainty due to sample size. */
|
|
43
|
+
alphaCI: BootstrapCI;
|
|
44
|
+
/** Quadratic-weighted κ. */
|
|
45
|
+
weightedKappa: number;
|
|
46
|
+
/** Pearson product-moment correlation. NaN if either coder has zero variance. */
|
|
47
|
+
pearson: number;
|
|
48
|
+
/** How many rating pairs went into the calculation. */
|
|
49
|
+
sampleCount: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Krippendorff's α with interval weights for two coders.
|
|
53
|
+
*
|
|
54
|
+
* Implementation follows Krippendorff (2011), "Computing Krippendorff's
|
|
55
|
+
* Alpha-Reliability". For interval scales the metric is δ²(c, k) = (c − k)².
|
|
56
|
+
*
|
|
57
|
+
* For two coders with one rating per unit:
|
|
58
|
+
* - Coincidence matrix entries o_{c,k} count both (a_u, b_u) and (b_u, a_u),
|
|
59
|
+
* so total mass n_·· = 2N.
|
|
60
|
+
* - D_o = Σ o_{c,k} · δ²(c,k) / n_··
|
|
61
|
+
* - D_e = Σ n_c · n_k · δ²(c,k) / (n_·· (n_·· − 1))
|
|
62
|
+
* - α = 1 − D_o / D_e
|
|
63
|
+
*
|
|
64
|
+
* Returns NaN when D_e = 0 (e.g. all ratings identical across both coders) —
|
|
65
|
+
* agreement is undefined when there is no variance to disagree about.
|
|
66
|
+
*/
|
|
67
|
+
export declare function computeKrippendorffAlpha(pairs: RatingPair[]): number;
|
|
68
|
+
/**
|
|
69
|
+
* Cohen's quadratic-weighted κ for two coders on a numeric scale.
|
|
70
|
+
*
|
|
71
|
+
* w(i,j) = (i − j)² / (K − 1)² (disagreement weight, scaled to [0,1])
|
|
72
|
+
* po_w = mean over units of (1 − w(a_u, b_u))
|
|
73
|
+
* pe_w = Σ_{i,j} marg_a(i) · marg_b(j) · (1 − w(i,j)) / N²
|
|
74
|
+
* κ_w = (po_w − pe_w) / (1 − pe_w)
|
|
75
|
+
*
|
|
76
|
+
* For continuous (non-integer) scores we still need a scale range; the caller
|
|
77
|
+
* passes (min, max). Default 1..5 fits omk's standard rubric.
|
|
78
|
+
*/
|
|
79
|
+
export declare function computeWeightedKappa(pairs: RatingPair[], scale?: {
|
|
80
|
+
min: number;
|
|
81
|
+
max: number;
|
|
82
|
+
}): number;
|
|
83
|
+
/**
|
|
84
|
+
* Pearson product-moment correlation. NaN when either coder has zero variance.
|
|
85
|
+
*/
|
|
86
|
+
export declare function computePearson(pairs: RatingPair[]): number;
|
|
87
|
+
/**
|
|
88
|
+
* Compute α + κ + Pearson, with bootstrap CI on α.
|
|
89
|
+
*
|
|
90
|
+
* Bootstrap resamples pairs (units), not individual ratings — pairs are the
|
|
91
|
+
* unit of replication; resampling individual ratings would break the (a,b) tie.
|
|
92
|
+
*/
|
|
93
|
+
export declare function computeAgreementWithCI(pairs: RatingPair[], options?: {
|
|
94
|
+
samples?: number;
|
|
95
|
+
seed?: number;
|
|
96
|
+
alpha?: number;
|
|
97
|
+
scale?: {
|
|
98
|
+
min: number;
|
|
99
|
+
max: number;
|
|
100
|
+
};
|
|
101
|
+
}): AgreementResult;
|
|
102
|
+
//# sourceMappingURL=human-gold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-gold.d.ts","sourceRoot":"","sources":["../../../src/grading/human-gold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAElF,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,OAAO,EAAE,WAAW,CAAC;IACrB,4BAA4B;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAoCpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,UAAU,EAAE,EACnB,KAAK,GAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAuB,GACvD,MAAM,CAgCR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAuB1D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAO,GACtG,eAAe,CA8BjB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human gold dataset agreement metrics.
|
|
3
|
+
*
|
|
4
|
+
* Why this module exists
|
|
5
|
+
* ----------------------
|
|
6
|
+
* Bootstrap CI (Phase 1) gives us *precision* — how stable the judge is across
|
|
7
|
+
* resampled evaluations. It does not give us *validity* — whether the judge is
|
|
8
|
+
* scoring the right thing at all. A judge can be extremely consistent (CI very
|
|
9
|
+
* narrow) and yet systematically biased; that's an undetected failure mode.
|
|
10
|
+
*
|
|
11
|
+
* Human gold (or stronger-model gold as a proxy) provides an external anchor.
|
|
12
|
+
* We compute agreement between the LLM judge and the gold annotator, and report
|
|
13
|
+
* the result alongside the Bootstrap CI. Both numbers must be acceptable for a
|
|
14
|
+
* conclusion to be trustworthy.
|
|
15
|
+
*
|
|
16
|
+
* Three metrics are exported:
|
|
17
|
+
*
|
|
18
|
+
* - **Krippendorff's α (interval)** — primary. Distribution-free, supports
|
|
19
|
+
* ordinal/interval scales naturally, doesn't assume coders are exchangeable
|
|
20
|
+
* (good fit when one "coder" is a model and the other a human annotator).
|
|
21
|
+
* - **Quadratic-weighted Cohen's κ** — secondary. Familiar to many readers,
|
|
22
|
+
* useful as a sanity check. Reports lower than α when marginals diverge.
|
|
23
|
+
* - **Pearson r** — tertiary. Captures rank-order agreement only; doesn't
|
|
24
|
+
* penalize systematic offset (a judge that always scores 1 lower than gold
|
|
25
|
+
* has Pearson 1 but α < 1). Listed for completeness.
|
|
26
|
+
*
|
|
27
|
+
* All three are wrapped with a bootstrap CI on α (the primary) so the user can
|
|
28
|
+
* see uncertainty on the agreement number itself when N is small.
|
|
29
|
+
*/
|
|
30
|
+
import { bootstrapWithMetric } from '../eval-core/bootstrap.js';
|
|
31
|
+
/**
|
|
32
|
+
* Krippendorff's α with interval weights for two coders.
|
|
33
|
+
*
|
|
34
|
+
* Implementation follows Krippendorff (2011), "Computing Krippendorff's
|
|
35
|
+
* Alpha-Reliability". For interval scales the metric is δ²(c, k) = (c − k)².
|
|
36
|
+
*
|
|
37
|
+
* For two coders with one rating per unit:
|
|
38
|
+
* - Coincidence matrix entries o_{c,k} count both (a_u, b_u) and (b_u, a_u),
|
|
39
|
+
* so total mass n_·· = 2N.
|
|
40
|
+
* - D_o = Σ o_{c,k} · δ²(c,k) / n_··
|
|
41
|
+
* - D_e = Σ n_c · n_k · δ²(c,k) / (n_·· (n_·· − 1))
|
|
42
|
+
* - α = 1 − D_o / D_e
|
|
43
|
+
*
|
|
44
|
+
* Returns NaN when D_e = 0 (e.g. all ratings identical across both coders) —
|
|
45
|
+
* agreement is undefined when there is no variance to disagree about.
|
|
46
|
+
*/
|
|
47
|
+
export function computeKrippendorffAlpha(pairs) {
|
|
48
|
+
if (pairs.length === 0)
|
|
49
|
+
return NaN;
|
|
50
|
+
// Marginal counts: how many times each value appears across both coders.
|
|
51
|
+
const marginal = new Map();
|
|
52
|
+
for (const p of pairs) {
|
|
53
|
+
marginal.set(p.coderA, (marginal.get(p.coderA) ?? 0) + 1);
|
|
54
|
+
marginal.set(p.coderB, (marginal.get(p.coderB) ?? 0) + 1);
|
|
55
|
+
}
|
|
56
|
+
const totalMass = 2 * pairs.length; // n_··
|
|
57
|
+
// Observed disagreement: average squared distance within units, ×2 because
|
|
58
|
+
// each unit contributes (a,b) and (b,a). Equivalent to mean (a-b)² over units.
|
|
59
|
+
let observedSum = 0;
|
|
60
|
+
for (const p of pairs) {
|
|
61
|
+
observedSum += 2 * (p.coderA - p.coderB) ** 2;
|
|
62
|
+
}
|
|
63
|
+
const Do = observedSum / totalMass;
|
|
64
|
+
// Expected disagreement under chance: pair every value with every other value
|
|
65
|
+
// proportional to marginals.
|
|
66
|
+
let expectedSum = 0;
|
|
67
|
+
const values = [...marginal.keys()];
|
|
68
|
+
for (const c of values) {
|
|
69
|
+
const nc = marginal.get(c);
|
|
70
|
+
for (const k of values) {
|
|
71
|
+
const nk = marginal.get(k);
|
|
72
|
+
expectedSum += nc * nk * (c - k) ** 2;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const denom = totalMass * (totalMass - 1);
|
|
76
|
+
if (denom === 0)
|
|
77
|
+
return NaN;
|
|
78
|
+
const De = expectedSum / denom;
|
|
79
|
+
if (De === 0)
|
|
80
|
+
return NaN; // no variance => agreement is undefined
|
|
81
|
+
return 1 - Do / De;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Cohen's quadratic-weighted κ for two coders on a numeric scale.
|
|
85
|
+
*
|
|
86
|
+
* w(i,j) = (i − j)² / (K − 1)² (disagreement weight, scaled to [0,1])
|
|
87
|
+
* po_w = mean over units of (1 − w(a_u, b_u))
|
|
88
|
+
* pe_w = Σ_{i,j} marg_a(i) · marg_b(j) · (1 − w(i,j)) / N²
|
|
89
|
+
* κ_w = (po_w − pe_w) / (1 − pe_w)
|
|
90
|
+
*
|
|
91
|
+
* For continuous (non-integer) scores we still need a scale range; the caller
|
|
92
|
+
* passes (min, max). Default 1..5 fits omk's standard rubric.
|
|
93
|
+
*/
|
|
94
|
+
export function computeWeightedKappa(pairs, scale = { min: 1, max: 5 }) {
|
|
95
|
+
if (pairs.length === 0)
|
|
96
|
+
return NaN;
|
|
97
|
+
const range = scale.max - scale.min;
|
|
98
|
+
if (range <= 0)
|
|
99
|
+
return NaN;
|
|
100
|
+
const denom2 = range * range;
|
|
101
|
+
const margA = new Map();
|
|
102
|
+
const margB = new Map();
|
|
103
|
+
for (const p of pairs) {
|
|
104
|
+
margA.set(p.coderA, (margA.get(p.coderA) ?? 0) + 1);
|
|
105
|
+
margB.set(p.coderB, (margB.get(p.coderB) ?? 0) + 1);
|
|
106
|
+
}
|
|
107
|
+
const N = pairs.length;
|
|
108
|
+
let poWeighted = 0;
|
|
109
|
+
for (const p of pairs) {
|
|
110
|
+
const w = ((p.coderA - p.coderB) ** 2) / denom2;
|
|
111
|
+
poWeighted += 1 - w;
|
|
112
|
+
}
|
|
113
|
+
poWeighted /= N;
|
|
114
|
+
let peWeighted = 0;
|
|
115
|
+
for (const [i, ni] of margA) {
|
|
116
|
+
for (const [j, nj] of margB) {
|
|
117
|
+
const w = ((i - j) ** 2) / denom2;
|
|
118
|
+
peWeighted += (ni * nj) * (1 - w);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
peWeighted /= N * N;
|
|
122
|
+
if (peWeighted === 1)
|
|
123
|
+
return NaN;
|
|
124
|
+
return (poWeighted - peWeighted) / (1 - peWeighted);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Pearson product-moment correlation. NaN when either coder has zero variance.
|
|
128
|
+
*/
|
|
129
|
+
export function computePearson(pairs) {
|
|
130
|
+
if (pairs.length < 2)
|
|
131
|
+
return NaN;
|
|
132
|
+
const n = pairs.length;
|
|
133
|
+
let sumA = 0;
|
|
134
|
+
let sumB = 0;
|
|
135
|
+
for (const p of pairs) {
|
|
136
|
+
sumA += p.coderA;
|
|
137
|
+
sumB += p.coderB;
|
|
138
|
+
}
|
|
139
|
+
const meanA = sumA / n;
|
|
140
|
+
const meanB = sumB / n;
|
|
141
|
+
let cov = 0;
|
|
142
|
+
let varA = 0;
|
|
143
|
+
let varB = 0;
|
|
144
|
+
for (const p of pairs) {
|
|
145
|
+
const da = p.coderA - meanA;
|
|
146
|
+
const db = p.coderB - meanB;
|
|
147
|
+
cov += da * db;
|
|
148
|
+
varA += da * da;
|
|
149
|
+
varB += db * db;
|
|
150
|
+
}
|
|
151
|
+
if (varA === 0 || varB === 0)
|
|
152
|
+
return NaN;
|
|
153
|
+
return cov / Math.sqrt(varA * varB);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Compute α + κ + Pearson, with bootstrap CI on α.
|
|
157
|
+
*
|
|
158
|
+
* Bootstrap resamples pairs (units), not individual ratings — pairs are the
|
|
159
|
+
* unit of replication; resampling individual ratings would break the (a,b) tie.
|
|
160
|
+
*/
|
|
161
|
+
export function computeAgreementWithCI(pairs, options = {}) {
|
|
162
|
+
const { samples = 1000, seed, alpha = 0.05, scale } = options;
|
|
163
|
+
if (pairs.length === 0) {
|
|
164
|
+
return {
|
|
165
|
+
alpha: NaN,
|
|
166
|
+
alphaCI: { low: NaN, high: NaN, estimate: NaN, samples: 0 },
|
|
167
|
+
weightedKappa: NaN,
|
|
168
|
+
pearson: NaN,
|
|
169
|
+
sampleCount: 0,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
// Encode pairs as indices so bootstrapWithMetric can resample them.
|
|
173
|
+
const indices = pairs.map((_, i) => i);
|
|
174
|
+
const alphaCI = bootstrapWithMetric(indices, (resampledIdx) => computeKrippendorffAlpha(resampledIdx.map((i) => pairs[i])), alpha, samples, seed);
|
|
175
|
+
return {
|
|
176
|
+
alpha: roundOrNaN(computeKrippendorffAlpha(pairs)),
|
|
177
|
+
alphaCI,
|
|
178
|
+
weightedKappa: roundOrNaN(computeWeightedKappa(pairs, scale)),
|
|
179
|
+
pearson: roundOrNaN(computePearson(pairs)),
|
|
180
|
+
sampleCount: pairs.length,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function roundOrNaN(x) {
|
|
184
|
+
if (Number.isNaN(x))
|
|
185
|
+
return NaN;
|
|
186
|
+
return Math.round(x * 10000) / 10000;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=human-gold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-gold.js","sourceRoot":"","sources":["../../../src/grading/human-gold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,mBAAmB,EAAoB,MAAM,2BAA2B,CAAC;AAwBlF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAAmB;IAC1D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAEnC,yEAAyE;IACzE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,SAAS,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO;IAE3C,2EAA2E;IAC3E,+EAA+E;IAC/E,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,WAAW,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,EAAE,GAAG,WAAW,GAAG,SAAS,CAAC;IAEnC,8EAA8E;IAC9E,6BAA6B;IAC7B,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;YAC5B,WAAW,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,SAAS,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAC5B,MAAM,EAAE,GAAG,WAAW,GAAG,KAAK,CAAC;IAC/B,IAAI,EAAE,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,CAAC,wCAAwC;IAElE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;AACrB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAmB,EACnB,QAAsC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE;IAExD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACpC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC3B,MAAM,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IAE7B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACpD,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IAEvB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC;QAChD,UAAU,IAAI,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IACD,UAAU,IAAI,CAAC,CAAC;IAEhB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC;YAClC,UAAU,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,UAAU,IAAI,CAAC,GAAG,CAAC,CAAC;IAEpB,IAAI,UAAU,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACjC,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;AACtD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAmB;IAChD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACjC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC;QACjB,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC;IACvB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC;IACvB,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC;QAC5B,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC;QAC5B,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC;QACf,IAAI,IAAI,EAAE,GAAG,EAAE,CAAC;QAChB,IAAI,IAAI,EAAE,GAAG,EAAE,CAAC;IAClB,CAAC;IACD,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACzC,OAAO,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAmB,EACnB,UAAqG,EAAE;IAEvG,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAE9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE;YAC3D,aAAa,EAAE,GAAG;YAClB,OAAO,EAAE,GAAG;YACZ,WAAW,EAAE,CAAC;SACf,CAAC;IACJ,CAAC;IAED,oEAAoE;IACpE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,mBAAmB,CACjC,OAAO,EACP,CAAC,YAAY,EAAE,EAAE,CAAC,wBAAwB,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAC7E,KAAK,EACL,OAAO,EACP,IAAI,CACL,CAAC;IAEF,OAAO;QACL,KAAK,EAAE,UAAU,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAClD,OAAO;QACP,aAAa,EAAE,UAAU,CAAC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC7D,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC1C,WAAW,EAAE,KAAK,CAAC,MAAM;KAC1B,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IAChC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;AACvC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Mixed grading: deterministic assertions + LLM judge + multi-dimensional scoring.
|
|
3
3
|
*/
|
|
4
|
-
import type { GradeResult, ExecutorFn, Sample, ToolCallInfo, TurnInfo } from '../types.js';
|
|
4
|
+
import type { GradeResult, ExecutorFn, JudgeConfig, Sample, ToolCallInfo, TurnInfo } from '../types.js';
|
|
5
5
|
interface GradeOptions {
|
|
6
6
|
output: string;
|
|
7
7
|
sample: Sample;
|
|
@@ -16,11 +16,36 @@ interface GradeOptions {
|
|
|
16
16
|
turns?: TurnInfo[];
|
|
17
17
|
};
|
|
18
18
|
samplesDir?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Run the LLM judge N times per (sample × dimension) pair and report mean + stddev.
|
|
21
|
+
* Default 1 (single judge call). Useful for measuring judge self-consistency:
|
|
22
|
+
* a high stddev means the judge isn't stable on this rubric and the score is noisy.
|
|
23
|
+
*/
|
|
24
|
+
judgeRepeat?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Multi-judge ensemble: if provided with >= 2 judges, every (sample × dimension) is
|
|
27
|
+
* scored by every judge. Each judge can use a different executor (claude/openai/etc)
|
|
28
|
+
* and model. Output gets per-judge breakdown + Pearson/MAD agreement metrics — used
|
|
29
|
+
* to refute "Claude judge Claude same-modality bias" critique.
|
|
30
|
+
*/
|
|
31
|
+
judgeModels?: JudgeConfig[];
|
|
32
|
+
/**
|
|
33
|
+
* Map from executor name → ExecutorFn. Required when judgeModels has judges with
|
|
34
|
+
* different executor strings. Pipeline layer pre-creates these so grade() stays pure.
|
|
35
|
+
*/
|
|
36
|
+
judgeExecutors?: Record<string, ExecutorFn>;
|
|
37
|
+
/**
|
|
38
|
+
* v0.21 length-debias toggle. Defaults to true — judge prompt includes the
|
|
39
|
+
* "length is not a quality signal" instruction, prompt template version is
|
|
40
|
+
* v3-cot-length. Set false (via `--no-debias-length`) to revert to the
|
|
41
|
+
* legacy v2-cot prompt for reproducing historical reports.
|
|
42
|
+
*/
|
|
43
|
+
lengthDebias?: boolean;
|
|
19
44
|
}
|
|
20
45
|
/**
|
|
21
46
|
* Grade a model output against a sample's criteria.
|
|
22
47
|
*/
|
|
23
|
-
export declare function grade({ output, sample, executor, judgeModel, allowLlmJudge, execMetrics, samplesDir }: GradeOptions): Promise<GradeResult>;
|
|
48
|
+
export declare function grade({ output, sample, executor, judgeModel, allowLlmJudge, execMetrics, samplesDir, judgeRepeat, judgeModels, judgeExecutors, lengthDebias }: GradeOptions): Promise<GradeResult>;
|
|
24
49
|
export { runAssertions, validateJsonSchema } from './assertions.js';
|
|
25
50
|
export { buildTraceSummary } from './judge.js';
|
|
26
51
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/grading/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/grading/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAKxG,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,UAAU,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAC;IAC3H,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;IAC5B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC5C;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAoB,EAAE,WAAgB,EAAE,UAAgB,EAAE,WAAe,EAAE,WAAW,EAAE,cAAc,EAAE,YAAmB,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAuGrO;AACD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
|