@warmdrift/kgauto-compiler 2.0.0-alpha.60 → 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 +81 -46
- 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-RQ3BUUB6.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 +255 -7
- package/dist/index.d.ts +255 -7
- package/dist/index.js +841 -7
- package/dist/index.mjs +808 -9
- package/dist/{ir-BC4uDL98.d.mts → ir-B2dRyJXO.d.mts} +84 -11
- package/dist/{ir-B2h0GAEL.d.ts → ir-ClU56aBc.d.ts} +84 -11
- 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-CKuu7Clz.d.ts → types-ByN3r0_7.d.ts} +1 -1
- package/dist/{types-CsB2YASc.d.ts → types-C-Wp7HpI.d.ts} +1 -1
- package/dist/{types-B-pzBJKf.d.mts → types-CSDweZsl.d.mts} +1 -1
- package/dist/{types-CRy90cjp.d.mts → types-axAX1Bg0.d.mts} +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createBrainForwardRoutes
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-65ZMX5OT.mjs";
|
|
4
4
|
import {
|
|
5
5
|
ALL_ARCHETYPES,
|
|
6
6
|
DIALECT_VERSION,
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
hashShape,
|
|
12
12
|
isArchetype,
|
|
13
13
|
learningKey
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-3KQAID63.mjs";
|
|
15
15
|
import {
|
|
16
16
|
LIBRARY_VERSION,
|
|
17
17
|
createKeyHealthRoute
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-ABFXMO73.mjs";
|
|
19
19
|
import {
|
|
20
20
|
ABSOLUTE_FLOOR,
|
|
21
21
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
loadChainsFromBrain,
|
|
41
41
|
readBrainReadEnv,
|
|
42
42
|
resolveProviderKey
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-YZRPNSSQ.mjs";
|
|
44
44
|
import {
|
|
45
45
|
ALIASES,
|
|
46
46
|
LATENCY_TIER_MS,
|
|
@@ -2626,6 +2626,134 @@ function clearBrain() {
|
|
|
2626
2626
|
configureBrainQuery(void 0);
|
|
2627
2627
|
configureExclusionFindingsBrain(void 0);
|
|
2628
2628
|
}
|
|
2629
|
+
var DEAD_LETTER_MAX_ENTRIES = 50;
|
|
2630
|
+
var DEAD_LETTER_MAX_ATTEMPTS = 3;
|
|
2631
|
+
var ledger = {
|
|
2632
|
+
sent: 0,
|
|
2633
|
+
acked: 0,
|
|
2634
|
+
failed: 0,
|
|
2635
|
+
redirected: 0,
|
|
2636
|
+
lastAckAt: void 0,
|
|
2637
|
+
lastFailureAt: void 0,
|
|
2638
|
+
lastFailure: void 0
|
|
2639
|
+
};
|
|
2640
|
+
var deadLetter = [];
|
|
2641
|
+
var flushing = false;
|
|
2642
|
+
function noteSent() {
|
|
2643
|
+
ledger.sent += 1;
|
|
2644
|
+
}
|
|
2645
|
+
function noteAck() {
|
|
2646
|
+
ledger.acked += 1;
|
|
2647
|
+
ledger.lastAckAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2648
|
+
}
|
|
2649
|
+
function noteFailure(err) {
|
|
2650
|
+
ledger.failed += 1;
|
|
2651
|
+
ledger.lastFailureAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2652
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2653
|
+
ledger.lastFailure = msg.length > 300 ? `${msg.slice(0, 300)}\u2026` : msg;
|
|
2654
|
+
}
|
|
2655
|
+
function assertNotRedirected(res, route) {
|
|
2656
|
+
if (!res.redirected) return;
|
|
2657
|
+
ledger.redirected += 1;
|
|
2658
|
+
throw new Error(
|
|
2659
|
+
`brain ${route} POST was redirected to ${res.url} \u2014 the write did NOT land. Likely cause: consumer middleware (auth/login allowlist) intercepts the brain proxy path. Add your kgauto proxy routes to the middleware public paths (the 2026-07-02 tt-intel/IC middleware-drift class).`
|
|
2660
|
+
);
|
|
2661
|
+
}
|
|
2662
|
+
function pushDeadLetter(route, endpoint, payload, err) {
|
|
2663
|
+
if (deadLetter.length >= DEAD_LETTER_MAX_ENTRIES) deadLetter.shift();
|
|
2664
|
+
deadLetter.push({
|
|
2665
|
+
route,
|
|
2666
|
+
endpoint,
|
|
2667
|
+
payload,
|
|
2668
|
+
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2669
|
+
attempts: 1,
|
|
2670
|
+
lastError: err instanceof Error ? err.message : String(err)
|
|
2671
|
+
});
|
|
2672
|
+
}
|
|
2673
|
+
function maybeAutoFlush() {
|
|
2674
|
+
if (deadLetter.length === 0 || flushing) return;
|
|
2675
|
+
void flushBrainDeadLetter();
|
|
2676
|
+
}
|
|
2677
|
+
function brainHealth() {
|
|
2678
|
+
let endpointHost;
|
|
2679
|
+
if (activeConfig) {
|
|
2680
|
+
try {
|
|
2681
|
+
endpointHost = new URL(activeConfig.endpoint).host;
|
|
2682
|
+
} catch {
|
|
2683
|
+
endpointHost = void 0;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
return {
|
|
2687
|
+
configState: activeConfig ? "configured" : "not_configured",
|
|
2688
|
+
endpointHost,
|
|
2689
|
+
sync: activeConfig?.sync === true,
|
|
2690
|
+
sent: ledger.sent,
|
|
2691
|
+
acked: ledger.acked,
|
|
2692
|
+
failed: ledger.failed,
|
|
2693
|
+
redirected: ledger.redirected,
|
|
2694
|
+
lastAckAt: ledger.lastAckAt,
|
|
2695
|
+
lastFailureAt: ledger.lastFailureAt,
|
|
2696
|
+
lastFailure: ledger.lastFailure,
|
|
2697
|
+
deadLetterCount: deadLetter.length
|
|
2698
|
+
};
|
|
2699
|
+
}
|
|
2700
|
+
function peekBrainDeadLetter() {
|
|
2701
|
+
return deadLetter.slice();
|
|
2702
|
+
}
|
|
2703
|
+
async function flushBrainDeadLetter() {
|
|
2704
|
+
if (!activeConfig || flushing || deadLetter.length === 0) {
|
|
2705
|
+
return { attempted: 0, delivered: 0, remaining: deadLetter.length };
|
|
2706
|
+
}
|
|
2707
|
+
flushing = true;
|
|
2708
|
+
const config = activeConfig;
|
|
2709
|
+
const fetchFn = brainWriteFetch(config);
|
|
2710
|
+
let attempted = 0;
|
|
2711
|
+
let delivered = 0;
|
|
2712
|
+
try {
|
|
2713
|
+
const batch = deadLetter.filter((e) => e.endpoint === config.endpoint);
|
|
2714
|
+
for (const entry of batch) {
|
|
2715
|
+
attempted += 1;
|
|
2716
|
+
noteSent();
|
|
2717
|
+
try {
|
|
2718
|
+
const res = await fetchFn(`${config.endpoint}/${entry.route}`, {
|
|
2719
|
+
method: "POST",
|
|
2720
|
+
headers: {
|
|
2721
|
+
"Content-Type": "application/json",
|
|
2722
|
+
// Replay never chains a secondary — minimal is correct for all routes.
|
|
2723
|
+
Prefer: "return=minimal",
|
|
2724
|
+
...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
|
|
2725
|
+
},
|
|
2726
|
+
body: JSON.stringify(entry.payload)
|
|
2727
|
+
});
|
|
2728
|
+
assertNotRedirected(res, entry.route);
|
|
2729
|
+
if (!res.ok) {
|
|
2730
|
+
const text = await res.text().catch(() => "<no body>");
|
|
2731
|
+
throw new Error(describeBrainWriteFailure(res.status, entry.route, text));
|
|
2732
|
+
}
|
|
2733
|
+
noteAck();
|
|
2734
|
+
delivered += 1;
|
|
2735
|
+
const idx = deadLetter.indexOf(entry);
|
|
2736
|
+
if (idx !== -1) deadLetter.splice(idx, 1);
|
|
2737
|
+
} catch (err) {
|
|
2738
|
+
noteFailure(err);
|
|
2739
|
+
entry.attempts += 1;
|
|
2740
|
+
entry.lastError = err instanceof Error ? err.message : String(err);
|
|
2741
|
+
if (entry.attempts >= DEAD_LETTER_MAX_ATTEMPTS) {
|
|
2742
|
+
const idx = deadLetter.indexOf(entry);
|
|
2743
|
+
if (idx !== -1) deadLetter.splice(idx, 1);
|
|
2744
|
+
(config.onError ?? defaultOnError4)(
|
|
2745
|
+
new Error(
|
|
2746
|
+
`brain dead-letter: dropping ${entry.route} payload after ${entry.attempts} attempts \u2014 last error: ${entry.lastError}`
|
|
2747
|
+
)
|
|
2748
|
+
);
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
} finally {
|
|
2753
|
+
flushing = false;
|
|
2754
|
+
}
|
|
2755
|
+
return { attempted, delivered, remaining: deadLetter.length };
|
|
2756
|
+
}
|
|
2629
2757
|
var compileRegistry = /* @__PURE__ */ new Map();
|
|
2630
2758
|
var REGISTRY_MAX_ENTRIES = 1e4;
|
|
2631
2759
|
function registerCompile(appId, archetype, ir, result) {
|
|
@@ -2733,6 +2861,7 @@ async function record(input) {
|
|
|
2733
2861
|
const fetchFn = brainWriteFetch(config);
|
|
2734
2862
|
const send = async () => {
|
|
2735
2863
|
let outcomeId;
|
|
2864
|
+
noteSent();
|
|
2736
2865
|
try {
|
|
2737
2866
|
const res = await fetchFn(`${config.endpoint}/outcomes`, {
|
|
2738
2867
|
method: "POST",
|
|
@@ -2752,20 +2881,26 @@ async function record(input) {
|
|
|
2752
2881
|
},
|
|
2753
2882
|
body: JSON.stringify(payload)
|
|
2754
2883
|
});
|
|
2884
|
+
assertNotRedirected(res, "outcomes");
|
|
2755
2885
|
if (!res.ok) {
|
|
2756
2886
|
const text = await res.text().catch(() => "<no body>");
|
|
2757
2887
|
throw new Error(describeBrainWriteFailure(res.status, "outcomes", text));
|
|
2758
2888
|
}
|
|
2889
|
+
noteAck();
|
|
2759
2890
|
outcomeId = await tryExtractOutcomeId(res);
|
|
2760
2891
|
} catch (err) {
|
|
2892
|
+
noteFailure(err);
|
|
2893
|
+
pushDeadLetter("outcomes", config.endpoint, payload, err);
|
|
2761
2894
|
(config.onError ?? defaultOnError4)(err);
|
|
2762
2895
|
return;
|
|
2763
2896
|
}
|
|
2897
|
+
maybeAutoFlush();
|
|
2764
2898
|
const advisories = input.advisories ?? reg?.advisoriesFromCompile;
|
|
2765
2899
|
if (!advisories || advisories.length === 0) return;
|
|
2766
2900
|
if (outcomeId === void 0) return;
|
|
2901
|
+
const advisoryPayload = advisories.map((a) => buildAdvisoryRow(outcomeId, a));
|
|
2902
|
+
noteSent();
|
|
2767
2903
|
try {
|
|
2768
|
-
const advisoryPayload = advisories.map((a) => buildAdvisoryRow(outcomeId, a));
|
|
2769
2904
|
const res = await fetchFn(`${config.endpoint}/compile_outcome_advisories`, {
|
|
2770
2905
|
method: "POST",
|
|
2771
2906
|
headers: {
|
|
@@ -2775,11 +2910,15 @@ async function record(input) {
|
|
|
2775
2910
|
},
|
|
2776
2911
|
body: JSON.stringify(advisoryPayload)
|
|
2777
2912
|
});
|
|
2913
|
+
assertNotRedirected(res, "compile_outcome_advisories");
|
|
2778
2914
|
if (!res.ok) {
|
|
2779
2915
|
const text = await res.text().catch(() => "<no body>");
|
|
2780
2916
|
throw new Error(`brain advisories ${res.status}: ${text}`);
|
|
2781
2917
|
}
|
|
2918
|
+
noteAck();
|
|
2782
2919
|
} catch (err) {
|
|
2920
|
+
noteFailure(err);
|
|
2921
|
+
pushDeadLetter("compile_outcome_advisories", config.endpoint, advisoryPayload, err);
|
|
2783
2922
|
(config.onError ?? defaultOnError4)(err);
|
|
2784
2923
|
}
|
|
2785
2924
|
};
|
|
@@ -2918,6 +3057,7 @@ async function recordOutcome(input) {
|
|
|
2918
3057
|
observed_confidence: input.observedConfidence ?? null
|
|
2919
3058
|
};
|
|
2920
3059
|
const send = async () => {
|
|
3060
|
+
noteSent();
|
|
2921
3061
|
try {
|
|
2922
3062
|
const res = await fetchFn(`${config.endpoint}/compile_outcome_quality`, {
|
|
2923
3063
|
method: "POST",
|
|
@@ -2927,16 +3067,19 @@ async function recordOutcome(input) {
|
|
|
2927
3067
|
},
|
|
2928
3068
|
body: JSON.stringify(payload)
|
|
2929
3069
|
});
|
|
3070
|
+
assertNotRedirected(res, "compile_outcome_quality");
|
|
2930
3071
|
if (!res.ok) {
|
|
2931
3072
|
const text = await res.text().catch(() => "<no body>");
|
|
2932
|
-
|
|
3073
|
+
throw new Error(
|
|
2933
3074
|
describeBrainWriteFailure(res.status, "compile_outcome_quality", text)
|
|
2934
3075
|
);
|
|
2935
|
-
(config.onError ?? defaultOnError4)(err);
|
|
2936
|
-
return { ok: false, reason: "persistence_failed" };
|
|
2937
3076
|
}
|
|
3077
|
+
noteAck();
|
|
3078
|
+
maybeAutoFlush();
|
|
2938
3079
|
return { ok: true };
|
|
2939
3080
|
} catch (err) {
|
|
3081
|
+
noteFailure(err);
|
|
3082
|
+
pushDeadLetter("compile_outcome_quality", config.endpoint, payload, err);
|
|
2940
3083
|
(config.onError ?? defaultOnError4)(err);
|
|
2941
3084
|
return { ok: false, reason: "persistence_failed" };
|
|
2942
3085
|
}
|
|
@@ -2945,7 +3088,7 @@ async function recordOutcome(input) {
|
|
|
2945
3088
|
return send();
|
|
2946
3089
|
}
|
|
2947
3090
|
void send();
|
|
2948
|
-
return { ok:
|
|
3091
|
+
return { ok: "queued" };
|
|
2949
3092
|
}
|
|
2950
3093
|
function isBrainSync() {
|
|
2951
3094
|
return activeConfig?.sync === true;
|
|
@@ -2987,6 +3130,7 @@ async function recordShadowProbe(input) {
|
|
|
2987
3130
|
const fetchFn = brainWriteFetch(config);
|
|
2988
3131
|
const row = buildShadowProbeRow(input);
|
|
2989
3132
|
const send = async () => {
|
|
3133
|
+
noteSent();
|
|
2990
3134
|
try {
|
|
2991
3135
|
const res = await fetchFn(`${config.endpoint}/probe_outcomes`, {
|
|
2992
3136
|
method: "POST",
|
|
@@ -2997,11 +3141,71 @@ async function recordShadowProbe(input) {
|
|
|
2997
3141
|
},
|
|
2998
3142
|
body: JSON.stringify(row)
|
|
2999
3143
|
});
|
|
3144
|
+
assertNotRedirected(res, "probe_outcomes");
|
|
3000
3145
|
if (!res.ok) {
|
|
3001
3146
|
const text = await res.text().catch(() => "<no body>");
|
|
3002
3147
|
throw new Error(describeBrainWriteFailure(res.status, "probe_outcomes", text));
|
|
3003
3148
|
}
|
|
3149
|
+
noteAck();
|
|
3150
|
+
maybeAutoFlush();
|
|
3004
3151
|
} catch (err) {
|
|
3152
|
+
noteFailure(err);
|
|
3153
|
+
pushDeadLetter("probe_outcomes", config.endpoint, row, err);
|
|
3154
|
+
(config.onError ?? defaultOnError4)(err);
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
if (config.sync) {
|
|
3158
|
+
await send();
|
|
3159
|
+
} else {
|
|
3160
|
+
void send();
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
function peekRegisteredShapeKey(handle) {
|
|
3164
|
+
return compileRegistry.get(handle)?.shapeKey;
|
|
3165
|
+
}
|
|
3166
|
+
function buildGoldenIrRow(input) {
|
|
3167
|
+
return {
|
|
3168
|
+
app_id: input.appId,
|
|
3169
|
+
intent_archetype: input.archetype,
|
|
3170
|
+
shape_key: input.shapeKey ?? null,
|
|
3171
|
+
ir: input.ir,
|
|
3172
|
+
incumbent_model: input.incumbentModel,
|
|
3173
|
+
incumbent_output: input.incumbentOutput ?? null,
|
|
3174
|
+
incumbent_latency_ms: input.incumbentLatencyMs ?? null,
|
|
3175
|
+
incumbent_tokens_in: input.incumbentTokensIn ?? null,
|
|
3176
|
+
incumbent_tokens_out: input.incumbentTokensOut ?? null,
|
|
3177
|
+
source: input.source,
|
|
3178
|
+
consent: input.consent,
|
|
3179
|
+
outcome_handle: input.outcomeHandle ?? null
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
async function recordGoldenIr(input) {
|
|
3183
|
+
if (!activeConfig) return;
|
|
3184
|
+
const config = activeConfig;
|
|
3185
|
+
const fetchFn = brainWriteFetch(config);
|
|
3186
|
+
const row = buildGoldenIrRow(input);
|
|
3187
|
+
const send = async () => {
|
|
3188
|
+
noteSent();
|
|
3189
|
+
try {
|
|
3190
|
+
const res = await fetchFn(`${config.endpoint}/golden_irs`, {
|
|
3191
|
+
method: "POST",
|
|
3192
|
+
headers: {
|
|
3193
|
+
"Content-Type": "application/json",
|
|
3194
|
+
Prefer: "return=minimal",
|
|
3195
|
+
...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
|
|
3196
|
+
},
|
|
3197
|
+
body: JSON.stringify(row)
|
|
3198
|
+
});
|
|
3199
|
+
assertNotRedirected(res, "golden_irs");
|
|
3200
|
+
if (!res.ok) {
|
|
3201
|
+
const text = await res.text().catch(() => "<no body>");
|
|
3202
|
+
throw new Error(describeBrainWriteFailure(res.status, "golden_irs", text));
|
|
3203
|
+
}
|
|
3204
|
+
noteAck();
|
|
3205
|
+
maybeAutoFlush();
|
|
3206
|
+
} catch (err) {
|
|
3207
|
+
noteFailure(err);
|
|
3208
|
+
pushDeadLetter("golden_irs", config.endpoint, row, err);
|
|
3005
3209
|
(config.onError ?? defaultOnError4)(err);
|
|
3006
3210
|
}
|
|
3007
3211
|
};
|
|
@@ -3026,6 +3230,45 @@ var CallError = class extends Error {
|
|
|
3026
3230
|
}
|
|
3027
3231
|
};
|
|
3028
3232
|
|
|
3233
|
+
// src/golden.ts
|
|
3234
|
+
function parseGoldenCaptureRate(raw) {
|
|
3235
|
+
if (raw === void 0 || raw.trim() === "") return 0;
|
|
3236
|
+
const n = Number(raw);
|
|
3237
|
+
if (!Number.isFinite(n)) return 0;
|
|
3238
|
+
return Math.max(0, Math.min(1, n));
|
|
3239
|
+
}
|
|
3240
|
+
function resolveGoldenCaptureRate(optRate) {
|
|
3241
|
+
if (optRate !== void 0 && Number.isFinite(optRate)) {
|
|
3242
|
+
return Math.max(0, Math.min(1, optRate));
|
|
3243
|
+
}
|
|
3244
|
+
const env = typeof process !== "undefined" && process.env ? process.env.KGAUTO_GOLDEN_CAPTURE : void 0;
|
|
3245
|
+
return parseGoldenCaptureRate(env);
|
|
3246
|
+
}
|
|
3247
|
+
function shouldCaptureGolden(rate, rng = Math.random) {
|
|
3248
|
+
if (rate <= 0) return false;
|
|
3249
|
+
if (rate >= 1) return true;
|
|
3250
|
+
return rng() < rate;
|
|
3251
|
+
}
|
|
3252
|
+
async function captureGoldenIr(ctx) {
|
|
3253
|
+
try {
|
|
3254
|
+
await recordGoldenIr({
|
|
3255
|
+
appId: ctx.ir.appId,
|
|
3256
|
+
archetype: ctx.ir.intent.archetype,
|
|
3257
|
+
shapeKey: ctx.shapeKey,
|
|
3258
|
+
ir: ctx.ir,
|
|
3259
|
+
incumbentModel: ctx.servedModel,
|
|
3260
|
+
incumbentOutput: ctx.response.text,
|
|
3261
|
+
incumbentLatencyMs: ctx.latencyMs,
|
|
3262
|
+
incumbentTokensIn: ctx.response.tokens.input,
|
|
3263
|
+
incumbentTokensOut: ctx.response.tokens.output,
|
|
3264
|
+
source: "sampled",
|
|
3265
|
+
consent: ctx.consent,
|
|
3266
|
+
outcomeHandle: ctx.handle
|
|
3267
|
+
});
|
|
3268
|
+
} catch {
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3029
3272
|
// src/streaming.ts
|
|
3030
3273
|
var ANTHROPIC_URL = "https://api.anthropic.com/v1/messages";
|
|
3031
3274
|
async function streamAnthropic(request, apiKey, opts) {
|
|
@@ -3737,6 +3980,8 @@ async function call(ir, opts = {}) {
|
|
|
3737
3980
|
);
|
|
3738
3981
|
const fellOver = targetModel !== initial.target;
|
|
3739
3982
|
const fallbackReason = fellOver ? normalizeFallbackReason(attempts) : void 0;
|
|
3983
|
+
const goldenRate = resolveGoldenCaptureRate(opts.goldenCapture?.sampleRate);
|
|
3984
|
+
const goldenShapeKey = goldenRate > 0 ? peekRegisteredShapeKey(initial.handle) : void 0;
|
|
3740
3985
|
await record({
|
|
3741
3986
|
handle: initial.handle,
|
|
3742
3987
|
tokensIn: validated.response.tokens.input,
|
|
@@ -3789,6 +4034,23 @@ async function call(ir, opts = {}) {
|
|
|
3789
4034
|
void probe;
|
|
3790
4035
|
}
|
|
3791
4036
|
}
|
|
4037
|
+
if (goldenRate > 0 && validated.response.tokens.output > 0 && shouldCaptureGolden(goldenRate)) {
|
|
4038
|
+
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)`;
|
|
4039
|
+
const capture = captureGoldenIr({
|
|
4040
|
+
ir,
|
|
4041
|
+
servedModel: targetModel,
|
|
4042
|
+
response: validated.response,
|
|
4043
|
+
latencyMs: latencyMs2,
|
|
4044
|
+
handle: initial.handle,
|
|
4045
|
+
shapeKey: goldenShapeKey,
|
|
4046
|
+
consent
|
|
4047
|
+
});
|
|
4048
|
+
if (isBrainSync()) {
|
|
4049
|
+
await capture;
|
|
4050
|
+
} else {
|
|
4051
|
+
void capture;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
3792
4054
|
return {
|
|
3793
4055
|
handle: initial.handle,
|
|
3794
4056
|
actualModel: targetModel,
|
|
@@ -4128,6 +4390,18 @@ function safeEmit(fn) {
|
|
|
4128
4390
|
} catch {
|
|
4129
4391
|
}
|
|
4130
4392
|
}
|
|
4393
|
+
var _internal = {
|
|
4394
|
+
compileAndRegister,
|
|
4395
|
+
validateStructuredContract,
|
|
4396
|
+
extractPromptPreview,
|
|
4397
|
+
normalizeFallbackReason,
|
|
4398
|
+
generateTraceId,
|
|
4399
|
+
safeEmit,
|
|
4400
|
+
shouldSampleProbe,
|
|
4401
|
+
normalizeProbeCandidates,
|
|
4402
|
+
runShadowProbe,
|
|
4403
|
+
runProbeCandidates
|
|
4404
|
+
};
|
|
4131
4405
|
|
|
4132
4406
|
// src/streamtext-helpers.ts
|
|
4133
4407
|
function attachCacheControlToStreamTextInput(result, convertedMessages) {
|
|
@@ -4227,6 +4501,515 @@ function extractKeptToolNames(result) {
|
|
|
4227
4501
|
return names;
|
|
4228
4502
|
}
|
|
4229
4503
|
|
|
4504
|
+
// src/golden-eval.ts
|
|
4505
|
+
var GENERIC_RUBRIC = "overall correctness, completeness against the request, clarity, and adherence to any requested format";
|
|
4506
|
+
var JUDGE_RUBRICS = {
|
|
4507
|
+
summarize: "faithfulness to the source (no fabricated facts), coverage of the key points, concision, and adherence to the requested output format",
|
|
4508
|
+
classify: "assignment of the correct category from the allowed set, and nothing outside the allowed set",
|
|
4509
|
+
generate: "relevance to the brief, quality and coherence of the writing, and adherence to the requested format and tone",
|
|
4510
|
+
extract: "completeness of extraction (nothing relevant missed), precision (no invented fields or values), and exact schema adherence",
|
|
4511
|
+
ask: "correctness of the answer with respect to the provided data, relevance, and absence of fabrication",
|
|
4512
|
+
hunt: "relevance and novelty of the discovered entities, and absence of duplicates or fabricated entries",
|
|
4513
|
+
plan: "quality of the decomposition (complete, ordered, actionable steps) and realism of the sequencing",
|
|
4514
|
+
critique: "specificity and correctness of the assessment, and actionability of the feedback",
|
|
4515
|
+
transform: "preservation of the source content and correctness of the target format or style"
|
|
4516
|
+
};
|
|
4517
|
+
function rubricFor(archetype) {
|
|
4518
|
+
return JUDGE_RUBRICS[archetype] ?? GENERIC_RUBRIC;
|
|
4519
|
+
}
|
|
4520
|
+
var INPUT_TRUNCATE_CHARS = 24e3;
|
|
4521
|
+
var OUTPUT_TRUNCATE_CHARS = 12e3;
|
|
4522
|
+
function renderIrForJudge(ir) {
|
|
4523
|
+
const sections = (ir.sections ?? []).map((s) => s.text).filter(Boolean).join("\n\n");
|
|
4524
|
+
const turn = typeof ir.currentTurn?.content === "string" ? ir.currentTurn.content : "";
|
|
4525
|
+
const combined = [
|
|
4526
|
+
sections ? `[system]
|
|
4527
|
+
${sections}` : "",
|
|
4528
|
+
turn ? `[user]
|
|
4529
|
+
${turn}` : ""
|
|
4530
|
+
].filter(Boolean).join("\n\n");
|
|
4531
|
+
return combined.length > INPUT_TRUNCATE_CHARS ? `${combined.slice(0, INPUT_TRUNCATE_CHARS)}
|
|
4532
|
+
\u2026[truncated]` : combined;
|
|
4533
|
+
}
|
|
4534
|
+
function buildPairwiseJudgePrompt(args) {
|
|
4535
|
+
const clip = (s) => s.length > OUTPUT_TRUNCATE_CHARS ? `${s.slice(0, OUTPUT_TRUNCATE_CHARS)}
|
|
4536
|
+
\u2026[truncated]` : s;
|
|
4537
|
+
return `You are a strict pairwise judge comparing two AI responses to the SAME request.
|
|
4538
|
+
|
|
4539
|
+
The request (archetype: ${args.archetype}):
|
|
4540
|
+
<request>
|
|
4541
|
+
${args.renderedInput}
|
|
4542
|
+
</request>
|
|
4543
|
+
|
|
4544
|
+
Response A:
|
|
4545
|
+
<response_a>
|
|
4546
|
+
${clip(args.outputA)}
|
|
4547
|
+
</response_a>
|
|
4548
|
+
|
|
4549
|
+
Response B:
|
|
4550
|
+
<response_b>
|
|
4551
|
+
${clip(args.outputB)}
|
|
4552
|
+
</response_b>
|
|
4553
|
+
|
|
4554
|
+
Judge which response better satisfies the request. Rubric for ${args.archetype}: ${rubricFor(args.archetype)}.
|
|
4555
|
+
|
|
4556
|
+
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.
|
|
4557
|
+
|
|
4558
|
+
Output ONLY this JSON, no other text:
|
|
4559
|
+
{"winner": "A" | "B" | "tie", "rationale": "<one sentence>"}`;
|
|
4560
|
+
}
|
|
4561
|
+
function parseJudgeVerdict(raw) {
|
|
4562
|
+
const cleaned = raw.replace(/^```(?:json)?\s*/i, "").replace(/\s*```\s*$/, "").trim();
|
|
4563
|
+
let parsed;
|
|
4564
|
+
try {
|
|
4565
|
+
parsed = JSON.parse(cleaned);
|
|
4566
|
+
} catch {
|
|
4567
|
+
const match = cleaned.match(/\{[\s\S]*\}/);
|
|
4568
|
+
if (!match) return void 0;
|
|
4569
|
+
try {
|
|
4570
|
+
parsed = JSON.parse(match[0]);
|
|
4571
|
+
} catch {
|
|
4572
|
+
return void 0;
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
4576
|
+
const obj = parsed;
|
|
4577
|
+
const winner = obj.winner;
|
|
4578
|
+
if (winner !== "A" && winner !== "B" && winner !== "tie") return void 0;
|
|
4579
|
+
return {
|
|
4580
|
+
winner,
|
|
4581
|
+
rationale: typeof obj.rationale === "string" ? obj.rationale : void 0
|
|
4582
|
+
};
|
|
4583
|
+
}
|
|
4584
|
+
function combineOrderSwappedVerdicts(run1, run2) {
|
|
4585
|
+
if (run1 === run2) return run1;
|
|
4586
|
+
return "tied";
|
|
4587
|
+
}
|
|
4588
|
+
function p50(values) {
|
|
4589
|
+
if (values.length === 0) return null;
|
|
4590
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
4591
|
+
return sorted[Math.floor(sorted.length / 2)] ?? null;
|
|
4592
|
+
}
|
|
4593
|
+
function costUsd(model, tokensIn, tokensOut) {
|
|
4594
|
+
const profile = tryGetProfile(model);
|
|
4595
|
+
if (!profile) return null;
|
|
4596
|
+
return tokensIn / 1e6 * profile.costInputPer1m + tokensOut / 1e6 * profile.costOutputPer1m;
|
|
4597
|
+
}
|
|
4598
|
+
function hashForGolden(s) {
|
|
4599
|
+
let h = 5381;
|
|
4600
|
+
for (let i = 0; i < s.length; i += 1) {
|
|
4601
|
+
h = (h << 5) + h + s.charCodeAt(i) >>> 0;
|
|
4602
|
+
}
|
|
4603
|
+
return `g${h.toString(36)}`;
|
|
4604
|
+
}
|
|
4605
|
+
async function runGoldenEval(opts) {
|
|
4606
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
4607
|
+
const progress = opts.onProgress ?? (() => {
|
|
4608
|
+
});
|
|
4609
|
+
const limit = opts.limit ?? 50;
|
|
4610
|
+
const threshold = opts.winOrTieThreshold ?? 0.8;
|
|
4611
|
+
const latencyFloorRatio = opts.latencyFloorRatio ?? 3;
|
|
4612
|
+
const minJudgeable = opts.minJudgeableCases ?? 5;
|
|
4613
|
+
const judgeModel = opts.judgeModel ?? "claude-opus-4-8";
|
|
4614
|
+
const notes = [];
|
|
4615
|
+
const restHeaders = {
|
|
4616
|
+
apikey: opts.serviceKey,
|
|
4617
|
+
Authorization: `Bearer ${opts.serviceKey}`
|
|
4618
|
+
};
|
|
4619
|
+
const rest = (path) => `${opts.supabaseUrl}/rest/v1/${path}`;
|
|
4620
|
+
const setUrl = rest(
|
|
4621
|
+
`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}`
|
|
4622
|
+
);
|
|
4623
|
+
const setRes = await fetchFn(setUrl, { headers: restHeaders });
|
|
4624
|
+
if (!setRes.ok) {
|
|
4625
|
+
throw new Error(
|
|
4626
|
+
`golden-eval: failed to load golden set (${setRes.status}): ${await setRes.text().catch(() => "<no body>")}`
|
|
4627
|
+
);
|
|
4628
|
+
}
|
|
4629
|
+
const goldenRows = await setRes.json();
|
|
4630
|
+
if (goldenRows.length === 0) {
|
|
4631
|
+
throw new Error(
|
|
4632
|
+
`golden-eval: no active golden IRs for (${opts.appId}, ${opts.archetype}) \u2014 seed via KGAUTO_GOLDEN_CAPTURE or scripts/seed-golden-set.mjs first.`
|
|
4633
|
+
);
|
|
4634
|
+
}
|
|
4635
|
+
progress(`Loaded ${goldenRows.length} golden case(s).`);
|
|
4636
|
+
let incumbentModel = opts.incumbentModel;
|
|
4637
|
+
if (!incumbentModel) {
|
|
4638
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4639
|
+
for (const row of goldenRows) {
|
|
4640
|
+
counts.set(row.incumbent_model, (counts.get(row.incumbent_model) ?? 0) + 1);
|
|
4641
|
+
}
|
|
4642
|
+
const top = [...counts.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
4643
|
+
if (!top) throw new Error("golden-eval: could not resolve an incumbent from the golden set.");
|
|
4644
|
+
incumbentModel = top[0];
|
|
4645
|
+
notes.push(`incumbent defaulted to most-captured model: ${incumbentModel}`);
|
|
4646
|
+
}
|
|
4647
|
+
if (incumbentModel === opts.candidateModel) {
|
|
4648
|
+
throw new Error("golden-eval: candidate and incumbent are the same model.");
|
|
4649
|
+
}
|
|
4650
|
+
const judgeProfile = tryGetProfile(judgeModel);
|
|
4651
|
+
if (!judgeProfile) {
|
|
4652
|
+
throw new Error(`golden-eval: judge model '${judgeModel}' is not in the roster.`);
|
|
4653
|
+
}
|
|
4654
|
+
if (judgeProfile.family && [incumbentModel, opts.candidateModel].some(
|
|
4655
|
+
(m) => tryGetProfile(m)?.family === judgeProfile.family
|
|
4656
|
+
)) {
|
|
4657
|
+
notes.push(
|
|
4658
|
+
`WARNING: judge family (${judgeProfile.family}) overlaps a compared model \u2014 self-preference risk; consider --judge from another provider.`
|
|
4659
|
+
);
|
|
4660
|
+
}
|
|
4661
|
+
const replay = async (ir, model) => {
|
|
4662
|
+
try {
|
|
4663
|
+
const evalIr = {
|
|
4664
|
+
...ir,
|
|
4665
|
+
models: [model],
|
|
4666
|
+
constraints: { ...ir.constraints ?? {}, forceModel: model }
|
|
4667
|
+
};
|
|
4668
|
+
const compiled = compile(evalIr);
|
|
4669
|
+
const attempt = () => execute(compiled.request, { apiKeys: opts.apiKeys, fetchImpl: opts.fetchImpl });
|
|
4670
|
+
let started = Date.now();
|
|
4671
|
+
let exec = await attempt();
|
|
4672
|
+
if (!exec.ok && exec.errorType === "retryable") {
|
|
4673
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
4674
|
+
started = Date.now();
|
|
4675
|
+
exec = await attempt();
|
|
4676
|
+
}
|
|
4677
|
+
const latencyMs = Date.now() - started;
|
|
4678
|
+
if (!exec.ok) {
|
|
4679
|
+
return {
|
|
4680
|
+
ok: false,
|
|
4681
|
+
text: "",
|
|
4682
|
+
structuredOutput: null,
|
|
4683
|
+
tokensIn: 0,
|
|
4684
|
+
tokensOut: 0,
|
|
4685
|
+
latencyMs,
|
|
4686
|
+
errorClass: exec.errorCode
|
|
4687
|
+
};
|
|
4688
|
+
}
|
|
4689
|
+
const validated = _internal.validateStructuredContract(exec, evalIr);
|
|
4690
|
+
if (!validated.ok) {
|
|
4691
|
+
return {
|
|
4692
|
+
ok: true,
|
|
4693
|
+
text: exec.response.text,
|
|
4694
|
+
structuredOutput: null,
|
|
4695
|
+
tokensIn: exec.response.tokens.input,
|
|
4696
|
+
tokensOut: exec.response.tokens.output,
|
|
4697
|
+
latencyMs,
|
|
4698
|
+
contractViolation: validated.errorCode
|
|
4699
|
+
};
|
|
4700
|
+
}
|
|
4701
|
+
return {
|
|
4702
|
+
ok: true,
|
|
4703
|
+
text: validated.response.text,
|
|
4704
|
+
structuredOutput: validated.response.structuredOutput,
|
|
4705
|
+
parseError: validated.response.parseError,
|
|
4706
|
+
tokensIn: validated.response.tokens.input,
|
|
4707
|
+
tokensOut: validated.response.tokens.output,
|
|
4708
|
+
latencyMs
|
|
4709
|
+
};
|
|
4710
|
+
} catch (err) {
|
|
4711
|
+
return {
|
|
4712
|
+
ok: false,
|
|
4713
|
+
text: "",
|
|
4714
|
+
structuredOutput: null,
|
|
4715
|
+
tokensIn: 0,
|
|
4716
|
+
tokensOut: 0,
|
|
4717
|
+
latencyMs: 0,
|
|
4718
|
+
errorClass: err instanceof Error ? `execute_rejected:${err.message}` : "execute_rejected"
|
|
4719
|
+
};
|
|
4720
|
+
}
|
|
4721
|
+
};
|
|
4722
|
+
const judgeOnce = async (ir, outputA, outputB) => {
|
|
4723
|
+
const judgeIr = {
|
|
4724
|
+
appId: opts.appId,
|
|
4725
|
+
intent: { name: "golden-eval-judge", archetype: "judge" },
|
|
4726
|
+
sections: [
|
|
4727
|
+
{
|
|
4728
|
+
id: "role",
|
|
4729
|
+
text: "You are a strict, impartial pairwise judge. You output only JSON.",
|
|
4730
|
+
weight: 10
|
|
4731
|
+
}
|
|
4732
|
+
],
|
|
4733
|
+
currentTurn: {
|
|
4734
|
+
role: "user",
|
|
4735
|
+
content: buildPairwiseJudgePrompt({
|
|
4736
|
+
archetype: opts.archetype,
|
|
4737
|
+
renderedInput: renderIrForJudge(ir),
|
|
4738
|
+
outputA,
|
|
4739
|
+
outputB
|
|
4740
|
+
})
|
|
4741
|
+
},
|
|
4742
|
+
models: [judgeModel],
|
|
4743
|
+
constraints: { forceModel: judgeModel }
|
|
4744
|
+
};
|
|
4745
|
+
try {
|
|
4746
|
+
const compiled = compile(judgeIr);
|
|
4747
|
+
const exec = await execute(compiled.request, {
|
|
4748
|
+
apiKeys: opts.apiKeys,
|
|
4749
|
+
fetchImpl: opts.fetchImpl
|
|
4750
|
+
});
|
|
4751
|
+
if (!exec.ok) return void 0;
|
|
4752
|
+
return parseJudgeVerdict(exec.response.text);
|
|
4753
|
+
} catch {
|
|
4754
|
+
return void 0;
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4757
|
+
const cases = [];
|
|
4758
|
+
const floorDetail = {};
|
|
4759
|
+
const bumpFloor = (k) => {
|
|
4760
|
+
floorDetail[k] = (floorDetail[k] ?? 0) + 1;
|
|
4761
|
+
};
|
|
4762
|
+
for (const [i, row] of goldenRows.entries()) {
|
|
4763
|
+
progress(
|
|
4764
|
+
`Case ${i + 1}/${goldenRows.length} (golden #${row.id}): replaying ${incumbentModel} + ${opts.candidateModel}\u2026`
|
|
4765
|
+
);
|
|
4766
|
+
const [inc, cand] = await Promise.all([
|
|
4767
|
+
replay(row.ir, incumbentModel),
|
|
4768
|
+
replay(row.ir, opts.candidateModel)
|
|
4769
|
+
]);
|
|
4770
|
+
if (!inc.ok || inc.contractViolation) {
|
|
4771
|
+
const why = !inc.ok ? `incumbent replay failed (${inc.errorClass ?? "unknown"})` : `incumbent contract violation (${inc.contractViolation})`;
|
|
4772
|
+
cases.push({
|
|
4773
|
+
goldenIrId: row.id,
|
|
4774
|
+
verdict: "inconclusive",
|
|
4775
|
+
floorViolations: [],
|
|
4776
|
+
excludedReason: why
|
|
4777
|
+
});
|
|
4778
|
+
notes.push(`case #${row.id}: ${why} \u2014 excluded`);
|
|
4779
|
+
continue;
|
|
4780
|
+
}
|
|
4781
|
+
const incumbentLeg = {
|
|
4782
|
+
latencyMs: inc.latencyMs,
|
|
4783
|
+
tokensIn: inc.tokensIn,
|
|
4784
|
+
tokensOut: inc.tokensOut,
|
|
4785
|
+
text: inc.text
|
|
4786
|
+
};
|
|
4787
|
+
const violations = [];
|
|
4788
|
+
if (!cand.ok) {
|
|
4789
|
+
violations.push("candidate_error");
|
|
4790
|
+
bumpFloor("candidate_error");
|
|
4791
|
+
} else if (cand.tokensOut === 0 || cand.text.trim() === "" && !cand.structuredOutput) {
|
|
4792
|
+
violations.push("empty");
|
|
4793
|
+
bumpFloor("empty");
|
|
4794
|
+
} else if (cand.contractViolation) {
|
|
4795
|
+
violations.push("schema");
|
|
4796
|
+
bumpFloor("schema");
|
|
4797
|
+
}
|
|
4798
|
+
if (violations.length > 0) {
|
|
4799
|
+
cases.push({
|
|
4800
|
+
goldenIrId: row.id,
|
|
4801
|
+
verdict: "current-better",
|
|
4802
|
+
floorViolations: violations,
|
|
4803
|
+
errorClass: cand.errorClass ?? cand.contractViolation,
|
|
4804
|
+
incumbent: incumbentLeg,
|
|
4805
|
+
candidate: cand.ok ? { latencyMs: cand.latencyMs, tokensIn: cand.tokensIn, tokensOut: cand.tokensOut, text: cand.text } : void 0
|
|
4806
|
+
});
|
|
4807
|
+
continue;
|
|
4808
|
+
}
|
|
4809
|
+
const [order1, order2] = await Promise.all([
|
|
4810
|
+
judgeOnce(row.ir, inc.text, cand.text),
|
|
4811
|
+
judgeOnce(row.ir, cand.text, inc.text)
|
|
4812
|
+
]);
|
|
4813
|
+
if (!order1 || !order2) {
|
|
4814
|
+
cases.push({
|
|
4815
|
+
goldenIrId: row.id,
|
|
4816
|
+
verdict: "inconclusive",
|
|
4817
|
+
floorViolations: [],
|
|
4818
|
+
incumbent: incumbentLeg,
|
|
4819
|
+
candidate: { latencyMs: cand.latencyMs, tokensIn: cand.tokensIn, tokensOut: cand.tokensOut, text: cand.text },
|
|
4820
|
+
excludedReason: "judge call failed or unparseable"
|
|
4821
|
+
});
|
|
4822
|
+
notes.push(`case #${row.id}: judge failed \u2014 excluded`);
|
|
4823
|
+
continue;
|
|
4824
|
+
}
|
|
4825
|
+
const v1 = order1.winner === "tie" ? "tied" : order1.winner === "B" ? "candidate-better" : "current-better";
|
|
4826
|
+
const v2 = order2.winner === "tie" ? "tied" : order2.winner === "A" ? "candidate-better" : "current-better";
|
|
4827
|
+
const verdict2 = combineOrderSwappedVerdicts(v1, v2);
|
|
4828
|
+
cases.push({
|
|
4829
|
+
goldenIrId: row.id,
|
|
4830
|
+
verdict: verdict2,
|
|
4831
|
+
judgeRationale: order1.rationale ?? order2.rationale,
|
|
4832
|
+
floorViolations: [],
|
|
4833
|
+
incumbent: incumbentLeg,
|
|
4834
|
+
candidate: { latencyMs: cand.latencyMs, tokensIn: cand.tokensIn, tokensOut: cand.tokensOut, text: cand.text }
|
|
4835
|
+
});
|
|
4836
|
+
}
|
|
4837
|
+
const evaluable = cases.filter((c) => !c.excludedReason);
|
|
4838
|
+
const wins = evaluable.filter((c) => c.verdict === "candidate-better").length;
|
|
4839
|
+
const ties = evaluable.filter((c) => c.verdict === "tied").length;
|
|
4840
|
+
const losses = evaluable.filter((c) => c.verdict === "current-better").length;
|
|
4841
|
+
const floorViolations = evaluable.reduce((n, c) => n + c.floorViolations.length, 0);
|
|
4842
|
+
const winOrTieRatio = evaluable.length > 0 ? (wins + ties) / evaluable.length : null;
|
|
4843
|
+
const incP50 = p50(evaluable.map((c) => c.incumbent?.latencyMs ?? NaN).filter(Number.isFinite));
|
|
4844
|
+
const candP50 = p50(evaluable.map((c) => c.candidate?.latencyMs ?? NaN).filter(Number.isFinite));
|
|
4845
|
+
const latencyRatio = incP50 && candP50 ? candP50 / incP50 : null;
|
|
4846
|
+
const sum = (xs) => xs.some((x) => x === null) ? null : xs.reduce((a, b) => a + b, 0);
|
|
4847
|
+
const costIncumbentUsd = sum(
|
|
4848
|
+
evaluable.map(
|
|
4849
|
+
(c) => c.incumbent ? costUsd(incumbentModel, c.incumbent.tokensIn, c.incumbent.tokensOut) : null
|
|
4850
|
+
)
|
|
4851
|
+
);
|
|
4852
|
+
const costCandidateUsd = sum(
|
|
4853
|
+
evaluable.map(
|
|
4854
|
+
(c) => c.candidate ? costUsd(opts.candidateModel, c.candidate.tokensIn, c.candidate.tokensOut) : null
|
|
4855
|
+
)
|
|
4856
|
+
);
|
|
4857
|
+
const latencyFloorOk = latencyRatio === null || latencyRatio <= latencyFloorRatio;
|
|
4858
|
+
if (!latencyFloorOk) bumpFloor("latency");
|
|
4859
|
+
let verdict;
|
|
4860
|
+
if (evaluable.length < minJudgeable) {
|
|
4861
|
+
verdict = "inconclusive";
|
|
4862
|
+
notes.push(
|
|
4863
|
+
`only ${evaluable.length} evaluable case(s) < minJudgeableCases=${minJudgeable}`
|
|
4864
|
+
);
|
|
4865
|
+
} else if (winOrTieRatio !== null && winOrTieRatio >= threshold && floorViolations === 0 && latencyFloorOk) {
|
|
4866
|
+
verdict = "promote-ready";
|
|
4867
|
+
} else {
|
|
4868
|
+
verdict = "not-inferior";
|
|
4869
|
+
}
|
|
4870
|
+
const result = {
|
|
4871
|
+
verdict,
|
|
4872
|
+
appId: opts.appId,
|
|
4873
|
+
archetype: opts.archetype,
|
|
4874
|
+
incumbentModel,
|
|
4875
|
+
candidateModel: opts.candidateModel,
|
|
4876
|
+
judgeModel,
|
|
4877
|
+
nCases: evaluable.length,
|
|
4878
|
+
wins,
|
|
4879
|
+
ties,
|
|
4880
|
+
losses,
|
|
4881
|
+
floorViolations: floorViolations + (latencyFloorOk ? 0 : 1),
|
|
4882
|
+
floorDetail,
|
|
4883
|
+
winOrTieRatio,
|
|
4884
|
+
latencyRatio,
|
|
4885
|
+
costIncumbentUsd,
|
|
4886
|
+
costCandidateUsd,
|
|
4887
|
+
cases,
|
|
4888
|
+
notes
|
|
4889
|
+
};
|
|
4890
|
+
if (opts.dryRun) {
|
|
4891
|
+
notes.push("dry-run: no brain rows written");
|
|
4892
|
+
return result;
|
|
4893
|
+
}
|
|
4894
|
+
progress("Writing eval evidence to the brain\u2026");
|
|
4895
|
+
const runRes = await fetchFn(rest("kgauto_golden_eval_runs"), {
|
|
4896
|
+
method: "POST",
|
|
4897
|
+
headers: {
|
|
4898
|
+
...restHeaders,
|
|
4899
|
+
"Content-Type": "application/json",
|
|
4900
|
+
Prefer: "return=representation"
|
|
4901
|
+
},
|
|
4902
|
+
body: JSON.stringify({
|
|
4903
|
+
app_id: opts.appId,
|
|
4904
|
+
intent_archetype: opts.archetype,
|
|
4905
|
+
incumbent_model: incumbentModel,
|
|
4906
|
+
candidate_model: opts.candidateModel,
|
|
4907
|
+
trigger_source: opts.triggerSource ?? "manual",
|
|
4908
|
+
judge_model: judgeModel,
|
|
4909
|
+
n_cases: result.nCases,
|
|
4910
|
+
wins,
|
|
4911
|
+
ties,
|
|
4912
|
+
losses,
|
|
4913
|
+
floor_violations: result.floorViolations,
|
|
4914
|
+
floor_detail: floorDetail,
|
|
4915
|
+
win_or_tie_ratio: winOrTieRatio,
|
|
4916
|
+
latency_ratio: latencyRatio,
|
|
4917
|
+
verdict,
|
|
4918
|
+
win_or_tie_threshold: threshold,
|
|
4919
|
+
notes: notes.join(" | ") || null
|
|
4920
|
+
})
|
|
4921
|
+
});
|
|
4922
|
+
if (!runRes.ok) {
|
|
4923
|
+
throw new Error(
|
|
4924
|
+
`golden-eval: run-row write failed (${runRes.status}): ${await runRes.text().catch(() => "<no body>")}`
|
|
4925
|
+
);
|
|
4926
|
+
}
|
|
4927
|
+
const runRows = await runRes.json();
|
|
4928
|
+
result.runId = runRows[0]?.id;
|
|
4929
|
+
const caseRows = cases.filter((c) => !c.excludedReason).map((c) => {
|
|
4930
|
+
const row = goldenRows.find((g) => g.id === c.goldenIrId);
|
|
4931
|
+
const turn = typeof row?.ir.currentTurn?.content === "string" ? row.ir.currentTurn.content : "";
|
|
4932
|
+
return {
|
|
4933
|
+
app_id: opts.appId,
|
|
4934
|
+
intent_archetype: opts.archetype,
|
|
4935
|
+
family: tryGetProfile(opts.candidateModel)?.family ?? "unknown",
|
|
4936
|
+
candidate_model: opts.candidateModel,
|
|
4937
|
+
current_model: incumbentModel,
|
|
4938
|
+
prompt_hash: hashForGolden(turn),
|
|
4939
|
+
current_response: c.incumbent?.text.slice(0, 500) ?? null,
|
|
4940
|
+
candidate_response: c.candidate?.text.slice(0, 500) ?? null,
|
|
4941
|
+
judge_verdict: c.floorViolations.length > 0 ? null : c.verdict,
|
|
4942
|
+
judge_rationale: c.floorViolations.length > 0 ? `hard-floor violation: ${c.floorViolations.join(",")}` : c.judgeRationale ?? null,
|
|
4943
|
+
judge_model: judgeModel,
|
|
4944
|
+
tokens_current_in: c.incumbent?.tokensIn ?? null,
|
|
4945
|
+
tokens_current_out: c.incumbent?.tokensOut ?? null,
|
|
4946
|
+
tokens_candidate_in: c.candidate?.tokensIn ?? null,
|
|
4947
|
+
tokens_candidate_out: c.candidate?.tokensOut ?? null,
|
|
4948
|
+
latency_current_ms: c.incumbent?.latencyMs ?? null,
|
|
4949
|
+
latency_candidate_ms: c.candidate?.latencyMs ?? null,
|
|
4950
|
+
prompt_fidelity: 1,
|
|
4951
|
+
replay_source: "golden-replay",
|
|
4952
|
+
outcome: c.floorViolations.includes("candidate_error") ? "candidate_error" : "completed",
|
|
4953
|
+
// migration-032 taxonomy: error_class only on candidate_error rows;
|
|
4954
|
+
// schema-floor detail rides in judge_rationale + the run's floor_detail.
|
|
4955
|
+
error_class: c.floorViolations.includes("candidate_error") ? c.errorClass ?? null : null,
|
|
4956
|
+
golden_run_id: result.runId ?? null,
|
|
4957
|
+
golden_ir_id: c.goldenIrId
|
|
4958
|
+
};
|
|
4959
|
+
});
|
|
4960
|
+
if (caseRows.length > 0) {
|
|
4961
|
+
const casesRes = await fetchFn(rest("probe_outcomes"), {
|
|
4962
|
+
method: "POST",
|
|
4963
|
+
headers: { ...restHeaders, "Content-Type": "application/json", Prefer: "return=minimal" },
|
|
4964
|
+
body: JSON.stringify(caseRows)
|
|
4965
|
+
});
|
|
4966
|
+
if (!casesRes.ok) {
|
|
4967
|
+
notes.push(
|
|
4968
|
+
`case-row write failed (${casesRes.status}): ${await casesRes.text().catch(() => "<no body>")}`
|
|
4969
|
+
);
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
const latestRes = await fetchFn(
|
|
4973
|
+
rest(
|
|
4974
|
+
`compile_outcomes?app_id=eq.${encodeURIComponent(opts.appId)}&select=id&order=id.desc&limit=1`
|
|
4975
|
+
),
|
|
4976
|
+
{ headers: restHeaders }
|
|
4977
|
+
);
|
|
4978
|
+
const latest = latestRes.ok ? await latestRes.json() : [];
|
|
4979
|
+
const latestOutcomeId = latest[0]?.id;
|
|
4980
|
+
if (latestOutcomeId !== void 0) {
|
|
4981
|
+
const costLine = costIncumbentUsd !== null && costCandidateUsd !== null ? ` Eval cost basis: incumbent $${costIncumbentUsd.toFixed(4)} vs candidate $${costCandidateUsd.toFixed(4)} across the set.` : "";
|
|
4982
|
+
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;
|
|
4983
|
+
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}.`;
|
|
4984
|
+
const advRes = await fetchFn(rest("compile_outcome_advisories"), {
|
|
4985
|
+
method: "POST",
|
|
4986
|
+
headers: { ...restHeaders, "Content-Type": "application/json", Prefer: "return=minimal" },
|
|
4987
|
+
body: JSON.stringify({
|
|
4988
|
+
outcome_id: latestOutcomeId,
|
|
4989
|
+
code: "golden-eval-verdict",
|
|
4990
|
+
level: "info",
|
|
4991
|
+
recommendation_type: "model-swap",
|
|
4992
|
+
message,
|
|
4993
|
+
suggestion
|
|
4994
|
+
})
|
|
4995
|
+
});
|
|
4996
|
+
if (advRes.ok) {
|
|
4997
|
+
result.advisoryOutcomeId = latestOutcomeId;
|
|
4998
|
+
await fetchFn(rest(`kgauto_golden_eval_runs?id=eq.${result.runId}`), {
|
|
4999
|
+
method: "PATCH",
|
|
5000
|
+
headers: { ...restHeaders, "Content-Type": "application/json", Prefer: "return=minimal" },
|
|
5001
|
+
body: JSON.stringify({ advisory_outcome_id: latestOutcomeId })
|
|
5002
|
+
}).catch(() => {
|
|
5003
|
+
});
|
|
5004
|
+
} else {
|
|
5005
|
+
notes.push(`advisory write failed (${advRes.status})`);
|
|
5006
|
+
}
|
|
5007
|
+
} else {
|
|
5008
|
+
notes.push("no compile_outcomes row for app \u2014 evidence advisory skipped (view needs a real outcome_id)");
|
|
5009
|
+
}
|
|
5010
|
+
return result;
|
|
5011
|
+
}
|
|
5012
|
+
|
|
4230
5013
|
// src/oracle.ts
|
|
4231
5014
|
var DEFAULT_DIMENSIONS = ["correctness", "completeness", "conciseness", "format"];
|
|
4232
5015
|
var judgeCallTimes = [];
|
|
@@ -4664,6 +5447,7 @@ export {
|
|
|
4664
5447
|
DIALECT_VERSION,
|
|
4665
5448
|
FamilyResolutionError,
|
|
4666
5449
|
INTENT_ARCHETYPES,
|
|
5450
|
+
JUDGE_RUBRICS,
|
|
4667
5451
|
LATENCY_TIER_MS,
|
|
4668
5452
|
LIBRARY_VERSION,
|
|
4669
5453
|
MEASURED_GROUNDING_MIN_N,
|
|
@@ -4675,13 +5459,18 @@ export {
|
|
|
4675
5459
|
applyArchetypeConvention,
|
|
4676
5460
|
applySectionRewrites,
|
|
4677
5461
|
attachCacheControlToStreamTextInput,
|
|
5462
|
+
brainHealth,
|
|
4678
5463
|
bucketContext,
|
|
4679
5464
|
bucketHistory,
|
|
4680
5465
|
bucketToolCount,
|
|
5466
|
+
buildGoldenIrRow,
|
|
4681
5467
|
buildLLMJudge,
|
|
5468
|
+
buildPairwiseJudgePrompt,
|
|
4682
5469
|
buildShadowProbeRow,
|
|
4683
5470
|
call,
|
|
5471
|
+
captureGoldenIr,
|
|
4684
5472
|
clearBrain,
|
|
5473
|
+
combineOrderSwappedVerdicts,
|
|
4685
5474
|
compile2 as compile,
|
|
4686
5475
|
compileForAISDKv6,
|
|
4687
5476
|
configureBrain,
|
|
@@ -4692,6 +5481,7 @@ export {
|
|
|
4692
5481
|
deriveOwnership,
|
|
4693
5482
|
execute,
|
|
4694
5483
|
findBetterFit,
|
|
5484
|
+
flushBrainDeadLetter,
|
|
4695
5485
|
getActionableAdvisories,
|
|
4696
5486
|
getAllStarterChains,
|
|
4697
5487
|
getAllStarterChainsWithGrounding,
|
|
@@ -4726,18 +5516,27 @@ export {
|
|
|
4726
5516
|
markAdvisoryResolved,
|
|
4727
5517
|
markExclusionFindingHandled,
|
|
4728
5518
|
markPromoteReadyHandled,
|
|
5519
|
+
parseGoldenCaptureRate,
|
|
5520
|
+
parseJudgeVerdict,
|
|
5521
|
+
peekBrainDeadLetter,
|
|
4729
5522
|
probeShadow,
|
|
4730
5523
|
profileToRow,
|
|
4731
5524
|
profilesByProvider,
|
|
4732
5525
|
readBrainReadEnv,
|
|
4733
5526
|
record,
|
|
5527
|
+
recordGoldenIr,
|
|
4734
5528
|
recordOutcome,
|
|
4735
5529
|
recordShadowProbe,
|
|
5530
|
+
renderIrForJudge,
|
|
4736
5531
|
resetTokenizer,
|
|
4737
5532
|
resolveConventionsForProfile,
|
|
5533
|
+
resolveGoldenCaptureRate,
|
|
4738
5534
|
resolvePricingAt,
|
|
4739
5535
|
resolveProviderKey,
|
|
5536
|
+
rubricFor,
|
|
4740
5537
|
runAdvisor,
|
|
5538
|
+
runGoldenEval,
|
|
4741
5539
|
setTokenizer,
|
|
5540
|
+
shouldCaptureGolden,
|
|
4742
5541
|
tryGetProfile
|
|
4743
5542
|
};
|