@warmdrift/kgauto-compiler 2.0.0-alpha.61 → 2.0.0-alpha.62
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 +30 -12
- package/dist/brain-proxy.js +5 -1
- package/dist/brain-proxy.mjs +1 -1
- package/dist/{chunk-5TI6PNSK.mjs → chunk-3KQAID63.mjs} +5 -0
- package/dist/{chunk-IUWFML6Z.mjs → chunk-65ZMX5OT.mjs} +5 -1
- package/dist/{chunk-3RMLZCUK.mjs → chunk-ABFXMO73.mjs} +1 -1
- package/dist/{chunk-IIMPJZNH.mjs → chunk-YZRPNSSQ.mjs} +10 -0
- package/dist/dialect.d.mts +14 -2
- package/dist/dialect.d.ts +14 -2
- package/dist/dialect.js +5 -0
- package/dist/dialect.mjs +1 -1
- package/dist/glassbox/index.d.mts +3 -3
- package/dist/glassbox/index.d.ts +3 -3
- package/dist/glassbox-routes/format.d.mts +2 -2
- package/dist/glassbox-routes/format.d.ts +2 -2
- package/dist/glassbox-routes/index.d.mts +4 -4
- package/dist/glassbox-routes/index.d.ts +4 -4
- package/dist/glassbox-routes/index.js +10 -0
- package/dist/glassbox-routes/index.mjs +1 -1
- package/dist/glassbox-routes/react/index.d.mts +2 -2
- package/dist/glassbox-routes/react/index.d.ts +2 -2
- package/dist/index.d.mts +193 -6
- package/dist/index.d.ts +193 -6
- package/dist/index.js +681 -2
- package/dist/index.mjs +651 -4
- package/dist/{ir-DAKlQsVb.d.mts → ir-B2dRyJXO.d.mts} +74 -9
- package/dist/{ir-DmUuJsWc.d.ts → ir-ClU56aBc.d.ts} +74 -9
- package/dist/key-health.js +1 -1
- package/dist/key-health.mjs +1 -1
- package/dist/profiles.d.mts +1 -1
- package/dist/profiles.d.ts +1 -1
- package/dist/{types-B8X1Pyhx.d.ts → types-ByN3r0_7.d.ts} +1 -1
- package/dist/{types-DR62iPcO.d.ts → types-C-Wp7HpI.d.ts} +1 -1
- package/dist/{types-MRMBUqzY.d.mts → types-CSDweZsl.d.mts} +1 -1
- package/dist/{types-CssWqd0X.d.mts → types-axAX1Bg0.d.mts} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ __export(index_exports, {
|
|
|
30
30
|
DIALECT_VERSION: () => DIALECT_VERSION,
|
|
31
31
|
FamilyResolutionError: () => FamilyResolutionError,
|
|
32
32
|
INTENT_ARCHETYPES: () => INTENT_ARCHETYPES,
|
|
33
|
+
JUDGE_RUBRICS: () => JUDGE_RUBRICS,
|
|
33
34
|
LATENCY_TIER_MS: () => LATENCY_TIER_MS,
|
|
34
35
|
LIBRARY_VERSION: () => LIBRARY_VERSION,
|
|
35
36
|
MEASURED_GROUNDING_MIN_N: () => MEASURED_GROUNDING_MIN_N,
|
|
@@ -45,10 +46,14 @@ __export(index_exports, {
|
|
|
45
46
|
bucketContext: () => bucketContext,
|
|
46
47
|
bucketHistory: () => bucketHistory,
|
|
47
48
|
bucketToolCount: () => bucketToolCount,
|
|
49
|
+
buildGoldenIrRow: () => buildGoldenIrRow,
|
|
48
50
|
buildLLMJudge: () => buildLLMJudge,
|
|
51
|
+
buildPairwiseJudgePrompt: () => buildPairwiseJudgePrompt,
|
|
49
52
|
buildShadowProbeRow: () => buildShadowProbeRow,
|
|
50
53
|
call: () => call,
|
|
54
|
+
captureGoldenIr: () => captureGoldenIr,
|
|
51
55
|
clearBrain: () => clearBrain,
|
|
56
|
+
combineOrderSwappedVerdicts: () => combineOrderSwappedVerdicts,
|
|
52
57
|
compile: () => compile2,
|
|
53
58
|
compileForAISDKv6: () => compileForAISDKv6,
|
|
54
59
|
configureBrain: () => configureBrain,
|
|
@@ -94,20 +99,28 @@ __export(index_exports, {
|
|
|
94
99
|
markAdvisoryResolved: () => markAdvisoryResolved,
|
|
95
100
|
markExclusionFindingHandled: () => markExclusionFindingHandled,
|
|
96
101
|
markPromoteReadyHandled: () => markPromoteReadyHandled,
|
|
102
|
+
parseGoldenCaptureRate: () => parseGoldenCaptureRate,
|
|
103
|
+
parseJudgeVerdict: () => parseJudgeVerdict,
|
|
97
104
|
peekBrainDeadLetter: () => peekBrainDeadLetter,
|
|
98
105
|
probeShadow: () => probeShadow,
|
|
99
106
|
profileToRow: () => profileToRow,
|
|
100
107
|
profilesByProvider: () => profilesByProvider,
|
|
101
108
|
readBrainReadEnv: () => readBrainReadEnv,
|
|
102
109
|
record: () => record,
|
|
110
|
+
recordGoldenIr: () => recordGoldenIr,
|
|
103
111
|
recordOutcome: () => recordOutcome,
|
|
104
112
|
recordShadowProbe: () => recordShadowProbe,
|
|
113
|
+
renderIrForJudge: () => renderIrForJudge,
|
|
105
114
|
resetTokenizer: () => resetTokenizer,
|
|
106
115
|
resolveConventionsForProfile: () => resolveConventionsForProfile,
|
|
116
|
+
resolveGoldenCaptureRate: () => resolveGoldenCaptureRate,
|
|
107
117
|
resolvePricingAt: () => resolvePricingAt,
|
|
108
118
|
resolveProviderKey: () => resolveProviderKey,
|
|
119
|
+
rubricFor: () => rubricFor,
|
|
109
120
|
runAdvisor: () => runAdvisor,
|
|
121
|
+
runGoldenEval: () => runGoldenEval,
|
|
110
122
|
setTokenizer: () => setTokenizer,
|
|
123
|
+
shouldCaptureGolden: () => shouldCaptureGolden,
|
|
111
124
|
tryGetProfile: () => tryGetProfile
|
|
112
125
|
});
|
|
113
126
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -2295,6 +2308,11 @@ var INTENT_ARCHETYPES = {
|
|
|
2295
2308
|
name: "transform",
|
|
2296
2309
|
description: "Change format or style while preserving content",
|
|
2297
2310
|
examples: ["markdown \u2192 html", "formal \u2192 casual", "translate"]
|
|
2311
|
+
},
|
|
2312
|
+
judge: {
|
|
2313
|
+
name: "judge",
|
|
2314
|
+
description: "Pairwise comparison of two candidate outputs for the same input",
|
|
2315
|
+
examples: ["golden-set eval A/B verdict", "model-swap non-inferiority check", "response bake-off"]
|
|
2298
2316
|
}
|
|
2299
2317
|
};
|
|
2300
2318
|
var ALL_ARCHETYPES = Object.keys(INTENT_ARCHETYPES);
|
|
@@ -5005,6 +5023,61 @@ async function recordShadowProbe(input) {
|
|
|
5005
5023
|
void send();
|
|
5006
5024
|
}
|
|
5007
5025
|
}
|
|
5026
|
+
function peekRegisteredShapeKey(handle) {
|
|
5027
|
+
return compileRegistry.get(handle)?.shapeKey;
|
|
5028
|
+
}
|
|
5029
|
+
function buildGoldenIrRow(input) {
|
|
5030
|
+
return {
|
|
5031
|
+
app_id: input.appId,
|
|
5032
|
+
intent_archetype: input.archetype,
|
|
5033
|
+
shape_key: input.shapeKey ?? null,
|
|
5034
|
+
ir: input.ir,
|
|
5035
|
+
incumbent_model: input.incumbentModel,
|
|
5036
|
+
incumbent_output: input.incumbentOutput ?? null,
|
|
5037
|
+
incumbent_latency_ms: input.incumbentLatencyMs ?? null,
|
|
5038
|
+
incumbent_tokens_in: input.incumbentTokensIn ?? null,
|
|
5039
|
+
incumbent_tokens_out: input.incumbentTokensOut ?? null,
|
|
5040
|
+
source: input.source,
|
|
5041
|
+
consent: input.consent,
|
|
5042
|
+
outcome_handle: input.outcomeHandle ?? null
|
|
5043
|
+
};
|
|
5044
|
+
}
|
|
5045
|
+
async function recordGoldenIr(input) {
|
|
5046
|
+
if (!activeConfig) return;
|
|
5047
|
+
const config = activeConfig;
|
|
5048
|
+
const fetchFn = brainWriteFetch(config);
|
|
5049
|
+
const row = buildGoldenIrRow(input);
|
|
5050
|
+
const send = async () => {
|
|
5051
|
+
noteSent();
|
|
5052
|
+
try {
|
|
5053
|
+
const res = await fetchFn(`${config.endpoint}/golden_irs`, {
|
|
5054
|
+
method: "POST",
|
|
5055
|
+
headers: {
|
|
5056
|
+
"Content-Type": "application/json",
|
|
5057
|
+
Prefer: "return=minimal",
|
|
5058
|
+
...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
|
|
5059
|
+
},
|
|
5060
|
+
body: JSON.stringify(row)
|
|
5061
|
+
});
|
|
5062
|
+
assertNotRedirected(res, "golden_irs");
|
|
5063
|
+
if (!res.ok) {
|
|
5064
|
+
const text = await res.text().catch(() => "<no body>");
|
|
5065
|
+
throw new Error(describeBrainWriteFailure(res.status, "golden_irs", text));
|
|
5066
|
+
}
|
|
5067
|
+
noteAck();
|
|
5068
|
+
maybeAutoFlush();
|
|
5069
|
+
} catch (err) {
|
|
5070
|
+
noteFailure(err);
|
|
5071
|
+
pushDeadLetter("golden_irs", config.endpoint, row, err);
|
|
5072
|
+
(config.onError ?? defaultOnError5)(err);
|
|
5073
|
+
}
|
|
5074
|
+
};
|
|
5075
|
+
if (config.sync) {
|
|
5076
|
+
await send();
|
|
5077
|
+
} else {
|
|
5078
|
+
void send();
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5008
5081
|
|
|
5009
5082
|
// src/ir.ts
|
|
5010
5083
|
var CallError = class extends Error {
|
|
@@ -5020,6 +5093,45 @@ var CallError = class extends Error {
|
|
|
5020
5093
|
}
|
|
5021
5094
|
};
|
|
5022
5095
|
|
|
5096
|
+
// src/golden.ts
|
|
5097
|
+
function parseGoldenCaptureRate(raw) {
|
|
5098
|
+
if (raw === void 0 || raw.trim() === "") return 0;
|
|
5099
|
+
const n = Number(raw);
|
|
5100
|
+
if (!Number.isFinite(n)) return 0;
|
|
5101
|
+
return Math.max(0, Math.min(1, n));
|
|
5102
|
+
}
|
|
5103
|
+
function resolveGoldenCaptureRate(optRate) {
|
|
5104
|
+
if (optRate !== void 0 && Number.isFinite(optRate)) {
|
|
5105
|
+
return Math.max(0, Math.min(1, optRate));
|
|
5106
|
+
}
|
|
5107
|
+
const env = typeof process !== "undefined" && process.env ? process.env.KGAUTO_GOLDEN_CAPTURE : void 0;
|
|
5108
|
+
return parseGoldenCaptureRate(env);
|
|
5109
|
+
}
|
|
5110
|
+
function shouldCaptureGolden(rate, rng = Math.random) {
|
|
5111
|
+
if (rate <= 0) return false;
|
|
5112
|
+
if (rate >= 1) return true;
|
|
5113
|
+
return rng() < rate;
|
|
5114
|
+
}
|
|
5115
|
+
async function captureGoldenIr(ctx) {
|
|
5116
|
+
try {
|
|
5117
|
+
await recordGoldenIr({
|
|
5118
|
+
appId: ctx.ir.appId,
|
|
5119
|
+
archetype: ctx.ir.intent.archetype,
|
|
5120
|
+
shapeKey: ctx.shapeKey,
|
|
5121
|
+
ir: ctx.ir,
|
|
5122
|
+
incumbentModel: ctx.servedModel,
|
|
5123
|
+
incumbentOutput: ctx.response.text,
|
|
5124
|
+
incumbentLatencyMs: ctx.latencyMs,
|
|
5125
|
+
incumbentTokensIn: ctx.response.tokens.input,
|
|
5126
|
+
incumbentTokensOut: ctx.response.tokens.output,
|
|
5127
|
+
source: "sampled",
|
|
5128
|
+
consent: ctx.consent,
|
|
5129
|
+
outcomeHandle: ctx.handle
|
|
5130
|
+
});
|
|
5131
|
+
} catch {
|
|
5132
|
+
}
|
|
5133
|
+
}
|
|
5134
|
+
|
|
5023
5135
|
// src/env.ts
|
|
5024
5136
|
var SUPPORTED_PROVIDERS = Object.freeze([
|
|
5025
5137
|
"anthropic",
|
|
@@ -5654,6 +5766,16 @@ var STARTER_CHAINS_GROUNDED = {
|
|
|
5654
5766
|
{ id: "gemini-2.5-pro", grounding: "judgment", reason: "Cross-provider anchor in similar quality bracket" },
|
|
5655
5767
|
{ id: "gpt-5.5", grounding: "judgment", reason: "alpha.16: third-provider frontier-tier floor (archetypePerf=9)" }
|
|
5656
5768
|
],
|
|
5769
|
+
// alpha.62 (eval spine) — pairwise output comparison. Same reasoning-floor
|
|
5770
|
+
// posture as critique (a verdict from a weak judge is worse than no
|
|
5771
|
+
// verdict); tier 0 anchored on the CURRENT opus (4-8, s64 roster). Strict
|
|
5772
|
+
// JSON output, so every tier declares native structured output.
|
|
5773
|
+
judge: [
|
|
5774
|
+
{ id: "claude-opus-4-8", grounding: "judgment", reason: "Highest reasoning bar for pairwise verdicts \u2014 current opus (s64 roster)" },
|
|
5775
|
+
{ id: "claude-sonnet-4-6", grounding: "judgment", reason: "Same-provider walk-down on 429" },
|
|
5776
|
+
{ id: "gemini-2.5-pro", grounding: "judgment", reason: "Cross-provider anchor in similar quality bracket" },
|
|
5777
|
+
{ id: "gpt-5.5", grounding: "judgment", reason: "Third-provider frontier-tier floor" }
|
|
5778
|
+
],
|
|
5657
5779
|
// Reasoning matters — Sonnet primary; walk UP to Opus on 429.
|
|
5658
5780
|
plan: [
|
|
5659
5781
|
{ id: "claude-sonnet-4-6", grounding: "judgment", reason: "Reasoning + cost balance \u2014 engineer pick" },
|
|
@@ -6446,6 +6568,8 @@ async function call(ir, opts = {}) {
|
|
|
6446
6568
|
);
|
|
6447
6569
|
const fellOver = targetModel !== initial.target;
|
|
6448
6570
|
const fallbackReason = fellOver ? normalizeFallbackReason(attempts) : void 0;
|
|
6571
|
+
const goldenRate = resolveGoldenCaptureRate(opts.goldenCapture?.sampleRate);
|
|
6572
|
+
const goldenShapeKey = goldenRate > 0 ? peekRegisteredShapeKey(initial.handle) : void 0;
|
|
6449
6573
|
await record({
|
|
6450
6574
|
handle: initial.handle,
|
|
6451
6575
|
tokensIn: validated.response.tokens.input,
|
|
@@ -6498,6 +6622,23 @@ async function call(ir, opts = {}) {
|
|
|
6498
6622
|
void probe;
|
|
6499
6623
|
}
|
|
6500
6624
|
}
|
|
6625
|
+
if (goldenRate > 0 && validated.response.tokens.output > 0 && shouldCaptureGolden(goldenRate)) {
|
|
6626
|
+
const consent = opts.goldenCapture?.sampleRate !== void 0 ? `CallOptions.goldenCapture.sampleRate=${goldenRate} (consumer code opt-in)` + (opts.goldenCapture.consentNote ? ` \u2014 ${opts.goldenCapture.consentNote}` : "") : `KGAUTO_GOLDEN_CAPTURE=${goldenRate} (consumer env opt-in)`;
|
|
6627
|
+
const capture = captureGoldenIr({
|
|
6628
|
+
ir,
|
|
6629
|
+
servedModel: targetModel,
|
|
6630
|
+
response: validated.response,
|
|
6631
|
+
latencyMs: latencyMs2,
|
|
6632
|
+
handle: initial.handle,
|
|
6633
|
+
shapeKey: goldenShapeKey,
|
|
6634
|
+
consent
|
|
6635
|
+
});
|
|
6636
|
+
if (isBrainSync()) {
|
|
6637
|
+
await capture;
|
|
6638
|
+
} else {
|
|
6639
|
+
void capture;
|
|
6640
|
+
}
|
|
6641
|
+
}
|
|
6501
6642
|
return {
|
|
6502
6643
|
handle: initial.handle,
|
|
6503
6644
|
actualModel: targetModel,
|
|
@@ -6837,6 +6978,18 @@ function safeEmit(fn) {
|
|
|
6837
6978
|
} catch {
|
|
6838
6979
|
}
|
|
6839
6980
|
}
|
|
6981
|
+
var _internal = {
|
|
6982
|
+
compileAndRegister,
|
|
6983
|
+
validateStructuredContract,
|
|
6984
|
+
extractPromptPreview,
|
|
6985
|
+
normalizeFallbackReason,
|
|
6986
|
+
generateTraceId,
|
|
6987
|
+
safeEmit,
|
|
6988
|
+
shouldSampleProbe,
|
|
6989
|
+
normalizeProbeCandidates,
|
|
6990
|
+
runShadowProbe,
|
|
6991
|
+
runProbeCandidates
|
|
6992
|
+
};
|
|
6840
6993
|
|
|
6841
6994
|
// src/streamtext-helpers.ts
|
|
6842
6995
|
function attachCacheControlToStreamTextInput(result, convertedMessages) {
|
|
@@ -6936,12 +7089,525 @@ function extractKeptToolNames(result) {
|
|
|
6936
7089
|
return names;
|
|
6937
7090
|
}
|
|
6938
7091
|
|
|
7092
|
+
// src/golden-eval.ts
|
|
7093
|
+
var GENERIC_RUBRIC = "overall correctness, completeness against the request, clarity, and adherence to any requested format";
|
|
7094
|
+
var JUDGE_RUBRICS = {
|
|
7095
|
+
summarize: "faithfulness to the source (no fabricated facts), coverage of the key points, concision, and adherence to the requested output format",
|
|
7096
|
+
classify: "assignment of the correct category from the allowed set, and nothing outside the allowed set",
|
|
7097
|
+
generate: "relevance to the brief, quality and coherence of the writing, and adherence to the requested format and tone",
|
|
7098
|
+
extract: "completeness of extraction (nothing relevant missed), precision (no invented fields or values), and exact schema adherence",
|
|
7099
|
+
ask: "correctness of the answer with respect to the provided data, relevance, and absence of fabrication",
|
|
7100
|
+
hunt: "relevance and novelty of the discovered entities, and absence of duplicates or fabricated entries",
|
|
7101
|
+
plan: "quality of the decomposition (complete, ordered, actionable steps) and realism of the sequencing",
|
|
7102
|
+
critique: "specificity and correctness of the assessment, and actionability of the feedback",
|
|
7103
|
+
transform: "preservation of the source content and correctness of the target format or style"
|
|
7104
|
+
};
|
|
7105
|
+
function rubricFor(archetype) {
|
|
7106
|
+
return JUDGE_RUBRICS[archetype] ?? GENERIC_RUBRIC;
|
|
7107
|
+
}
|
|
7108
|
+
var INPUT_TRUNCATE_CHARS = 24e3;
|
|
7109
|
+
var OUTPUT_TRUNCATE_CHARS = 12e3;
|
|
7110
|
+
function renderIrForJudge(ir) {
|
|
7111
|
+
const sections = (ir.sections ?? []).map((s) => s.text).filter(Boolean).join("\n\n");
|
|
7112
|
+
const turn = typeof ir.currentTurn?.content === "string" ? ir.currentTurn.content : "";
|
|
7113
|
+
const combined = [
|
|
7114
|
+
sections ? `[system]
|
|
7115
|
+
${sections}` : "",
|
|
7116
|
+
turn ? `[user]
|
|
7117
|
+
${turn}` : ""
|
|
7118
|
+
].filter(Boolean).join("\n\n");
|
|
7119
|
+
return combined.length > INPUT_TRUNCATE_CHARS ? `${combined.slice(0, INPUT_TRUNCATE_CHARS)}
|
|
7120
|
+
\u2026[truncated]` : combined;
|
|
7121
|
+
}
|
|
7122
|
+
function buildPairwiseJudgePrompt(args) {
|
|
7123
|
+
const clip = (s) => s.length > OUTPUT_TRUNCATE_CHARS ? `${s.slice(0, OUTPUT_TRUNCATE_CHARS)}
|
|
7124
|
+
\u2026[truncated]` : s;
|
|
7125
|
+
return `You are a strict pairwise judge comparing two AI responses to the SAME request.
|
|
7126
|
+
|
|
7127
|
+
The request (archetype: ${args.archetype}):
|
|
7128
|
+
<request>
|
|
7129
|
+
${args.renderedInput}
|
|
7130
|
+
</request>
|
|
7131
|
+
|
|
7132
|
+
Response A:
|
|
7133
|
+
<response_a>
|
|
7134
|
+
${clip(args.outputA)}
|
|
7135
|
+
</response_a>
|
|
7136
|
+
|
|
7137
|
+
Response B:
|
|
7138
|
+
<response_b>
|
|
7139
|
+
${clip(args.outputB)}
|
|
7140
|
+
</response_b>
|
|
7141
|
+
|
|
7142
|
+
Judge which response better satisfies the request. Rubric for ${args.archetype}: ${rubricFor(args.archetype)}.
|
|
7143
|
+
|
|
7144
|
+
A tie is a legitimate verdict \u2014 declare it when the responses are of genuinely comparable quality. Do NOT prefer a response merely for being longer.
|
|
7145
|
+
|
|
7146
|
+
Output ONLY this JSON, no other text:
|
|
7147
|
+
{"winner": "A" | "B" | "tie", "rationale": "<one sentence>"}`;
|
|
7148
|
+
}
|
|
7149
|
+
function parseJudgeVerdict(raw) {
|
|
7150
|
+
const cleaned = raw.replace(/^```(?:json)?\s*/i, "").replace(/\s*```\s*$/, "").trim();
|
|
7151
|
+
let parsed;
|
|
7152
|
+
try {
|
|
7153
|
+
parsed = JSON.parse(cleaned);
|
|
7154
|
+
} catch {
|
|
7155
|
+
const match = cleaned.match(/\{[\s\S]*\}/);
|
|
7156
|
+
if (!match) return void 0;
|
|
7157
|
+
try {
|
|
7158
|
+
parsed = JSON.parse(match[0]);
|
|
7159
|
+
} catch {
|
|
7160
|
+
return void 0;
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
7164
|
+
const obj = parsed;
|
|
7165
|
+
const winner = obj.winner;
|
|
7166
|
+
if (winner !== "A" && winner !== "B" && winner !== "tie") return void 0;
|
|
7167
|
+
return {
|
|
7168
|
+
winner,
|
|
7169
|
+
rationale: typeof obj.rationale === "string" ? obj.rationale : void 0
|
|
7170
|
+
};
|
|
7171
|
+
}
|
|
7172
|
+
function combineOrderSwappedVerdicts(run1, run2) {
|
|
7173
|
+
if (run1 === run2) return run1;
|
|
7174
|
+
return "tied";
|
|
7175
|
+
}
|
|
7176
|
+
function p50(values) {
|
|
7177
|
+
if (values.length === 0) return null;
|
|
7178
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
7179
|
+
return sorted[Math.floor(sorted.length / 2)] ?? null;
|
|
7180
|
+
}
|
|
7181
|
+
function costUsd(model, tokensIn, tokensOut) {
|
|
7182
|
+
const profile = tryGetProfile(model);
|
|
7183
|
+
if (!profile) return null;
|
|
7184
|
+
return tokensIn / 1e6 * profile.costInputPer1m + tokensOut / 1e6 * profile.costOutputPer1m;
|
|
7185
|
+
}
|
|
7186
|
+
function hashForGolden(s) {
|
|
7187
|
+
let h = 5381;
|
|
7188
|
+
for (let i = 0; i < s.length; i += 1) {
|
|
7189
|
+
h = (h << 5) + h + s.charCodeAt(i) >>> 0;
|
|
7190
|
+
}
|
|
7191
|
+
return `g${h.toString(36)}`;
|
|
7192
|
+
}
|
|
7193
|
+
async function runGoldenEval(opts) {
|
|
7194
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
7195
|
+
const progress = opts.onProgress ?? (() => {
|
|
7196
|
+
});
|
|
7197
|
+
const limit = opts.limit ?? 50;
|
|
7198
|
+
const threshold = opts.winOrTieThreshold ?? 0.8;
|
|
7199
|
+
const latencyFloorRatio = opts.latencyFloorRatio ?? 3;
|
|
7200
|
+
const minJudgeable = opts.minJudgeableCases ?? 5;
|
|
7201
|
+
const judgeModel = opts.judgeModel ?? "claude-opus-4-8";
|
|
7202
|
+
const notes = [];
|
|
7203
|
+
const restHeaders = {
|
|
7204
|
+
apikey: opts.serviceKey,
|
|
7205
|
+
Authorization: `Bearer ${opts.serviceKey}`
|
|
7206
|
+
};
|
|
7207
|
+
const rest = (path) => `${opts.supabaseUrl}/rest/v1/${path}`;
|
|
7208
|
+
const setUrl = rest(
|
|
7209
|
+
`kgauto_golden_irs?app_id=eq.${encodeURIComponent(opts.appId)}&intent_archetype=eq.${encodeURIComponent(opts.archetype)}&active=is.true&select=id,app_id,intent_archetype,ir,incumbent_model&order=captured_at.desc&limit=${limit}`
|
|
7210
|
+
);
|
|
7211
|
+
const setRes = await fetchFn(setUrl, { headers: restHeaders });
|
|
7212
|
+
if (!setRes.ok) {
|
|
7213
|
+
throw new Error(
|
|
7214
|
+
`golden-eval: failed to load golden set (${setRes.status}): ${await setRes.text().catch(() => "<no body>")}`
|
|
7215
|
+
);
|
|
7216
|
+
}
|
|
7217
|
+
const goldenRows = await setRes.json();
|
|
7218
|
+
if (goldenRows.length === 0) {
|
|
7219
|
+
throw new Error(
|
|
7220
|
+
`golden-eval: no active golden IRs for (${opts.appId}, ${opts.archetype}) \u2014 seed via KGAUTO_GOLDEN_CAPTURE or scripts/seed-golden-set.mjs first.`
|
|
7221
|
+
);
|
|
7222
|
+
}
|
|
7223
|
+
progress(`Loaded ${goldenRows.length} golden case(s).`);
|
|
7224
|
+
let incumbentModel = opts.incumbentModel;
|
|
7225
|
+
if (!incumbentModel) {
|
|
7226
|
+
const counts = /* @__PURE__ */ new Map();
|
|
7227
|
+
for (const row of goldenRows) {
|
|
7228
|
+
counts.set(row.incumbent_model, (counts.get(row.incumbent_model) ?? 0) + 1);
|
|
7229
|
+
}
|
|
7230
|
+
const top = [...counts.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
7231
|
+
if (!top) throw new Error("golden-eval: could not resolve an incumbent from the golden set.");
|
|
7232
|
+
incumbentModel = top[0];
|
|
7233
|
+
notes.push(`incumbent defaulted to most-captured model: ${incumbentModel}`);
|
|
7234
|
+
}
|
|
7235
|
+
if (incumbentModel === opts.candidateModel) {
|
|
7236
|
+
throw new Error("golden-eval: candidate and incumbent are the same model.");
|
|
7237
|
+
}
|
|
7238
|
+
const judgeProfile = tryGetProfile(judgeModel);
|
|
7239
|
+
if (!judgeProfile) {
|
|
7240
|
+
throw new Error(`golden-eval: judge model '${judgeModel}' is not in the roster.`);
|
|
7241
|
+
}
|
|
7242
|
+
if (judgeProfile.family && [incumbentModel, opts.candidateModel].some(
|
|
7243
|
+
(m) => tryGetProfile(m)?.family === judgeProfile.family
|
|
7244
|
+
)) {
|
|
7245
|
+
notes.push(
|
|
7246
|
+
`WARNING: judge family (${judgeProfile.family}) overlaps a compared model \u2014 self-preference risk; consider --judge from another provider.`
|
|
7247
|
+
);
|
|
7248
|
+
}
|
|
7249
|
+
const replay = async (ir, model) => {
|
|
7250
|
+
try {
|
|
7251
|
+
const evalIr = {
|
|
7252
|
+
...ir,
|
|
7253
|
+
models: [model],
|
|
7254
|
+
constraints: { ...ir.constraints ?? {}, forceModel: model }
|
|
7255
|
+
};
|
|
7256
|
+
const compiled = compile(evalIr);
|
|
7257
|
+
const attempt = () => execute(compiled.request, { apiKeys: opts.apiKeys, fetchImpl: opts.fetchImpl });
|
|
7258
|
+
let started = Date.now();
|
|
7259
|
+
let exec = await attempt();
|
|
7260
|
+
if (!exec.ok && exec.errorType === "retryable") {
|
|
7261
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
7262
|
+
started = Date.now();
|
|
7263
|
+
exec = await attempt();
|
|
7264
|
+
}
|
|
7265
|
+
const latencyMs = Date.now() - started;
|
|
7266
|
+
if (!exec.ok) {
|
|
7267
|
+
return {
|
|
7268
|
+
ok: false,
|
|
7269
|
+
text: "",
|
|
7270
|
+
structuredOutput: null,
|
|
7271
|
+
tokensIn: 0,
|
|
7272
|
+
tokensOut: 0,
|
|
7273
|
+
latencyMs,
|
|
7274
|
+
errorClass: exec.errorCode
|
|
7275
|
+
};
|
|
7276
|
+
}
|
|
7277
|
+
const validated = _internal.validateStructuredContract(exec, evalIr);
|
|
7278
|
+
if (!validated.ok) {
|
|
7279
|
+
return {
|
|
7280
|
+
ok: true,
|
|
7281
|
+
text: exec.response.text,
|
|
7282
|
+
structuredOutput: null,
|
|
7283
|
+
tokensIn: exec.response.tokens.input,
|
|
7284
|
+
tokensOut: exec.response.tokens.output,
|
|
7285
|
+
latencyMs,
|
|
7286
|
+
contractViolation: validated.errorCode
|
|
7287
|
+
};
|
|
7288
|
+
}
|
|
7289
|
+
return {
|
|
7290
|
+
ok: true,
|
|
7291
|
+
text: validated.response.text,
|
|
7292
|
+
structuredOutput: validated.response.structuredOutput,
|
|
7293
|
+
parseError: validated.response.parseError,
|
|
7294
|
+
tokensIn: validated.response.tokens.input,
|
|
7295
|
+
tokensOut: validated.response.tokens.output,
|
|
7296
|
+
latencyMs
|
|
7297
|
+
};
|
|
7298
|
+
} catch (err) {
|
|
7299
|
+
return {
|
|
7300
|
+
ok: false,
|
|
7301
|
+
text: "",
|
|
7302
|
+
structuredOutput: null,
|
|
7303
|
+
tokensIn: 0,
|
|
7304
|
+
tokensOut: 0,
|
|
7305
|
+
latencyMs: 0,
|
|
7306
|
+
errorClass: err instanceof Error ? `execute_rejected:${err.message}` : "execute_rejected"
|
|
7307
|
+
};
|
|
7308
|
+
}
|
|
7309
|
+
};
|
|
7310
|
+
const judgeOnce = async (ir, outputA, outputB) => {
|
|
7311
|
+
const judgeIr = {
|
|
7312
|
+
appId: opts.appId,
|
|
7313
|
+
intent: { name: "golden-eval-judge", archetype: "judge" },
|
|
7314
|
+
sections: [
|
|
7315
|
+
{
|
|
7316
|
+
id: "role",
|
|
7317
|
+
text: "You are a strict, impartial pairwise judge. You output only JSON.",
|
|
7318
|
+
weight: 10
|
|
7319
|
+
}
|
|
7320
|
+
],
|
|
7321
|
+
currentTurn: {
|
|
7322
|
+
role: "user",
|
|
7323
|
+
content: buildPairwiseJudgePrompt({
|
|
7324
|
+
archetype: opts.archetype,
|
|
7325
|
+
renderedInput: renderIrForJudge(ir),
|
|
7326
|
+
outputA,
|
|
7327
|
+
outputB
|
|
7328
|
+
})
|
|
7329
|
+
},
|
|
7330
|
+
models: [judgeModel],
|
|
7331
|
+
constraints: { forceModel: judgeModel }
|
|
7332
|
+
};
|
|
7333
|
+
try {
|
|
7334
|
+
const compiled = compile(judgeIr);
|
|
7335
|
+
const exec = await execute(compiled.request, {
|
|
7336
|
+
apiKeys: opts.apiKeys,
|
|
7337
|
+
fetchImpl: opts.fetchImpl
|
|
7338
|
+
});
|
|
7339
|
+
if (!exec.ok) return void 0;
|
|
7340
|
+
return parseJudgeVerdict(exec.response.text);
|
|
7341
|
+
} catch {
|
|
7342
|
+
return void 0;
|
|
7343
|
+
}
|
|
7344
|
+
};
|
|
7345
|
+
const cases = [];
|
|
7346
|
+
const floorDetail = {};
|
|
7347
|
+
const bumpFloor = (k) => {
|
|
7348
|
+
floorDetail[k] = (floorDetail[k] ?? 0) + 1;
|
|
7349
|
+
};
|
|
7350
|
+
for (const [i, row] of goldenRows.entries()) {
|
|
7351
|
+
progress(
|
|
7352
|
+
`Case ${i + 1}/${goldenRows.length} (golden #${row.id}): replaying ${incumbentModel} + ${opts.candidateModel}\u2026`
|
|
7353
|
+
);
|
|
7354
|
+
const [inc, cand] = await Promise.all([
|
|
7355
|
+
replay(row.ir, incumbentModel),
|
|
7356
|
+
replay(row.ir, opts.candidateModel)
|
|
7357
|
+
]);
|
|
7358
|
+
if (!inc.ok || inc.contractViolation) {
|
|
7359
|
+
const why = !inc.ok ? `incumbent replay failed (${inc.errorClass ?? "unknown"})` : `incumbent contract violation (${inc.contractViolation})`;
|
|
7360
|
+
cases.push({
|
|
7361
|
+
goldenIrId: row.id,
|
|
7362
|
+
verdict: "inconclusive",
|
|
7363
|
+
floorViolations: [],
|
|
7364
|
+
excludedReason: why
|
|
7365
|
+
});
|
|
7366
|
+
notes.push(`case #${row.id}: ${why} \u2014 excluded`);
|
|
7367
|
+
continue;
|
|
7368
|
+
}
|
|
7369
|
+
const incumbentLeg = {
|
|
7370
|
+
latencyMs: inc.latencyMs,
|
|
7371
|
+
tokensIn: inc.tokensIn,
|
|
7372
|
+
tokensOut: inc.tokensOut,
|
|
7373
|
+
text: inc.text
|
|
7374
|
+
};
|
|
7375
|
+
const violations = [];
|
|
7376
|
+
if (!cand.ok) {
|
|
7377
|
+
violations.push("candidate_error");
|
|
7378
|
+
bumpFloor("candidate_error");
|
|
7379
|
+
} else if (cand.tokensOut === 0 || cand.text.trim() === "" && !cand.structuredOutput) {
|
|
7380
|
+
violations.push("empty");
|
|
7381
|
+
bumpFloor("empty");
|
|
7382
|
+
} else if (cand.contractViolation) {
|
|
7383
|
+
violations.push("schema");
|
|
7384
|
+
bumpFloor("schema");
|
|
7385
|
+
}
|
|
7386
|
+
if (violations.length > 0) {
|
|
7387
|
+
cases.push({
|
|
7388
|
+
goldenIrId: row.id,
|
|
7389
|
+
verdict: "current-better",
|
|
7390
|
+
floorViolations: violations,
|
|
7391
|
+
errorClass: cand.errorClass ?? cand.contractViolation,
|
|
7392
|
+
incumbent: incumbentLeg,
|
|
7393
|
+
candidate: cand.ok ? { latencyMs: cand.latencyMs, tokensIn: cand.tokensIn, tokensOut: cand.tokensOut, text: cand.text } : void 0
|
|
7394
|
+
});
|
|
7395
|
+
continue;
|
|
7396
|
+
}
|
|
7397
|
+
const [order1, order2] = await Promise.all([
|
|
7398
|
+
judgeOnce(row.ir, inc.text, cand.text),
|
|
7399
|
+
judgeOnce(row.ir, cand.text, inc.text)
|
|
7400
|
+
]);
|
|
7401
|
+
if (!order1 || !order2) {
|
|
7402
|
+
cases.push({
|
|
7403
|
+
goldenIrId: row.id,
|
|
7404
|
+
verdict: "inconclusive",
|
|
7405
|
+
floorViolations: [],
|
|
7406
|
+
incumbent: incumbentLeg,
|
|
7407
|
+
candidate: { latencyMs: cand.latencyMs, tokensIn: cand.tokensIn, tokensOut: cand.tokensOut, text: cand.text },
|
|
7408
|
+
excludedReason: "judge call failed or unparseable"
|
|
7409
|
+
});
|
|
7410
|
+
notes.push(`case #${row.id}: judge failed \u2014 excluded`);
|
|
7411
|
+
continue;
|
|
7412
|
+
}
|
|
7413
|
+
const v1 = order1.winner === "tie" ? "tied" : order1.winner === "B" ? "candidate-better" : "current-better";
|
|
7414
|
+
const v2 = order2.winner === "tie" ? "tied" : order2.winner === "A" ? "candidate-better" : "current-better";
|
|
7415
|
+
const verdict2 = combineOrderSwappedVerdicts(v1, v2);
|
|
7416
|
+
cases.push({
|
|
7417
|
+
goldenIrId: row.id,
|
|
7418
|
+
verdict: verdict2,
|
|
7419
|
+
judgeRationale: order1.rationale ?? order2.rationale,
|
|
7420
|
+
floorViolations: [],
|
|
7421
|
+
incumbent: incumbentLeg,
|
|
7422
|
+
candidate: { latencyMs: cand.latencyMs, tokensIn: cand.tokensIn, tokensOut: cand.tokensOut, text: cand.text }
|
|
7423
|
+
});
|
|
7424
|
+
}
|
|
7425
|
+
const evaluable = cases.filter((c) => !c.excludedReason);
|
|
7426
|
+
const wins = evaluable.filter((c) => c.verdict === "candidate-better").length;
|
|
7427
|
+
const ties = evaluable.filter((c) => c.verdict === "tied").length;
|
|
7428
|
+
const losses = evaluable.filter((c) => c.verdict === "current-better").length;
|
|
7429
|
+
const floorViolations = evaluable.reduce((n, c) => n + c.floorViolations.length, 0);
|
|
7430
|
+
const winOrTieRatio = evaluable.length > 0 ? (wins + ties) / evaluable.length : null;
|
|
7431
|
+
const incP50 = p50(evaluable.map((c) => c.incumbent?.latencyMs ?? NaN).filter(Number.isFinite));
|
|
7432
|
+
const candP50 = p50(evaluable.map((c) => c.candidate?.latencyMs ?? NaN).filter(Number.isFinite));
|
|
7433
|
+
const latencyRatio = incP50 && candP50 ? candP50 / incP50 : null;
|
|
7434
|
+
const sum = (xs) => xs.some((x) => x === null) ? null : xs.reduce((a, b) => a + b, 0);
|
|
7435
|
+
const costIncumbentUsd = sum(
|
|
7436
|
+
evaluable.map(
|
|
7437
|
+
(c) => c.incumbent ? costUsd(incumbentModel, c.incumbent.tokensIn, c.incumbent.tokensOut) : null
|
|
7438
|
+
)
|
|
7439
|
+
);
|
|
7440
|
+
const costCandidateUsd = sum(
|
|
7441
|
+
evaluable.map(
|
|
7442
|
+
(c) => c.candidate ? costUsd(opts.candidateModel, c.candidate.tokensIn, c.candidate.tokensOut) : null
|
|
7443
|
+
)
|
|
7444
|
+
);
|
|
7445
|
+
const latencyFloorOk = latencyRatio === null || latencyRatio <= latencyFloorRatio;
|
|
7446
|
+
if (!latencyFloorOk) bumpFloor("latency");
|
|
7447
|
+
let verdict;
|
|
7448
|
+
if (evaluable.length < minJudgeable) {
|
|
7449
|
+
verdict = "inconclusive";
|
|
7450
|
+
notes.push(
|
|
7451
|
+
`only ${evaluable.length} evaluable case(s) < minJudgeableCases=${minJudgeable}`
|
|
7452
|
+
);
|
|
7453
|
+
} else if (winOrTieRatio !== null && winOrTieRatio >= threshold && floorViolations === 0 && latencyFloorOk) {
|
|
7454
|
+
verdict = "promote-ready";
|
|
7455
|
+
} else {
|
|
7456
|
+
verdict = "not-inferior";
|
|
7457
|
+
}
|
|
7458
|
+
const result = {
|
|
7459
|
+
verdict,
|
|
7460
|
+
appId: opts.appId,
|
|
7461
|
+
archetype: opts.archetype,
|
|
7462
|
+
incumbentModel,
|
|
7463
|
+
candidateModel: opts.candidateModel,
|
|
7464
|
+
judgeModel,
|
|
7465
|
+
nCases: evaluable.length,
|
|
7466
|
+
wins,
|
|
7467
|
+
ties,
|
|
7468
|
+
losses,
|
|
7469
|
+
floorViolations: floorViolations + (latencyFloorOk ? 0 : 1),
|
|
7470
|
+
floorDetail,
|
|
7471
|
+
winOrTieRatio,
|
|
7472
|
+
latencyRatio,
|
|
7473
|
+
costIncumbentUsd,
|
|
7474
|
+
costCandidateUsd,
|
|
7475
|
+
cases,
|
|
7476
|
+
notes
|
|
7477
|
+
};
|
|
7478
|
+
if (opts.dryRun) {
|
|
7479
|
+
notes.push("dry-run: no brain rows written");
|
|
7480
|
+
return result;
|
|
7481
|
+
}
|
|
7482
|
+
progress("Writing eval evidence to the brain\u2026");
|
|
7483
|
+
const runRes = await fetchFn(rest("kgauto_golden_eval_runs"), {
|
|
7484
|
+
method: "POST",
|
|
7485
|
+
headers: {
|
|
7486
|
+
...restHeaders,
|
|
7487
|
+
"Content-Type": "application/json",
|
|
7488
|
+
Prefer: "return=representation"
|
|
7489
|
+
},
|
|
7490
|
+
body: JSON.stringify({
|
|
7491
|
+
app_id: opts.appId,
|
|
7492
|
+
intent_archetype: opts.archetype,
|
|
7493
|
+
incumbent_model: incumbentModel,
|
|
7494
|
+
candidate_model: opts.candidateModel,
|
|
7495
|
+
trigger_source: opts.triggerSource ?? "manual",
|
|
7496
|
+
judge_model: judgeModel,
|
|
7497
|
+
n_cases: result.nCases,
|
|
7498
|
+
wins,
|
|
7499
|
+
ties,
|
|
7500
|
+
losses,
|
|
7501
|
+
floor_violations: result.floorViolations,
|
|
7502
|
+
floor_detail: floorDetail,
|
|
7503
|
+
win_or_tie_ratio: winOrTieRatio,
|
|
7504
|
+
latency_ratio: latencyRatio,
|
|
7505
|
+
verdict,
|
|
7506
|
+
win_or_tie_threshold: threshold,
|
|
7507
|
+
notes: notes.join(" | ") || null
|
|
7508
|
+
})
|
|
7509
|
+
});
|
|
7510
|
+
if (!runRes.ok) {
|
|
7511
|
+
throw new Error(
|
|
7512
|
+
`golden-eval: run-row write failed (${runRes.status}): ${await runRes.text().catch(() => "<no body>")}`
|
|
7513
|
+
);
|
|
7514
|
+
}
|
|
7515
|
+
const runRows = await runRes.json();
|
|
7516
|
+
result.runId = runRows[0]?.id;
|
|
7517
|
+
const caseRows = cases.filter((c) => !c.excludedReason).map((c) => {
|
|
7518
|
+
const row = goldenRows.find((g) => g.id === c.goldenIrId);
|
|
7519
|
+
const turn = typeof row?.ir.currentTurn?.content === "string" ? row.ir.currentTurn.content : "";
|
|
7520
|
+
return {
|
|
7521
|
+
app_id: opts.appId,
|
|
7522
|
+
intent_archetype: opts.archetype,
|
|
7523
|
+
family: tryGetProfile(opts.candidateModel)?.family ?? "unknown",
|
|
7524
|
+
candidate_model: opts.candidateModel,
|
|
7525
|
+
current_model: incumbentModel,
|
|
7526
|
+
prompt_hash: hashForGolden(turn),
|
|
7527
|
+
current_response: c.incumbent?.text.slice(0, 500) ?? null,
|
|
7528
|
+
candidate_response: c.candidate?.text.slice(0, 500) ?? null,
|
|
7529
|
+
judge_verdict: c.floorViolations.length > 0 ? null : c.verdict,
|
|
7530
|
+
judge_rationale: c.floorViolations.length > 0 ? `hard-floor violation: ${c.floorViolations.join(",")}` : c.judgeRationale ?? null,
|
|
7531
|
+
judge_model: judgeModel,
|
|
7532
|
+
tokens_current_in: c.incumbent?.tokensIn ?? null,
|
|
7533
|
+
tokens_current_out: c.incumbent?.tokensOut ?? null,
|
|
7534
|
+
tokens_candidate_in: c.candidate?.tokensIn ?? null,
|
|
7535
|
+
tokens_candidate_out: c.candidate?.tokensOut ?? null,
|
|
7536
|
+
latency_current_ms: c.incumbent?.latencyMs ?? null,
|
|
7537
|
+
latency_candidate_ms: c.candidate?.latencyMs ?? null,
|
|
7538
|
+
prompt_fidelity: 1,
|
|
7539
|
+
replay_source: "golden-replay",
|
|
7540
|
+
outcome: c.floorViolations.includes("candidate_error") ? "candidate_error" : "completed",
|
|
7541
|
+
// migration-032 taxonomy: error_class only on candidate_error rows;
|
|
7542
|
+
// schema-floor detail rides in judge_rationale + the run's floor_detail.
|
|
7543
|
+
error_class: c.floorViolations.includes("candidate_error") ? c.errorClass ?? null : null,
|
|
7544
|
+
golden_run_id: result.runId ?? null,
|
|
7545
|
+
golden_ir_id: c.goldenIrId
|
|
7546
|
+
};
|
|
7547
|
+
});
|
|
7548
|
+
if (caseRows.length > 0) {
|
|
7549
|
+
const casesRes = await fetchFn(rest("probe_outcomes"), {
|
|
7550
|
+
method: "POST",
|
|
7551
|
+
headers: { ...restHeaders, "Content-Type": "application/json", Prefer: "return=minimal" },
|
|
7552
|
+
body: JSON.stringify(caseRows)
|
|
7553
|
+
});
|
|
7554
|
+
if (!casesRes.ok) {
|
|
7555
|
+
notes.push(
|
|
7556
|
+
`case-row write failed (${casesRes.status}): ${await casesRes.text().catch(() => "<no body>")}`
|
|
7557
|
+
);
|
|
7558
|
+
}
|
|
7559
|
+
}
|
|
7560
|
+
const latestRes = await fetchFn(
|
|
7561
|
+
rest(
|
|
7562
|
+
`compile_outcomes?app_id=eq.${encodeURIComponent(opts.appId)}&select=id&order=id.desc&limit=1`
|
|
7563
|
+
),
|
|
7564
|
+
{ headers: restHeaders }
|
|
7565
|
+
);
|
|
7566
|
+
const latest = latestRes.ok ? await latestRes.json() : [];
|
|
7567
|
+
const latestOutcomeId = latest[0]?.id;
|
|
7568
|
+
if (latestOutcomeId !== void 0) {
|
|
7569
|
+
const costLine = costIncumbentUsd !== null && costCandidateUsd !== null ? ` Eval cost basis: incumbent $${costIncumbentUsd.toFixed(4)} vs candidate $${costCandidateUsd.toFixed(4)} across the set.` : "";
|
|
7570
|
+
const message = `Golden-set eval (run #${result.runId}): ${opts.candidateModel} vs ${incumbentModel} on ${opts.appId}/${opts.archetype} \u2014 verdict ${verdict.toUpperCase()}. ${wins}W/${ties}T/${losses}L over ${result.nCases} real workload case(s), wins-or-ties ${(100 * (winOrTieRatio ?? 0)).toFixed(0)}% (threshold ${(100 * threshold).toFixed(0)}%), ${result.floorViolations} hard-floor violation(s), latency ratio ${latencyRatio?.toFixed(2) ?? "n/a"}.` + costLine;
|
|
7571
|
+
const suggestion = verdict === "promote-ready" ? `Candidate passed the non-inferiority rule on your real workload. To promote: node v2/scripts/promote-model.mjs --model ${opts.candidateModel} (or ask kgauto-Cairn). Evidence: kgauto_golden_eval_runs #${result.runId} + probe_outcomes golden_run_id=${result.runId}.` : `Candidate did NOT clear the non-inferiority rule \u2014 no action needed; the incumbent stays. Evidence rows: golden_run_id=${result.runId}.`;
|
|
7572
|
+
const advRes = await fetchFn(rest("compile_outcome_advisories"), {
|
|
7573
|
+
method: "POST",
|
|
7574
|
+
headers: { ...restHeaders, "Content-Type": "application/json", Prefer: "return=minimal" },
|
|
7575
|
+
body: JSON.stringify({
|
|
7576
|
+
outcome_id: latestOutcomeId,
|
|
7577
|
+
code: "golden-eval-verdict",
|
|
7578
|
+
level: "info",
|
|
7579
|
+
recommendation_type: "model-swap",
|
|
7580
|
+
message,
|
|
7581
|
+
suggestion
|
|
7582
|
+
})
|
|
7583
|
+
});
|
|
7584
|
+
if (advRes.ok) {
|
|
7585
|
+
result.advisoryOutcomeId = latestOutcomeId;
|
|
7586
|
+
await fetchFn(rest(`kgauto_golden_eval_runs?id=eq.${result.runId}`), {
|
|
7587
|
+
method: "PATCH",
|
|
7588
|
+
headers: { ...restHeaders, "Content-Type": "application/json", Prefer: "return=minimal" },
|
|
7589
|
+
body: JSON.stringify({ advisory_outcome_id: latestOutcomeId })
|
|
7590
|
+
}).catch(() => {
|
|
7591
|
+
});
|
|
7592
|
+
} else {
|
|
7593
|
+
notes.push(`advisory write failed (${advRes.status})`);
|
|
7594
|
+
}
|
|
7595
|
+
} else {
|
|
7596
|
+
notes.push("no compile_outcomes row for app \u2014 evidence advisory skipped (view needs a real outcome_id)");
|
|
7597
|
+
}
|
|
7598
|
+
return result;
|
|
7599
|
+
}
|
|
7600
|
+
|
|
6939
7601
|
// src/brain-proxy.ts
|
|
6940
7602
|
var SEGMENT_TO_TABLE = {
|
|
6941
7603
|
outcomes: "compile_outcomes",
|
|
6942
7604
|
compile_outcome_advisories: "compile_outcome_advisories",
|
|
6943
7605
|
compile_outcome_quality: "compile_outcome_quality",
|
|
6944
|
-
probe_outcomes: "probe_outcomes"
|
|
7606
|
+
probe_outcomes: "probe_outcomes",
|
|
7607
|
+
// alpha.62 (eval spine): golden-set capture. Only receives rows when the
|
|
7608
|
+
// consumer has explicitly opted in via KGAUTO_GOLDEN_CAPTURE — the segment
|
|
7609
|
+
// existing here does not by itself store anything.
|
|
7610
|
+
golden_irs: "kgauto_golden_irs"
|
|
6945
7611
|
};
|
|
6946
7612
|
var KNOWN_SEGMENTS = Object.keys(SEGMENT_TO_TABLE);
|
|
6947
7613
|
var JSON_HEADERS = { "Content-Type": "application/json" };
|
|
@@ -7099,7 +7765,7 @@ function createBrainForwardRoutes(config) {
|
|
|
7099
7765
|
}
|
|
7100
7766
|
|
|
7101
7767
|
// src/version.ts
|
|
7102
|
-
var LIBRARY_VERSION = "2.0.0-alpha.
|
|
7768
|
+
var LIBRARY_VERSION = "2.0.0-alpha.62";
|
|
7103
7769
|
|
|
7104
7770
|
// src/key-health.ts
|
|
7105
7771
|
var JSON_HEADERS2 = { "Content-Type": "application/json" };
|
|
@@ -7722,6 +8388,7 @@ function compile2(ir, opts) {
|
|
|
7722
8388
|
DIALECT_VERSION,
|
|
7723
8389
|
FamilyResolutionError,
|
|
7724
8390
|
INTENT_ARCHETYPES,
|
|
8391
|
+
JUDGE_RUBRICS,
|
|
7725
8392
|
LATENCY_TIER_MS,
|
|
7726
8393
|
LIBRARY_VERSION,
|
|
7727
8394
|
MEASURED_GROUNDING_MIN_N,
|
|
@@ -7737,10 +8404,14 @@ function compile2(ir, opts) {
|
|
|
7737
8404
|
bucketContext,
|
|
7738
8405
|
bucketHistory,
|
|
7739
8406
|
bucketToolCount,
|
|
8407
|
+
buildGoldenIrRow,
|
|
7740
8408
|
buildLLMJudge,
|
|
8409
|
+
buildPairwiseJudgePrompt,
|
|
7741
8410
|
buildShadowProbeRow,
|
|
7742
8411
|
call,
|
|
8412
|
+
captureGoldenIr,
|
|
7743
8413
|
clearBrain,
|
|
8414
|
+
combineOrderSwappedVerdicts,
|
|
7744
8415
|
compile,
|
|
7745
8416
|
compileForAISDKv6,
|
|
7746
8417
|
configureBrain,
|
|
@@ -7786,19 +8457,27 @@ function compile2(ir, opts) {
|
|
|
7786
8457
|
markAdvisoryResolved,
|
|
7787
8458
|
markExclusionFindingHandled,
|
|
7788
8459
|
markPromoteReadyHandled,
|
|
8460
|
+
parseGoldenCaptureRate,
|
|
8461
|
+
parseJudgeVerdict,
|
|
7789
8462
|
peekBrainDeadLetter,
|
|
7790
8463
|
probeShadow,
|
|
7791
8464
|
profileToRow,
|
|
7792
8465
|
profilesByProvider,
|
|
7793
8466
|
readBrainReadEnv,
|
|
7794
8467
|
record,
|
|
8468
|
+
recordGoldenIr,
|
|
7795
8469
|
recordOutcome,
|
|
7796
8470
|
recordShadowProbe,
|
|
8471
|
+
renderIrForJudge,
|
|
7797
8472
|
resetTokenizer,
|
|
7798
8473
|
resolveConventionsForProfile,
|
|
8474
|
+
resolveGoldenCaptureRate,
|
|
7799
8475
|
resolvePricingAt,
|
|
7800
8476
|
resolveProviderKey,
|
|
8477
|
+
rubricFor,
|
|
7801
8478
|
runAdvisor,
|
|
8479
|
+
runGoldenEval,
|
|
7802
8480
|
setTokenizer,
|
|
8481
|
+
shouldCaptureGolden,
|
|
7803
8482
|
tryGetProfile
|
|
7804
8483
|
});
|