@tangle-network/agent-eval 0.117.1 → 0.118.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/CHANGELOG.md +30 -0
- package/dist/analyst/index.d.ts +2772 -21
- package/dist/analyst/index.js +7 -6
- package/dist/analyst/index.js.map +1 -1
- package/dist/belief-state/index.d.ts +706 -10
- package/dist/belief-state/index.js +2 -1
- package/dist/belief-state/index.js.map +1 -1
- package/dist/benchmarks/index.d.ts +958 -14
- package/dist/benchmarks/index.js +12 -10
- package/dist/builder-eval/index.d.ts +449 -4
- package/dist/builder-eval/index.js +4 -3
- package/dist/builder-eval/index.js.map +1 -1
- package/dist/campaign/index.d.ts +4275 -73
- package/dist/campaign/index.js +12 -10
- package/dist/{chunk-VF3XSYTI.js → chunk-33JA4TFA.js} +6 -6
- package/dist/{chunk-4JLWXDYA.js → chunk-3EHHMC6E.js} +2 -2
- package/dist/{chunk-CCZIVI3F.js → chunk-BFW56GTT.js} +2 -2
- package/dist/{chunk-YZPO4UHR.js → chunk-FTUMG2U7.js} +124 -149
- package/dist/chunk-FTUMG2U7.js.map +1 -0
- package/dist/{chunk-E4BUPP7Z.js → chunk-HKUCJ437.js} +38 -66
- package/dist/chunk-HKUCJ437.js.map +1 -0
- package/dist/{chunk-HZHNRYHK.js → chunk-K6N6XJJX.js} +2 -2
- package/dist/chunk-KSDQVPLR.js +286 -0
- package/dist/chunk-KSDQVPLR.js.map +1 -0
- package/dist/chunk-MA6HLL3S.js +65 -0
- package/dist/chunk-MA6HLL3S.js.map +1 -0
- package/dist/{chunk-MGEHEHSN.js → chunk-OIIMMLRB.js} +11 -11
- package/dist/{chunk-DXZRATT5.js → chunk-OYZAPX5G.js} +3 -3
- package/dist/chunk-PXE2VKMX.js +140 -0
- package/dist/chunk-PXE2VKMX.js.map +1 -0
- package/dist/{chunk-JSJZ4PJ6.js → chunk-Q442S5AS.js} +17 -17
- package/dist/{chunk-ODVOOEWQ.js → chunk-QBRSJK47.js} +2 -2
- package/dist/{chunk-S2F4J57L.js → chunk-QKEGNI5B.js} +77 -32
- package/dist/chunk-QKEGNI5B.js.map +1 -0
- package/dist/{chunk-5UF54T55.js → chunk-S3UZOQ5Y.js} +34 -7
- package/dist/chunk-S3UZOQ5Y.js.map +1 -0
- package/dist/{chunk-FQNLDL4D.js → chunk-SVH2ANFD.js} +136 -4
- package/dist/chunk-SVH2ANFD.js.map +1 -0
- package/dist/{chunk-GQCZRZ7L.js → chunk-U5CHZ5M3.js} +9 -9
- package/dist/{chunk-TVVP3ZZQ.js → chunk-VQMK5FMP.js} +2 -1
- package/dist/{chunk-TVVP3ZZQ.js.map → chunk-VQMK5FMP.js.map} +1 -1
- package/dist/chunk-WDHBCA3M.js +31 -0
- package/dist/chunk-WDHBCA3M.js.map +1 -0
- package/dist/{chunk-HQPHZGL6.js → chunk-YLMUS4MM.js} +9 -9
- package/dist/{chunk-LQUTGLOZ.js → chunk-ZET2UAYW.js} +15 -65
- package/dist/chunk-ZET2UAYW.js.map +1 -0
- package/dist/contract/index.d.ts +4012 -38
- package/dist/contract/index.js +56 -29
- package/dist/contract/index.js.map +1 -1
- package/dist/control.d.ts +1013 -9
- package/dist/control.js +4 -3
- package/dist/fuzz.d.ts +194 -4
- package/dist/fuzz.js +3 -3
- package/dist/hosted/index.d.ts +498 -17
- package/dist/index.d.ts +10992 -1288
- package/dist/index.js +101 -80
- package/dist/index.js.map +1 -1
- package/dist/matrix/index.d.ts +139 -4
- package/dist/meta-eval/index.d.ts +862 -15
- package/dist/meta-eval/index.js +2 -1
- package/dist/meta-eval/index.js.map +1 -1
- package/dist/multishot/index.d.ts +214 -14
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.d.ts +392 -7
- package/dist/pipelines/index.js +5 -3
- package/dist/pipelines/index.js.map +1 -1
- package/dist/reporting.d.ts +1277 -17
- package/dist/rl.d.ts +2359 -28
- package/dist/rl.js +6 -5
- package/dist/rl.js.map +1 -1
- package/dist/storyboard/index.d.ts +86 -1
- package/dist/trace-attributes.d.ts +16 -0
- package/dist/trace-attributes.js +32 -0
- package/dist/trace-attributes.js.map +1 -0
- package/dist/traces.d.ts +1978 -697
- package/dist/traces.js +53 -32
- package/dist/wire/index.d.ts +655 -9
- package/docs/insight-report.md +44 -0
- package/package.json +9 -3
- package/dist/adversarial-B7loGVVX.d.ts +0 -19
- package/dist/analyst-C8HHvfJp.d.ts +0 -88
- package/dist/analyze-runs--2x39HZ7.d.ts +0 -81
- package/dist/baseline-DKq3gJpP.d.ts +0 -141
- package/dist/calibration-C8MTS7cw.d.ts +0 -101
- package/dist/chunk-5UF54T55.js.map +0 -1
- package/dist/chunk-E4BUPP7Z.js.map +0 -1
- package/dist/chunk-FQNLDL4D.js.map +0 -1
- package/dist/chunk-LQUTGLOZ.js.map +0 -1
- package/dist/chunk-S2F4J57L.js.map +0 -1
- package/dist/chunk-YZPO4UHR.js.map +0 -1
- package/dist/code-agent-session-CjZsVd19.d.ts +0 -87
- package/dist/control-6vuGfmDH.d.ts +0 -258
- package/dist/cost-ledger-DWy3XdJc.d.ts +0 -183
- package/dist/dataset-NENEzRgk.d.ts +0 -115
- package/dist/default-registry-DaK8b3fv.d.ts +0 -155
- package/dist/emitter-CjD7vUwv.d.ts +0 -122
- package/dist/errors-oeQrLqXC.d.ts +0 -74
- package/dist/failure-cluster-DOAcSJ87.d.ts +0 -76
- package/dist/feedback-trajectory-BUnM58xL.d.ts +0 -348
- package/dist/gepa-eESocoDi.d.ts +0 -642
- package/dist/index-PdX4VnPA.d.ts +0 -423
- package/dist/insight-report-DY4nDW9Q.d.ts +0 -310
- package/dist/integrity-DqlBiLyK.d.ts +0 -81
- package/dist/judge-calibration-7C-IDmKr.d.ts +0 -145
- package/dist/kind-factory-ClZmO25A.d.ts +0 -171
- package/dist/llm-client-qoDd18Qz.d.ts +0 -289
- package/dist/multi-layer-verifier-BsqKuLyN.d.ts +0 -150
- package/dist/off-policy-DiwuKKg7.d.ts +0 -132
- package/dist/outcome-store-rnXLEqSn.d.ts +0 -63
- package/dist/policy-edit-wG9uFEFm.d.ts +0 -455
- package/dist/pre-registration-BWQhJ3vz.d.ts +0 -761
- package/dist/provenance-DpjwyseI.d.ts +0 -541
- package/dist/query-CF7PG61p.d.ts +0 -35
- package/dist/raw-provider-sink-C46HDghv.d.ts +0 -132
- package/dist/release-report-C8G2i5Xi.d.ts +0 -236
- package/dist/researcher-C8XyxQsu.d.ts +0 -387
- package/dist/rubric-predictive-validity-p49lLVrE.d.ts +0 -105
- package/dist/run-record-BDH49H2E.d.ts +0 -360
- package/dist/runtime-trajectory-DGBIUt4B.d.ts +0 -49
- package/dist/schema-B3Q3l9Z_.d.ts +0 -201
- package/dist/semantic-concept-judge-CXnPEJbf.d.ts +0 -723
- package/dist/sequential-5iSVfzl2.d.ts +0 -139
- package/dist/series-convergence-D5OWMBg6.d.ts +0 -33
- package/dist/statistics-KUnG73jH.d.ts +0 -494
- package/dist/storage-DrX3v_5B.d.ts +0 -50
- package/dist/store-C1YxJDEK.d.ts +0 -248
- package/dist/store-DGqD0Pyo.d.ts +0 -116
- package/dist/summary-report-C5bKFfm-.d.ts +0 -445
- package/dist/test-graded-scenario-B0ybnPY7.d.ts +0 -166
- package/dist/types-BSw1rOUB.d.ts +0 -634
- package/dist/types-BUxNaJ8c.d.ts +0 -108
- package/dist/types-BkfcQnxV.d.ts +0 -313
- package/dist/verdict-C9MlYujm.d.ts +0 -35
- /package/dist/{chunk-VF3XSYTI.js.map → chunk-33JA4TFA.js.map} +0 -0
- /package/dist/{chunk-4JLWXDYA.js.map → chunk-3EHHMC6E.js.map} +0 -0
- /package/dist/{chunk-CCZIVI3F.js.map → chunk-BFW56GTT.js.map} +0 -0
- /package/dist/{chunk-HZHNRYHK.js.map → chunk-K6N6XJJX.js.map} +0 -0
- /package/dist/{chunk-MGEHEHSN.js.map → chunk-OIIMMLRB.js.map} +0 -0
- /package/dist/{chunk-DXZRATT5.js.map → chunk-OYZAPX5G.js.map} +0 -0
- /package/dist/{chunk-JSJZ4PJ6.js.map → chunk-Q442S5AS.js.map} +0 -0
- /package/dist/{chunk-ODVOOEWQ.js.map → chunk-QBRSJK47.js.map} +0 -0
- /package/dist/{chunk-GQCZRZ7L.js.map → chunk-U5CHZ5M3.js.map} +0 -0
- /package/dist/{chunk-HQPHZGL6.js.map → chunk-YLMUS4MM.js.map} +0 -0
|
@@ -1,17 +1,864 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
type RunStatus = 'running' | 'completed' | 'failed' | 'aborted';
|
|
2
|
+
interface BudgetSpec {
|
|
3
|
+
tokens?: number;
|
|
4
|
+
wallMs?: number;
|
|
5
|
+
calls?: number;
|
|
6
|
+
usd?: number;
|
|
7
|
+
}
|
|
8
|
+
interface RunOutcome$1 {
|
|
9
|
+
score?: number;
|
|
10
|
+
pass?: boolean;
|
|
11
|
+
failureClass?: FailureClass;
|
|
12
|
+
notes?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Layer — optional classification in a nested build workflow.
|
|
16
|
+
* `builder`: the meta-agent editing a project (e.g. agent-builder Forge chat).
|
|
17
|
+
* `app-build`: sandbox harness that compiled + tested the generated scaffold.
|
|
18
|
+
* `app-runtime`: a run of the generated agent against a domain scenario.
|
|
19
|
+
* `meta`: any meta-eval (judge replay, correlation analysis).
|
|
20
|
+
*/
|
|
21
|
+
type RunLayer = 'builder' | 'app-build' | 'app-runtime' | 'meta' | 'custom';
|
|
22
|
+
interface Run {
|
|
23
|
+
runId: string;
|
|
24
|
+
/**
|
|
25
|
+
* Stable identifier of the scenario being executed.
|
|
26
|
+
*
|
|
27
|
+
* Always populated on the persisted Run — but `TraceEmitter.startRun` accepts
|
|
28
|
+
* input WITHOUT this field, substituting a sensible default
|
|
29
|
+
* (`run.layer ?? run.tags?.['kind'] ?? 'runtime'`) when the caller has no
|
|
30
|
+
* curated scenario to anchor to (runtime / operator / meta-eval runs). This
|
|
31
|
+
* keeps the persisted shape unambiguous for downstream filters + aggregations
|
|
32
|
+
* while removing the boilerplate of inventing placeholder ids at the call site.
|
|
33
|
+
*/
|
|
34
|
+
scenarioId: string;
|
|
35
|
+
variantId?: string;
|
|
36
|
+
datasetVersion?: string;
|
|
37
|
+
/** Git SHA of agent code at run time. */
|
|
38
|
+
codeSha?: string;
|
|
39
|
+
/** Hash of the prompt template + any system prompt. */
|
|
40
|
+
promptSha?: string;
|
|
41
|
+
/** Model id + date + system-prompt hash, concatenated. */
|
|
42
|
+
modelFingerprint?: string;
|
|
43
|
+
seed?: number;
|
|
44
|
+
/** Arbitrary environment markers (shell, docker version, tz). */
|
|
45
|
+
envFingerprint?: Record<string, string>;
|
|
46
|
+
/** Version of the redaction rules applied to this run. */
|
|
47
|
+
redactionVersion?: string;
|
|
48
|
+
/** Parent run in a nested build workflow. A builder run's children are
|
|
49
|
+
* app-build runs; those children are app-runtime runs. */
|
|
50
|
+
parentRunId?: string;
|
|
51
|
+
/** Stable project identifier — groups runs across chats + sessions. */
|
|
52
|
+
projectId?: string;
|
|
53
|
+
/** Chat/conversation identifier within a project. */
|
|
54
|
+
chatId?: string;
|
|
55
|
+
/** Layer classification — hint for aggregation; not enforced. */
|
|
56
|
+
layer?: RunLayer;
|
|
57
|
+
startedAt: number;
|
|
58
|
+
endedAt?: number;
|
|
59
|
+
status: RunStatus;
|
|
60
|
+
outcome?: RunOutcome$1;
|
|
61
|
+
budget?: BudgetSpec;
|
|
62
|
+
/** Free-form labels for downstream grouping. */
|
|
63
|
+
tags?: Record<string, string>;
|
|
64
|
+
}
|
|
65
|
+
type SpanKind = 'agent' | 'llm' | 'tool' | 'retrieval' | 'judge' | 'sandbox' | 'custom';
|
|
66
|
+
type SpanStatus = 'ok' | 'error';
|
|
67
|
+
interface SpanBase {
|
|
68
|
+
spanId: string;
|
|
69
|
+
parentSpanId?: string;
|
|
70
|
+
runId: string;
|
|
71
|
+
kind: SpanKind;
|
|
72
|
+
name: string;
|
|
73
|
+
startedAt: number;
|
|
74
|
+
endedAt?: number;
|
|
75
|
+
status?: SpanStatus;
|
|
76
|
+
error?: string;
|
|
77
|
+
/** Anything not covered by typed fields. Kept deliberately free-form. */
|
|
78
|
+
attributes?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
interface Message {
|
|
81
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
82
|
+
content: string;
|
|
83
|
+
tokens?: number;
|
|
84
|
+
/** Multi-modal content descriptors; blobs themselves live in Artifacts. */
|
|
85
|
+
images?: Array<{
|
|
86
|
+
artifactId?: string;
|
|
87
|
+
url?: string;
|
|
88
|
+
mime?: string;
|
|
89
|
+
}>;
|
|
90
|
+
}
|
|
91
|
+
interface LlmSpan extends SpanBase {
|
|
92
|
+
kind: 'llm';
|
|
93
|
+
model: string;
|
|
94
|
+
messages: Message[];
|
|
95
|
+
output?: string;
|
|
96
|
+
inputTokens?: number;
|
|
97
|
+
/** All generated tokens, including the reasoning subset when present. */
|
|
98
|
+
outputTokens?: number;
|
|
99
|
+
cachedTokens?: number;
|
|
100
|
+
cacheWriteTokens?: number;
|
|
101
|
+
/** Reasoning-token subset of `outputTokens`. */
|
|
102
|
+
reasoningTokens?: number;
|
|
103
|
+
costUsd?: number;
|
|
104
|
+
finishReason?: string;
|
|
105
|
+
}
|
|
106
|
+
interface ToolSpan extends SpanBase {
|
|
107
|
+
kind: 'tool';
|
|
108
|
+
toolName: string;
|
|
109
|
+
args: unknown;
|
|
110
|
+
/** False when the source observed the call but did not capture its arguments. */
|
|
111
|
+
argsCaptured?: boolean;
|
|
112
|
+
result?: unknown;
|
|
113
|
+
latencyMs?: number;
|
|
114
|
+
}
|
|
115
|
+
interface RetrievalSpan extends SpanBase {
|
|
116
|
+
kind: 'retrieval';
|
|
117
|
+
query: string;
|
|
118
|
+
hits: Array<{
|
|
119
|
+
docId: string;
|
|
120
|
+
score: number;
|
|
121
|
+
content?: string;
|
|
122
|
+
}>;
|
|
123
|
+
}
|
|
124
|
+
interface JudgeSpan extends SpanBase {
|
|
125
|
+
kind: 'judge';
|
|
126
|
+
judgeId: string;
|
|
127
|
+
/** Span this judgment applies to. */
|
|
128
|
+
targetSpanId: string;
|
|
129
|
+
dimension: string;
|
|
130
|
+
/** Numeric score (free-range; interpretation up to the judge). */
|
|
131
|
+
score: number;
|
|
132
|
+
rationale?: string;
|
|
133
|
+
evidence?: string;
|
|
134
|
+
}
|
|
135
|
+
interface SandboxSpan extends SpanBase {
|
|
136
|
+
kind: 'sandbox';
|
|
137
|
+
image?: string;
|
|
138
|
+
command?: string;
|
|
139
|
+
exitCode?: number;
|
|
140
|
+
testsTotal?: number;
|
|
141
|
+
testsPassed?: number;
|
|
142
|
+
stdoutHash?: string;
|
|
143
|
+
stderrHash?: string;
|
|
144
|
+
/** Duration in ms; the harness fills this explicitly (endedAt - startedAt may miss setup). */
|
|
145
|
+
wallMs?: number;
|
|
146
|
+
}
|
|
147
|
+
interface GenericSpan extends SpanBase {
|
|
148
|
+
kind: 'agent' | 'custom';
|
|
149
|
+
}
|
|
150
|
+
type Span = LlmSpan | ToolSpan | RetrievalSpan | JudgeSpan | SandboxSpan | GenericSpan;
|
|
151
|
+
type EventKind = 'log' | 'error' | 'budget_decrement' | 'budget_breach' | 'state_mutation' | 'policy_violation' | 'redaction_applied' | 'custom';
|
|
152
|
+
interface TraceEvent {
|
|
153
|
+
eventId: string;
|
|
154
|
+
runId: string;
|
|
155
|
+
spanId?: string;
|
|
156
|
+
kind: EventKind;
|
|
157
|
+
timestamp: number;
|
|
158
|
+
payload: Record<string, unknown>;
|
|
159
|
+
}
|
|
160
|
+
interface BudgetLedgerEntry {
|
|
161
|
+
runId: string;
|
|
162
|
+
dimension: keyof BudgetSpec;
|
|
163
|
+
limit: number;
|
|
164
|
+
consumed: number;
|
|
165
|
+
remaining: number;
|
|
166
|
+
timestamp: number;
|
|
167
|
+
breached: boolean;
|
|
168
|
+
/** Span that triggered this entry, if any. */
|
|
169
|
+
spanId?: string;
|
|
170
|
+
}
|
|
171
|
+
interface Artifact {
|
|
172
|
+
artifactId: string;
|
|
173
|
+
runId: string;
|
|
174
|
+
spanId?: string;
|
|
175
|
+
contentType: string;
|
|
176
|
+
sizeBytes: number;
|
|
177
|
+
/** sha256 in hex. */
|
|
178
|
+
hash: string;
|
|
179
|
+
/** External storage URL (R2, S3, filesystem path). */
|
|
180
|
+
storageUrl?: string;
|
|
181
|
+
/** Inline content for small blobs — keep under ~64KB. */
|
|
182
|
+
inlineContent?: string;
|
|
183
|
+
}
|
|
184
|
+
type FailureClass = 'success' | 'reasoning_error' | 'tool_selection_error' | 'tool_argument_error' | 'tool_recovery_failure' | 'hallucination' | 'instruction_following' | 'safety_refusal_miss' | 'policy_violation' | 'budget_exceeded' | 'format_drift' | 'permission_escalation' | 'pii_leak' | 'cost_overrun' | 'timeout' | 'sandbox_failure' | 'missing_user_data' | 'missing_domain_data' | 'missing_codebase_context' | 'missing_runtime_context' | 'missing_credentials' | 'missing_integration_connection' | 'missing_integration_scope' | 'integration_approval_required' | 'integration_auth_expired' | 'integration_provider_failure' | 'bad_integration_manifest' | 'unsafe_integration_write_denied' | 'stale_external_data' | 'bad_retrieval' | 'insufficient_evidence' | 'contradictory_evidence' | 'ambiguous_user_intent' | 'knowledge_readiness_blocked' | 'unknown';
|
|
185
|
+
|
|
186
|
+
interface RunFilter {
|
|
187
|
+
scenarioId?: string;
|
|
188
|
+
variantId?: string;
|
|
189
|
+
status?: RunStatus;
|
|
190
|
+
since?: number;
|
|
191
|
+
until?: number;
|
|
192
|
+
tag?: {
|
|
193
|
+
key: string;
|
|
194
|
+
value: string;
|
|
195
|
+
};
|
|
196
|
+
parentRunId?: string;
|
|
197
|
+
projectId?: string;
|
|
198
|
+
chatId?: string;
|
|
199
|
+
layer?: RunLayer;
|
|
200
|
+
}
|
|
201
|
+
interface SpanFilter {
|
|
202
|
+
runId?: string;
|
|
203
|
+
parentSpanId?: string;
|
|
204
|
+
kind?: SpanKind;
|
|
205
|
+
name?: string;
|
|
206
|
+
toolName?: string;
|
|
207
|
+
judgeId?: string;
|
|
208
|
+
since?: number;
|
|
209
|
+
until?: number;
|
|
210
|
+
}
|
|
211
|
+
interface EventFilter {
|
|
212
|
+
runId?: string;
|
|
213
|
+
spanId?: string;
|
|
214
|
+
kind?: EventKind;
|
|
215
|
+
since?: number;
|
|
216
|
+
until?: number;
|
|
217
|
+
}
|
|
218
|
+
interface TraceStore {
|
|
219
|
+
appendRun(run: Run): Promise<void>;
|
|
220
|
+
updateRun(runId: string, patch: Partial<Run>): Promise<void>;
|
|
221
|
+
appendSpan(span: Span): Promise<void>;
|
|
222
|
+
updateSpan(spanId: string, patch: Partial<Span>): Promise<void>;
|
|
223
|
+
appendEvent(event: TraceEvent): Promise<void>;
|
|
224
|
+
appendArtifact(artifact: Artifact): Promise<void>;
|
|
225
|
+
appendBudgetEntry(entry: BudgetLedgerEntry): Promise<void>;
|
|
226
|
+
getRun(runId: string): Promise<Run | undefined>;
|
|
227
|
+
listRuns(filter?: RunFilter): Promise<Run[]>;
|
|
228
|
+
spans(filter?: SpanFilter): Promise<Span[]>;
|
|
229
|
+
events(filter?: EventFilter): Promise<TraceEvent[]>;
|
|
230
|
+
budget(runId: string): Promise<BudgetLedgerEntry[]>;
|
|
231
|
+
artifacts(runId: string): Promise<Artifact[]>;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* OutcomeStore — deployment outcomes attached to Run IDs.
|
|
236
|
+
*
|
|
237
|
+
* Outcomes arrive asynchronously from production telemetry after the
|
|
238
|
+
* eval run completed: user ratings, retention flags, conversion events,
|
|
239
|
+
* revenue, support-ticket rate, anything a product team can measure.
|
|
240
|
+
* The store is a peer to TraceStore — separate lifecycle, same runId
|
|
241
|
+
* foreign key.
|
|
242
|
+
*
|
|
243
|
+
* The whole point of this module is to make the meta-eval correlation
|
|
244
|
+
* question computable: `correlate(evalMetric, outcomeMetric) → r, ρ, n, CI`.
|
|
245
|
+
*/
|
|
246
|
+
interface DeploymentOutcome {
|
|
247
|
+
runId: string;
|
|
248
|
+
capturedAt: number;
|
|
249
|
+
/** Numeric outcomes keyed by name — retention_7d, csat, revenue_usd, etc. */
|
|
250
|
+
metrics: Record<string, number>;
|
|
251
|
+
/** Dimensions for stratified analysis — cohort, region, user_segment. */
|
|
252
|
+
labels?: Record<string, string>;
|
|
253
|
+
/** Free-form provenance (source system, pipeline version). */
|
|
254
|
+
source?: string;
|
|
255
|
+
}
|
|
256
|
+
interface OutcomeFilter {
|
|
257
|
+
runIds?: string[];
|
|
258
|
+
since?: number;
|
|
259
|
+
until?: number;
|
|
260
|
+
label?: {
|
|
261
|
+
key: string;
|
|
262
|
+
value: string;
|
|
263
|
+
};
|
|
264
|
+
source?: string;
|
|
265
|
+
}
|
|
266
|
+
interface OutcomeStore {
|
|
267
|
+
append(outcome: DeploymentOutcome): Promise<void>;
|
|
268
|
+
/** All outcomes attached to this run (a single run can have many — multiple
|
|
269
|
+
* capture windows over deployment time). */
|
|
270
|
+
forRun(runId: string): Promise<DeploymentOutcome[]>;
|
|
271
|
+
list(filter?: OutcomeFilter): Promise<DeploymentOutcome[]>;
|
|
272
|
+
}
|
|
273
|
+
declare class InMemoryOutcomeStore implements OutcomeStore {
|
|
274
|
+
private items;
|
|
275
|
+
append(outcome: DeploymentOutcome): Promise<void>;
|
|
276
|
+
forRun(runId: string): Promise<DeploymentOutcome[]>;
|
|
277
|
+
list(filter?: OutcomeFilter): Promise<DeploymentOutcome[]>;
|
|
278
|
+
}
|
|
279
|
+
interface FileSystemOutcomeStoreOptions {
|
|
280
|
+
dir: string;
|
|
281
|
+
maxBytes?: number;
|
|
282
|
+
}
|
|
283
|
+
declare class FileSystemOutcomeStore implements OutcomeStore {
|
|
284
|
+
private dir;
|
|
285
|
+
private maxBytes;
|
|
286
|
+
private memo?;
|
|
287
|
+
private loaded;
|
|
288
|
+
constructor(options: FileSystemOutcomeStoreOptions);
|
|
289
|
+
private ensureDir;
|
|
290
|
+
append(outcome: DeploymentOutcome): Promise<void>;
|
|
291
|
+
private load;
|
|
292
|
+
forRun(runId: string): Promise<DeploymentOutcome[]>;
|
|
293
|
+
list(filter?: OutcomeFilter): Promise<DeploymentOutcome[]>;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Correlation study — "does our eval score predict real-world outcomes?"
|
|
298
|
+
*
|
|
299
|
+
* This is the load-bearing signal. Takes a TraceStore + OutcomeStore,
|
|
300
|
+
* joins on runId, computes Pearson + Spearman + bootstrap CI for every
|
|
301
|
+
* (evalMetric, outcomeMetric) pair the caller declares.
|
|
302
|
+
*
|
|
303
|
+
* Without this number the framework is ornamental. With it and r > 0.6
|
|
304
|
+
* the framework is a moat — no other agent-eval tool publishes one.
|
|
305
|
+
*/
|
|
306
|
+
|
|
307
|
+
interface EvalMetricSpec {
|
|
308
|
+
id: string;
|
|
309
|
+
/** Extract a scalar from a run (defaults cover score/pass/durationMs/costUsd/tokens). */
|
|
310
|
+
extract?: (run: Run, store: TraceStore) => Promise<number | null>;
|
|
311
|
+
}
|
|
312
|
+
interface OutcomePair {
|
|
313
|
+
evalMetric: string;
|
|
314
|
+
outcomeMetric: string;
|
|
315
|
+
}
|
|
316
|
+
interface CorrelationResult {
|
|
317
|
+
evalMetric: string;
|
|
318
|
+
outcomeMetric: string;
|
|
319
|
+
n: number;
|
|
320
|
+
pearson: number;
|
|
321
|
+
spearman: number;
|
|
322
|
+
/** 95% bootstrap CI for Pearson. */
|
|
323
|
+
pearsonCi95: {
|
|
324
|
+
lower: number;
|
|
325
|
+
upper: number;
|
|
326
|
+
};
|
|
327
|
+
/** Rough verdict: 'strong' ≥ 0.7, 'moderate' ≥ 0.4, else 'weak'. */
|
|
328
|
+
verdict: 'strong' | 'moderate' | 'weak';
|
|
329
|
+
}
|
|
330
|
+
interface CorrelationStudyResult {
|
|
331
|
+
pairs: CorrelationResult[];
|
|
332
|
+
joinedSamples: number;
|
|
333
|
+
skippedRuns: number;
|
|
334
|
+
}
|
|
335
|
+
interface CorrelationStudyOptions {
|
|
336
|
+
/** Only join outcomes captured within this window after run.startedAt. */
|
|
337
|
+
maxCaptureLagMs?: number;
|
|
338
|
+
/** Restrict to a subset of outcomes (cohort, region, source). */
|
|
339
|
+
outcomeFilter?: OutcomeFilter;
|
|
340
|
+
/** Which outcome per run to use when multiple exist. Default 'latest'. */
|
|
341
|
+
reduction?: 'latest' | 'mean' | 'max';
|
|
342
|
+
/** Bootstrap iterations for the CI. Default 500. */
|
|
343
|
+
bootstrapIterations?: number;
|
|
344
|
+
}
|
|
345
|
+
declare function correlationStudy(traceStore: TraceStore, outcomeStore: OutcomeStore, evalMetrics: EvalMetricSpec[], outcomeMetricNames: string[], options?: CorrelationStudyOptions): Promise<CorrelationStudyResult>;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Calibration curve — binned "if eval says X, what does reality show?"
|
|
349
|
+
*
|
|
350
|
+
* Companion to correlationStudy. Raw correlation is a single number;
|
|
351
|
+
* the calibration curve shows *where* the eval is well-calibrated vs
|
|
352
|
+
* overconfident / underconfident. Buckets the eval metric, computes
|
|
353
|
+
* mean outcome per bucket, reports expected-calibration-error (ECE).
|
|
354
|
+
*/
|
|
355
|
+
|
|
356
|
+
interface CalibrationBin {
|
|
357
|
+
lower: number;
|
|
358
|
+
upper: number;
|
|
359
|
+
n: number;
|
|
360
|
+
evalMean: number;
|
|
361
|
+
outcomeMean: number;
|
|
362
|
+
/** |outcomeMean − evalMean|; contributes to ECE weighted by n/total. */
|
|
363
|
+
gap: number;
|
|
364
|
+
}
|
|
365
|
+
interface CalibrationReport {
|
|
366
|
+
evalMetric: string;
|
|
367
|
+
outcomeMetric: string;
|
|
368
|
+
n: number;
|
|
369
|
+
bins: CalibrationBin[];
|
|
370
|
+
/** Expected Calibration Error — Σ (n_i/N) × |outcomeMean_i − evalMean_i|. */
|
|
371
|
+
ece: number;
|
|
372
|
+
/** Max bin gap — upper bound on miscalibration. */
|
|
373
|
+
maxGap: number;
|
|
374
|
+
}
|
|
375
|
+
interface CalibrationOptions {
|
|
376
|
+
bins?: number;
|
|
377
|
+
/** Equal-width (fixed bin edges) or equal-frequency (quantile bins). */
|
|
378
|
+
binning?: 'equal-width' | 'equal-frequency';
|
|
379
|
+
/** Clip eval values to [lo, hi] before binning. */
|
|
380
|
+
range?: {
|
|
381
|
+
lo: number;
|
|
382
|
+
hi: number;
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
interface CalibrationPair {
|
|
386
|
+
evalScore: number;
|
|
387
|
+
outcome: number;
|
|
388
|
+
}
|
|
389
|
+
declare function calibrationCurve(traceStore: TraceStore, outcomeStore: OutcomeStore, evalMetric: EvalMetricSpec, outcomeMetric: string, options?: CalibrationOptions): Promise<CalibrationReport | null>;
|
|
390
|
+
declare function calibrationFromPairs(inputPairs: CalibrationPair[], evalMetric: string, outcomeMetric: string, options?: CalibrationOptions): CalibrationReport | null;
|
|
391
|
+
|
|
392
|
+
type AgentProfileCellSchemaVersion = 'agent-profile-cell/v1';
|
|
393
|
+
type AgentProfileDimensionValue = string | number | boolean | null;
|
|
394
|
+
interface AgentProfileSource {
|
|
395
|
+
/** Runtime/profile contract being fingerprinted, e.g. `agent-interface-profile`. */
|
|
396
|
+
kind: string;
|
|
397
|
+
/** sha256 over the canonical source profile object. */
|
|
398
|
+
hash: string;
|
|
399
|
+
}
|
|
400
|
+
interface AgentProfileHarness {
|
|
401
|
+
id: string;
|
|
402
|
+
version?: string;
|
|
403
|
+
hash?: string;
|
|
404
|
+
}
|
|
405
|
+
interface AgentProfileCell {
|
|
406
|
+
schemaVersion: AgentProfileCellSchemaVersion;
|
|
407
|
+
cellId: string;
|
|
408
|
+
profileId: string;
|
|
409
|
+
sourceProfile: AgentProfileSource;
|
|
410
|
+
harness?: AgentProfileHarness;
|
|
411
|
+
model?: string;
|
|
412
|
+
promptHash?: string;
|
|
413
|
+
dimensions?: Record<string, AgentProfileDimensionValue>;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Paper-grade RunRecord schema + runtime validator.
|
|
418
|
+
*
|
|
419
|
+
* Every run that participates in a promotion gate, paper table, or
|
|
420
|
+
* researcher loop SHOULD be recorded as a `RunRecord`. The mandatory
|
|
421
|
+
* fields are exactly those the paper "Two Loops, Three Roles" requires
|
|
422
|
+
* for reproducibility: who/what/when/cost/seed/hash, plus the search vs
|
|
423
|
+
* holdout split tag and either a `searchScore` or a `holdoutScore`.
|
|
424
|
+
*
|
|
425
|
+
* This is intentionally NOT a replacement for the rich `Run` /
|
|
426
|
+
* `ProposeReviewReport` / `ScenarioResult` types already in the
|
|
427
|
+
* package. Those are runtime structures with full provenance. A
|
|
428
|
+
* `RunRecord` is the analysis-time projection — the JSON-friendly
|
|
429
|
+
* row you'd put in a parquet file or paste into a notebook.
|
|
430
|
+
*
|
|
431
|
+
* Validate at the boundary:
|
|
432
|
+
*
|
|
433
|
+
* const rec = validateRunRecord(rawJson) // throws on missing
|
|
434
|
+
* const ok = isRunRecord(rawJson) // boolean check
|
|
435
|
+
* const rec = parseRunRecordSafe(rawJson) // { ok, value | error }
|
|
436
|
+
*
|
|
437
|
+
* The validator runs in pure TS — zod is intentionally NOT a
|
|
438
|
+
* dependency. Round-trip tested in `tests/run-record.test.ts`.
|
|
439
|
+
*/
|
|
440
|
+
|
|
441
|
+
/** Search/dev/holdout split tag. 'search' is the paper-grade alias for the
|
|
442
|
+
* combined train+test pool that the optimizer is allowed to read. */
|
|
443
|
+
type RunSplitTag = 'search' | 'dev' | 'holdout';
|
|
444
|
+
interface RunTokenUsage {
|
|
445
|
+
input: number;
|
|
446
|
+
/** All generated tokens charged as output, including reasoning tokens. */
|
|
447
|
+
output: number;
|
|
448
|
+
/** Reasoning-token subset of `output`, when the provider reports it. */
|
|
449
|
+
reasoning?: number;
|
|
450
|
+
/** Prompt tokens served from a provider cache. */
|
|
451
|
+
cached?: number;
|
|
452
|
+
/** Prompt tokens written into a provider cache. */
|
|
453
|
+
cacheWrite?: number;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* How a run's USD amount was obtained.
|
|
457
|
+
*
|
|
458
|
+
* `costUsd` remains mandatory for wire compatibility. New producers should
|
|
459
|
+
* always populate this discriminated union so a missing bill is never
|
|
460
|
+
* mistaken for an observed zero-dollar run. For `uncaptured`, `costUsd` uses
|
|
461
|
+
* the legacy `0` sentinel while this field carries the truthful null.
|
|
462
|
+
*/
|
|
463
|
+
type RunCostProvenance = {
|
|
464
|
+
kind: 'observed';
|
|
465
|
+
usd: number;
|
|
466
|
+
} | {
|
|
467
|
+
kind: 'estimated';
|
|
468
|
+
usd: number;
|
|
469
|
+
} | {
|
|
470
|
+
kind: 'uncaptured';
|
|
471
|
+
usd: null;
|
|
472
|
+
};
|
|
473
|
+
interface RunJudgeMetadata {
|
|
474
|
+
model: string;
|
|
475
|
+
promptVersion: string;
|
|
476
|
+
/** [0,1] confidence the judge declared. Constant judge confidence
|
|
477
|
+
* across many runs is a fallback signal (see `canary.ts`). */
|
|
478
|
+
confidence: number;
|
|
479
|
+
/** True if the judge degraded to a fallback path (rules-only,
|
|
480
|
+
* prior-call cache, etc.). The canary uses this to alert. */
|
|
481
|
+
fallback: boolean;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Per-judge / per-dimension breakdown for runs scored by an ensemble of
|
|
485
|
+
* judges over a multi-dimensional rubric.
|
|
486
|
+
*
|
|
487
|
+
* The collapsed `outcome.searchScore` / `holdoutScore` carries the
|
|
488
|
+
* composite the gate uses. The full breakdown belongs here so consumers
|
|
489
|
+
* can answer "which judge disagreed?", "which dimension dragged the
|
|
490
|
+
* composite down?", and "did half the panel fail?" without re-running.
|
|
491
|
+
*
|
|
492
|
+
* `perJudge[judgeId][dim]` is the canonical source; `perDimMean` and
|
|
493
|
+
* `composite` are convenience projections — derivable but precomputed so
|
|
494
|
+
* downstream IRR primitives (`interRaterReliability`,
|
|
495
|
+
* `corpusInterRaterAgreement`) and reporters don't pay the same
|
|
496
|
+
* aggregation twice.
|
|
497
|
+
*
|
|
498
|
+
* Fail-loud discipline: judges that errored out land in `failedJudges`
|
|
499
|
+
* by id. A missing key in `perJudge` is ambiguous (silent zero vs not
|
|
500
|
+
* run); the explicit list makes a partial-failure recorded as such.
|
|
501
|
+
*/
|
|
502
|
+
interface JudgeScoresRecord {
|
|
503
|
+
/** Per-judge per-dimension scores. `{ "kimi-k2.6": { helpfulness: 0.8, clarity: 0.7 }, ... }`. */
|
|
504
|
+
perJudge: Record<string, Record<string, number>>;
|
|
505
|
+
/** Per-dim mean across judges. Convenience — derivable from `perJudge`. */
|
|
506
|
+
perDimMean: Record<string, number>;
|
|
507
|
+
/** Composite mean across all dims and judges. Mirrors the score
|
|
508
|
+
* the gate sees on `outcome.searchScore` / `holdoutScore`. */
|
|
509
|
+
composite: number;
|
|
510
|
+
/** Judges that errored or returned an unparseable verdict. Recorded
|
|
511
|
+
* by id (e.g. `['glm-5.1']`) so a partial-failure case is explicit,
|
|
512
|
+
* not inferred from missing keys in `perJudge`. */
|
|
513
|
+
failedJudges?: string[];
|
|
514
|
+
/** Free-form notes the judges emitted (joined across judges or
|
|
515
|
+
* first-judge only — consumer's choice). */
|
|
516
|
+
notes?: string;
|
|
517
|
+
}
|
|
518
|
+
interface RunOutcome {
|
|
519
|
+
/** Score on the search/optimization split. Optional because a
|
|
520
|
+
* holdout-only evaluation only fills `holdoutScore`. */
|
|
521
|
+
searchScore?: number;
|
|
522
|
+
/** Score on the held-out split. Optional because a search-only run
|
|
523
|
+
* only fills `searchScore`. At least one must be present. */
|
|
524
|
+
holdoutScore?: number;
|
|
525
|
+
/** Bag of any other metric the run produced — judge dimensions,
|
|
526
|
+
* pass/fail counters, latency stats, etc. Numeric only — keeps
|
|
527
|
+
* reporters honest. */
|
|
528
|
+
raw: Record<string, number>;
|
|
529
|
+
/** Per-judge / per-dim breakdown. Consumers writing ensemble
|
|
530
|
+
* judgements populate this; substrate primitives like
|
|
531
|
+
* `interRaterReliability` and `corpusInterRaterAgreement` accept
|
|
532
|
+
* these records as input. Optional — single-judge or scalar-only
|
|
533
|
+
* runs leave it unset. */
|
|
534
|
+
judgeScores?: JudgeScoresRecord;
|
|
535
|
+
/** Authenticity / realness verdict — did the run build the REAL thing on the
|
|
536
|
+
* intended infra, or fake it (see `./authenticity`)? Optional: only domains
|
|
537
|
+
* with an authenticity config populate it. Carried in the corpus so the
|
|
538
|
+
* flywheel / off-policy learning can optimize for real completion, not gamed
|
|
539
|
+
* pass-rate. `score` is 0-1; `gated` is the anti-Goodhart flag — a gated run
|
|
540
|
+
* must not count as a real success regardless of `score`. */
|
|
541
|
+
realness?: {
|
|
542
|
+
score: number;
|
|
543
|
+
gated: boolean;
|
|
544
|
+
reason?: string;
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Mandatory paper-grade fields for a single evaluation run. Optional
|
|
549
|
+
* fields are extension points; mandatory fields throw if missing.
|
|
550
|
+
*
|
|
551
|
+
* Hash discipline:
|
|
552
|
+
* - `promptHash` is the sha256 of the EFFECTIVE prompt sent to the
|
|
553
|
+
* model (after any steering bundle merge).
|
|
554
|
+
* - `configHash` is the sha256 of the effective run config (model,
|
|
555
|
+
* temperature, tools, judges, splits). The pair (promptHash,
|
|
556
|
+
* configHash) uniquely identifies an experiment cell.
|
|
557
|
+
*
|
|
558
|
+
* Model snapshot discipline:
|
|
559
|
+
* - `model` MUST encode a snapshot version. Bare aliases like
|
|
560
|
+
* `claude-sonnet-4` or `gpt-4o` are banned — they remap silently.
|
|
561
|
+
* Use `claude-sonnet-4-6@2025-04-15` or `gpt-4o-2024-11-20`.
|
|
562
|
+
*/
|
|
563
|
+
interface RunRecord {
|
|
564
|
+
/** UUID for the run. */
|
|
565
|
+
runId: string;
|
|
566
|
+
/** Logical experiment grouping (a treatment vs a baseline within
|
|
567
|
+
* the same sweep should share `experimentId`). */
|
|
568
|
+
experimentId: string;
|
|
569
|
+
/** Stable identifier for the candidate (variant) being run. The
|
|
570
|
+
* promotion gate compares two `candidateId`s on matched items. */
|
|
571
|
+
candidateId: string;
|
|
572
|
+
/** RNG seed for the run. Always recorded — silent re-seeding is
|
|
573
|
+
* the most common cause of non-reproducible numbers. */
|
|
574
|
+
seed: number;
|
|
575
|
+
/** Model identifier WITH snapshot version. */
|
|
576
|
+
model: string;
|
|
577
|
+
/** sha256 of the effective prompt (post-steering). */
|
|
578
|
+
promptHash: string;
|
|
579
|
+
/** sha256 of the effective config. */
|
|
580
|
+
configHash: string;
|
|
581
|
+
/** Git SHA the harness was run from. */
|
|
582
|
+
commitSha: string;
|
|
583
|
+
/** End-to-end wall-clock duration in milliseconds. */
|
|
584
|
+
wallMs: number;
|
|
585
|
+
/** Time spent queued before execution started, if known. */
|
|
586
|
+
queueMs?: number;
|
|
587
|
+
/** Total USD cost. Mandatory — runs without a cost number are
|
|
588
|
+
* unbounded by definition and must not be admitted into the gate.
|
|
589
|
+
* `0` is retained as the compatibility sentinel for an uncaptured amount;
|
|
590
|
+
* inspect `costProvenance` before treating it as observed. */
|
|
591
|
+
costUsd: number;
|
|
592
|
+
/** Observed, model-priced estimate, or genuinely uncaptured USD amount.
|
|
593
|
+
* Optional only so existing serialized RunRecords remain valid. */
|
|
594
|
+
costProvenance?: RunCostProvenance;
|
|
595
|
+
/** Token usage breakdown. */
|
|
596
|
+
tokenUsage: RunTokenUsage;
|
|
597
|
+
/** Judge-side metadata, if a judge was used. */
|
|
598
|
+
judgeMetadata?: RunJudgeMetadata;
|
|
599
|
+
/** Per-split scores + raw bag. */
|
|
600
|
+
outcome: RunOutcome;
|
|
601
|
+
/** Canonical, cross-agent failure class drawn from the shared
|
|
602
|
+
* `FAILURE_CLASSES` taxonomy. This is the aggregation key that makes
|
|
603
|
+
* "which failure dominates across the whole fleet" answerable in ONE
|
|
604
|
+
* vocabulary — every agent classifies against the same enum. Producers
|
|
605
|
+
* set it via the substrate classifier; leave unset only when the failure
|
|
606
|
+
* genuinely can't be classified. */
|
|
607
|
+
failureClass?: FailureClass;
|
|
608
|
+
/** Free-form domain-specific failure detail, scoped UNDER `failureClass`
|
|
609
|
+
* (e.g. failureClass='tool_recovery_failure', failureMode='forge_build_unsatisfied').
|
|
610
|
+
* The within-agent drill-down; `failureClass` is the cross-agent key. */
|
|
611
|
+
failureMode?: string;
|
|
612
|
+
/** Which split this run was drawn from. */
|
|
613
|
+
splitTag: RunSplitTag;
|
|
614
|
+
/**
|
|
615
|
+
* Stable scenario identifier the run was scored against. Optional for
|
|
616
|
+
* backwards compatibility, but **strongly recommended**: every primitive
|
|
617
|
+
* that pairs runs by scenario (preferences, paired stats, BT tournament)
|
|
618
|
+
* keys on this. The campaign artifact populates it canonically; legacy
|
|
619
|
+
* runs without it fall back to inference from `outcome.raw.scenario_id`
|
|
620
|
+
* or `experimentId`.
|
|
621
|
+
*/
|
|
622
|
+
scenarioId?: string;
|
|
623
|
+
/**
|
|
624
|
+
* Canonical identity for the agent profile cell that produced this row:
|
|
625
|
+
* profile artifact hash plus optional harness/model/prompt/reporting
|
|
626
|
+
* dimensions. Use `agentProfile.cellId` to group persona sweeps and
|
|
627
|
+
* longitudinal reports by the complete source profile, not by a loose
|
|
628
|
+
* candidate label or opaque config hash.
|
|
629
|
+
*/
|
|
630
|
+
agentProfile?: AgentProfileCell;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Rubric predictive validity — does our eval rubric predict deployment
|
|
635
|
+
* outcomes?
|
|
636
|
+
*
|
|
637
|
+
* `correlationStudy` (already in this package) joins a `TraceStore` to an
|
|
638
|
+
* `OutcomeStore` and computes Pearson + Spearman + bootstrap CI for each
|
|
639
|
+
* (eval-metric, outcome-metric) pair. That answers "does X correlate with
|
|
640
|
+
* Y at all." `rubricPredictiveValidity` is the campaign-shaped wrapper
|
|
641
|
+
* around it: take a sequence of `RunRecord`s (the canonical campaign
|
|
642
|
+
* artifact) and a `DeploymentOutcomeStore`, join on `runId`, return a
|
|
643
|
+
* ranked verdict on every rubric whose dimension scores were captured in
|
|
644
|
+
* `outcome.raw`.
|
|
645
|
+
*
|
|
646
|
+
* The point — quoting the methodology doc — is that **without this loop
|
|
647
|
+
* every rubric is faith-based**. Once it's wired, you know which rubrics
|
|
648
|
+
* have earned their promotion power and which ones are decoration.
|
|
649
|
+
*
|
|
650
|
+
* const validity = await rubricPredictiveValidity({
|
|
651
|
+
* runs: lastQuarter,
|
|
652
|
+
* outcomes: shipFlagOutcomeStore,
|
|
653
|
+
* outcomeMetrics: ['revenue_lift', 'retention_30d', 'csat'],
|
|
654
|
+
* rubrics: ['anti_slop', 'semantic_concept', 'tool_recovery'],
|
|
655
|
+
* })
|
|
656
|
+
* for (const r of validity.ranked) {
|
|
657
|
+
* console.log(`${r.rubric} → ${r.bestOutcome}: ρ=${r.spearman.toFixed(2)}`)
|
|
658
|
+
* }
|
|
659
|
+
*
|
|
660
|
+
* The function is intentionally read-only. Use the verdict to deprecate
|
|
661
|
+
* decorative rubrics, re-weight composite scores, or trigger a
|
|
662
|
+
* recalibration sweep when predictive validity drops below a threshold.
|
|
663
|
+
*/
|
|
664
|
+
|
|
665
|
+
interface RubricPredictiveValidityInput {
|
|
666
|
+
/**
|
|
667
|
+
* Canonical campaign output. Each record's `outcome.raw[<rubricId>]`
|
|
668
|
+
* provides the eval score; missing keys are silently skipped per pair.
|
|
669
|
+
*/
|
|
670
|
+
runs: RunRecord[];
|
|
671
|
+
outcomes: OutcomeStore;
|
|
672
|
+
/**
|
|
673
|
+
* Outcome metric names to evaluate against. Each must appear in at
|
|
674
|
+
* least one `DeploymentOutcome.metrics` keyspace; pairs with too few
|
|
675
|
+
* joined samples are excluded from the result.
|
|
676
|
+
*/
|
|
677
|
+
outcomeMetrics: string[];
|
|
678
|
+
/**
|
|
679
|
+
* Rubric ids to evaluate. Must appear as keys in `RunRecord.outcome.raw`.
|
|
680
|
+
* If omitted, every numeric key in `outcome.raw` across the run set is
|
|
681
|
+
* treated as a rubric.
|
|
682
|
+
*/
|
|
683
|
+
rubrics?: string[];
|
|
684
|
+
/** Minimum joined-sample count before a pair is reported. Default 8. */
|
|
685
|
+
minSamples?: number;
|
|
686
|
+
/** Bootstrap resamples for CI. Default 500. */
|
|
687
|
+
bootstrapResamples?: number;
|
|
688
|
+
/** Random seed for the bootstrap (mulberry32). Default unset (Math.random). */
|
|
689
|
+
seed?: number;
|
|
690
|
+
/**
|
|
691
|
+
* Reduction when multiple outcomes attach to one runId. Default `'latest'`
|
|
692
|
+
* (most recently captured).
|
|
693
|
+
*/
|
|
694
|
+
reduction?: 'latest' | 'mean' | 'max';
|
|
695
|
+
}
|
|
696
|
+
interface RubricOutcomePair {
|
|
697
|
+
rubric: string;
|
|
698
|
+
outcome: string;
|
|
699
|
+
n: number;
|
|
700
|
+
pearson: number;
|
|
701
|
+
spearman: number;
|
|
702
|
+
ci95: {
|
|
703
|
+
low: number;
|
|
704
|
+
high: number;
|
|
705
|
+
};
|
|
706
|
+
/**
|
|
707
|
+
* Verdict bucket. `load_bearing` ≥ 0.7, `informative` ≥ 0.4,
|
|
708
|
+
* `decorative` < 0.4 in absolute correlation. A negative correlation
|
|
709
|
+
* with a desired outcome is also `decorative` — actively misleading
|
|
710
|
+
* is worse than uninformative.
|
|
711
|
+
*/
|
|
712
|
+
verdict: 'load_bearing' | 'informative' | 'decorative';
|
|
713
|
+
}
|
|
714
|
+
interface RubricRanking {
|
|
715
|
+
rubric: string;
|
|
716
|
+
/** Outcome metric this rubric correlated best with. */
|
|
717
|
+
bestOutcome: string;
|
|
718
|
+
spearman: number;
|
|
719
|
+
pearson: number;
|
|
720
|
+
n: number;
|
|
721
|
+
verdict: RubricOutcomePair['verdict'];
|
|
722
|
+
}
|
|
723
|
+
interface RubricPredictiveValidityReport {
|
|
724
|
+
pairs: RubricOutcomePair[];
|
|
725
|
+
/** Per-rubric best pair, sorted descending by |spearman|. */
|
|
726
|
+
ranked: RubricRanking[];
|
|
727
|
+
joinedSamples: number;
|
|
728
|
+
skippedRuns: number;
|
|
729
|
+
/** Rubrics that were declared but never produced a usable score. */
|
|
730
|
+
rubricsWithoutData: string[];
|
|
731
|
+
}
|
|
732
|
+
declare function rubricPredictiveValidity(input: RubricPredictiveValidityInput): Promise<RubricPredictiveValidityReport>;
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Judge calibration — measure judge quality against human gold + bias.
|
|
736
|
+
*
|
|
737
|
+
* Workflow:
|
|
738
|
+
* 1. Build a golden set: {itemId, humanScore}[].
|
|
739
|
+
* 2. Run candidate judges; each produces {itemId, score}.
|
|
740
|
+
* 3. `calibrateJudge(golden, candidate)` reports κ + Pearson + MAE.
|
|
741
|
+
* 4. `calibrateJudgeContinuous(golden, candidate)` adds quadratic-weighted
|
|
742
|
+
* κ over the un-rounded [0,1] scores plus ICC(2,1), Pearson, Spearman,
|
|
743
|
+
* and bootstrap CIs — use this for fine-grained judges where rounding
|
|
744
|
+
* to int discards information (e.g. 0.78 vs 0.81 both round to 1 and
|
|
745
|
+
* look "perfectly agreed" to integer κ).
|
|
746
|
+
* 5. Run bias probes (positional, verbosity, self-preference) to
|
|
747
|
+
* detect systematic score inflation.
|
|
748
|
+
* 6. For N≥2 judges on the same items, `continuousAgreement(scores)`
|
|
749
|
+
* reports ICC(2,1) + κ_w + Pearson + Spearman with bootstrap CIs.
|
|
750
|
+
*
|
|
751
|
+
* Returns actionable diagnostics, not a single number. Consumers then
|
|
752
|
+
* decide whether to trust the judge, retrain it, or add a tie-breaker.
|
|
753
|
+
*/
|
|
754
|
+
interface GoldenItem {
|
|
755
|
+
itemId: string;
|
|
756
|
+
humanScore: number;
|
|
757
|
+
/** Optional group used for per-group bias audits (e.g. model-of-output family). */
|
|
758
|
+
group?: string;
|
|
759
|
+
}
|
|
760
|
+
interface CandidateScore {
|
|
761
|
+
itemId: string;
|
|
762
|
+
score: number;
|
|
763
|
+
/** Optional — enables positional-bias analysis (did order matter?). */
|
|
764
|
+
positionOfAInput?: 'first' | 'second';
|
|
765
|
+
}
|
|
766
|
+
interface CalibrationResult {
|
|
767
|
+
n: number;
|
|
768
|
+
pearson: number;
|
|
769
|
+
/** Cohen's κ with quadratic weights over integer-rounded scores. */
|
|
770
|
+
kappa: number;
|
|
771
|
+
/** Mean absolute error vs human. */
|
|
772
|
+
mae: number;
|
|
773
|
+
/** Worst-5 miscalibrations (largest |judge - human|). */
|
|
774
|
+
worstItems: Array<{
|
|
775
|
+
itemId: string;
|
|
776
|
+
judge: number;
|
|
777
|
+
human: number;
|
|
778
|
+
delta: number;
|
|
779
|
+
}>;
|
|
780
|
+
}
|
|
781
|
+
interface ContinuousAgreement {
|
|
782
|
+
/** Cohen's κ_w with quadratic weights, computed on raw [0,1] scores. */
|
|
783
|
+
weightedKappa: number;
|
|
784
|
+
/** ICC(2,1): two-way random effects, absolute agreement, single rater. */
|
|
785
|
+
icc: number;
|
|
786
|
+
/** Pearson product-moment correlation (averaged over rater pairs if N>2). */
|
|
787
|
+
pearson: number;
|
|
788
|
+
/** Spearman rank correlation (averaged over rater pairs if N>2). */
|
|
789
|
+
spearman: number;
|
|
790
|
+
/** 95% bootstrap percentile CIs over items. */
|
|
791
|
+
ci: {
|
|
792
|
+
icc: [number, number];
|
|
793
|
+
weightedKappa: [number, number];
|
|
794
|
+
};
|
|
795
|
+
/** Number of complete items (no NaN across raters). */
|
|
796
|
+
n: number;
|
|
797
|
+
/** Number of raters. */
|
|
798
|
+
raters: number;
|
|
799
|
+
}
|
|
800
|
+
interface ContinuousCalibrationResult extends CalibrationResult {
|
|
801
|
+
/** Cohen's κ_w computed on raw (un-rounded) scores. */
|
|
802
|
+
weightedKappaContinuous: number;
|
|
803
|
+
/** ICC(2,1) treating golden + candidate as two raters. */
|
|
804
|
+
icc: number;
|
|
805
|
+
spearman: number;
|
|
806
|
+
ci: {
|
|
807
|
+
icc: [number, number];
|
|
808
|
+
weightedKappa: [number, number];
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Series convergence — detects whether a sequence of scalar measurements
|
|
814
|
+
* is stabilizing, drifting, or noisy.
|
|
815
|
+
*
|
|
816
|
+
* Lifted from ADC convergence.ts. The per-turn `ConvergenceTracker` is
|
|
817
|
+
* about progress *within* a single run; this module is about drift
|
|
818
|
+
* *across* runs (e.g. "are my nightly eval scores stabilizing?").
|
|
819
|
+
*
|
|
820
|
+
* Three signals:
|
|
821
|
+
* - stabilized: last K values have low variance (< epsilon) — done
|
|
822
|
+
* - drifting: recent trend is monotonic and beyond noise — regressing or improving
|
|
823
|
+
* - noisy: neither — keep iterating, but flag as untrustworthy for gating
|
|
824
|
+
*/
|
|
825
|
+
interface SeriesConvergenceOptions {
|
|
826
|
+
/** Window size for "recent" analysis (default 5). */
|
|
827
|
+
window?: number;
|
|
828
|
+
/** Coefficient-of-variation threshold below which the window is stabilized (default 0.05 = 5%). */
|
|
829
|
+
stableCv?: number;
|
|
830
|
+
/** Minimum monotone run length to call drift (default 3). */
|
|
831
|
+
driftRun?: number;
|
|
832
|
+
}
|
|
833
|
+
interface SeriesConvergenceResult {
|
|
834
|
+
state: 'stabilized' | 'drifting-up' | 'drifting-down' | 'noisy' | 'insufficient-data';
|
|
835
|
+
windowMean: number;
|
|
836
|
+
windowCv: number;
|
|
837
|
+
/** Longest monotonic run at the tail of the series (positive for up, negative for down). */
|
|
838
|
+
tailRun: number;
|
|
839
|
+
/** True when n ≥ window AND windowCv ≤ stableCv. */
|
|
840
|
+
stable: boolean;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
interface CorpusAgreementPerDimension extends ContinuousAgreement {
|
|
844
|
+
dimension: string;
|
|
845
|
+
/** Item IDs that contributed to this dimension's matrix (every judge scored them). */
|
|
846
|
+
itemIds: string[];
|
|
847
|
+
/** Judge IDs that contributed to this dimension's matrix. */
|
|
848
|
+
judgeIds: string[];
|
|
849
|
+
}
|
|
850
|
+
interface CorpusAgreementReport {
|
|
851
|
+
/** Per-dimension ICC(2,1) + κ_w + Pearson + Spearman + bootstrap CIs. */
|
|
852
|
+
perDimension: CorpusAgreementPerDimension[];
|
|
853
|
+
/** Mean ICC across dimensions (NaN if no dimension yielded a finite ICC). */
|
|
854
|
+
overallIcc: number;
|
|
855
|
+
/** Mean weighted κ across dimensions (NaN if none finite). */
|
|
856
|
+
overallWeightedKappa: number;
|
|
857
|
+
/** Dimensions evaluated (sorted). */
|
|
858
|
+
dimensions: string[];
|
|
859
|
+
/** Judges seen across the corpus (sorted). */
|
|
860
|
+
judgeIds: string[];
|
|
861
|
+
}
|
|
15
862
|
|
|
16
863
|
/**
|
|
17
864
|
* Judge sentinel — eval trustworthiness as a continuously measured,
|
|
@@ -180,4 +1027,4 @@ interface EvalHealthStamp {
|
|
|
180
1027
|
*/
|
|
181
1028
|
declare function evalHealthStamp(report: SentinelReport): EvalHealthStamp;
|
|
182
1029
|
|
|
183
|
-
export { type EvalHealthStamp, type JudgeSentinelOptions, SENTINEL_METRIC_NAMES, type SentinelMetricName, type SentinelMetrics, type SentinelReport, type SentinelSetOptions, type SentinelSnapshot, type SentinelStore, type SentinelThresholds, type SentinelTrend, type SnapshotMeta, evalHealthStamp, fileSentinelStore, inMemorySentinelStore, judgeSentinelReport, snapshotFromAgreement, snapshotFromCalibration, snapshotFromSentinelSet, validateSentinelSnapshot };
|
|
1030
|
+
export { type CalibrationBin, type CalibrationOptions, type CalibrationPair, type CalibrationReport, type CorrelationResult, type CorrelationStudyOptions, type CorrelationStudyResult, type DeploymentOutcome, type EvalHealthStamp, type EvalMetricSpec, FileSystemOutcomeStore, type FileSystemOutcomeStoreOptions, InMemoryOutcomeStore, type JudgeSentinelOptions, type OutcomeFilter, type OutcomePair, type OutcomeStore, type RubricOutcomePair, type RubricPredictiveValidityInput, type RubricPredictiveValidityReport, type RubricRanking, SENTINEL_METRIC_NAMES, type SentinelMetricName, type SentinelMetrics, type SentinelReport, type SentinelSetOptions, type SentinelSnapshot, type SentinelStore, type SentinelThresholds, type SentinelTrend, type SnapshotMeta, calibrationCurve, calibrationFromPairs, correlationStudy, evalHealthStamp, fileSentinelStore, inMemorySentinelStore, judgeSentinelReport, rubricPredictiveValidity, snapshotFromAgreement, snapshotFromCalibration, snapshotFromSentinelSet, validateSentinelSnapshot };
|