@warmdrift/kgauto-compiler 2.0.0-alpha.49 → 2.0.0-alpha.51
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 +22 -0
- package/dist/{chunk-PKOFXEB3.mjs → chunk-CXH7KC4D.mjs} +119 -6
- package/dist/{chunk-SAWTKMD4.mjs → chunk-HHWBB46W.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 +119 -6
- package/dist/glassbox-routes/index.mjs +2 -2
- 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 +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +201 -12
- package/dist/index.mjs +84 -8
- package/dist/{ir-BBKgsUX7.d.mts → ir-dDcG8Pvu.d.mts} +43 -0
- package/dist/{ir-D3n-pBYI.d.ts → ir-rUUojj0s.d.ts} +43 -0
- package/dist/profiles.d.mts +1 -1
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.js +119 -6
- package/dist/profiles.mjs +1 -1
- package/dist/{types-xBWYk9Mm.d.mts → types-BTeRoSvM.d.mts} +1 -1
- package/dist/{types-B6z-8phQ.d.ts → types-B_pdPjxm.d.ts} +1 -1
- package/dist/{types-Bzw8tH-7.d.ts → types-UXPxWabQ.d.ts} +1 -1
- package/dist/{types-DMJqRzDF.d.mts → types-vGo-h0tZ.d.mts} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -310,6 +310,115 @@ var GOOGLE_LOWERING_BASE = {
|
|
|
310
310
|
};
|
|
311
311
|
var PROFILES_RAW = [
|
|
312
312
|
// ── Anthropic ──
|
|
313
|
+
{
|
|
314
|
+
// VERIFIED 2026-06-21 against the claude-api reference. Claude Fable 5 is
|
|
315
|
+
// Anthropic's most capable model — a NEW tier ABOVE the opus/sonnet/haiku
|
|
316
|
+
// line (the id has no MAJOR-MINOR version, just `-5`) at 2× Opus pricing
|
|
317
|
+
// ($10/$50 vs $5/$25). Hand-authored, not cloned: the onboarder regex can't
|
|
318
|
+
// match this shape (new family + single-segment version), AND a clone would
|
|
319
|
+
// carry the wrong (opus) pricing. status:'preview' — no brain evidence yet;
|
|
320
|
+
// it earns per-archetype placement through the machinery like any model, and
|
|
321
|
+
// at 2× Opus the quality-floor + cost gates do real work before it leads.
|
|
322
|
+
// API quirk (claude-api ref): an explicit `thinking:{type:"disabled"}` 400s
|
|
323
|
+
// on Fable 5 (omit instead) — MOOT here: ANTHROPIC_LOWERING_BASE carries no
|
|
324
|
+
// `thinking` field, so kgauto never sends the param. (L-049/L-081.)
|
|
325
|
+
id: "claude-fable-5",
|
|
326
|
+
verifiedAgainstDocs: "2026-06-21",
|
|
327
|
+
provider: "anthropic",
|
|
328
|
+
status: "preview",
|
|
329
|
+
maxContextTokens: 1e6,
|
|
330
|
+
maxOutputTokens: 128e3,
|
|
331
|
+
maxTools: 64,
|
|
332
|
+
parallelToolCalls: true,
|
|
333
|
+
structuredOutput: "grammar",
|
|
334
|
+
systemPromptMode: "inline",
|
|
335
|
+
streaming: true,
|
|
336
|
+
cliffs: [],
|
|
337
|
+
costInputPer1m: 10,
|
|
338
|
+
costOutputPer1m: 50,
|
|
339
|
+
lowering: ANTHROPIC_LOWERING_BASE,
|
|
340
|
+
recovery: [
|
|
341
|
+
{
|
|
342
|
+
signal: "rate_limit",
|
|
343
|
+
action: "escalate",
|
|
344
|
+
reason: "429 from Anthropic \u2014 escalate to fallback chain"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
signal: "model_not_found",
|
|
348
|
+
action: "escalate",
|
|
349
|
+
reason: "Model deprecated/renamed \u2014 escalate (L-061)"
|
|
350
|
+
}
|
|
351
|
+
],
|
|
352
|
+
strengths: ["reasoning", "agentic_coding", "long_context", "reliable_tool_use", "structured_output"],
|
|
353
|
+
weaknesses: ["cost", "latency"],
|
|
354
|
+
notes: "Most capable Anthropic model (2026-06); a new tier ABOVE Opus at 2\xD7 pricing ($10/$50). 1M ctx, 128k out. Verified 2026-06-21 against the claude-api reference. status:preview \u2014 no brain evidence yet; earns placement via the machinery (cost + quality-floor gates govern). latencyTier derives `slow` (weaknesses includes latency \u2014 minutes-long turns on hard tasks). The thinking-disabled-400 API quirk is moot for kgauto (Anthropic lowering emits no thinking param).",
|
|
355
|
+
// archetypePerf cloned from claude-opus-4-8 as a conservative estimate
|
|
356
|
+
// (Fable >= Opus in capability, but unmeasured — no fabricated superiority).
|
|
357
|
+
// Re-rank once brain rows exist.
|
|
358
|
+
archetypePerf: {
|
|
359
|
+
critique: 10,
|
|
360
|
+
plan: 10,
|
|
361
|
+
generate: 9,
|
|
362
|
+
ask: 9,
|
|
363
|
+
extract: 9,
|
|
364
|
+
transform: 9,
|
|
365
|
+
hunt: 8,
|
|
366
|
+
summarize: 8,
|
|
367
|
+
classify: 8
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
// Auto-onboarded 2026-06-04 from `claude-opus-4-7`; VERIFIED 2026-06-21
|
|
372
|
+
// against the claude-api reference. Opus 4.8 shares 4.7's surface exactly —
|
|
373
|
+
// 1M ctx, 128k out, $5/$25, no new breaking changes — so the clone's
|
|
374
|
+
// capability data was accurate (confirmed field-by-field). archetypePerf is
|
|
375
|
+
// still cloned from 4.7 (a sound estimate: 4.8 >= 4.7) — re-rank once brain
|
|
376
|
+
// rows exist. Promoted to status:'current' 2026-06-21 (Sacha: "include Claude
|
|
377
|
+
// Opus 4.8") — now live-selectable. Ties with the still-current claude-opus-4-7
|
|
378
|
+
// (identical $5/$25 + cloned perf) resolve to 4-8 by array order. (L-049/L-081.)
|
|
379
|
+
id: "claude-opus-4-8",
|
|
380
|
+
verifiedAgainstDocs: "2026-06-21",
|
|
381
|
+
provider: "anthropic",
|
|
382
|
+
status: "current",
|
|
383
|
+
maxContextTokens: 1e6,
|
|
384
|
+
maxOutputTokens: 128e3,
|
|
385
|
+
maxTools: 64,
|
|
386
|
+
parallelToolCalls: true,
|
|
387
|
+
structuredOutput: "grammar",
|
|
388
|
+
systemPromptMode: "inline",
|
|
389
|
+
streaming: true,
|
|
390
|
+
cliffs: [],
|
|
391
|
+
costInputPer1m: 5,
|
|
392
|
+
costOutputPer1m: 25,
|
|
393
|
+
lowering: ANTHROPIC_LOWERING_BASE,
|
|
394
|
+
recovery: [
|
|
395
|
+
{
|
|
396
|
+
signal: "rate_limit",
|
|
397
|
+
action: "escalate",
|
|
398
|
+
reason: "429 from Anthropic \u2014 escalate to fallback chain"
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
signal: "model_not_found",
|
|
402
|
+
action: "escalate",
|
|
403
|
+
reason: "Model deprecated/renamed \u2014 escalate (L-061)"
|
|
404
|
+
}
|
|
405
|
+
],
|
|
406
|
+
strengths: ["reasoning", "agentic_coding", "long_context", "reliable_tool_use", "structured_output"],
|
|
407
|
+
weaknesses: ["cost", "latency"],
|
|
408
|
+
notes: "Frontier Opus (2026-06), the current recommended Opus-tier default. Verified 2026-06-21 against the claude-api reference: 1M ctx, 128k out, $5/$25 \u2014 identical surface to 4.7, no new breaking changes. Promoted to status:current 2026-06-21 (live-selectable); coexists with claude-opus-4-7 (still current).",
|
|
409
|
+
// Cloned archetypePerf from claude-opus-4-7 — re-evaluate once brain rows exist.
|
|
410
|
+
archetypePerf: {
|
|
411
|
+
critique: 10,
|
|
412
|
+
plan: 10,
|
|
413
|
+
generate: 9,
|
|
414
|
+
ask: 9,
|
|
415
|
+
extract: 9,
|
|
416
|
+
transform: 9,
|
|
417
|
+
hunt: 8,
|
|
418
|
+
summarize: 8,
|
|
419
|
+
classify: 8
|
|
420
|
+
}
|
|
421
|
+
},
|
|
313
422
|
{
|
|
314
423
|
id: "claude-opus-4-7",
|
|
315
424
|
verifiedAgainstDocs: "2026-05-08",
|
|
@@ -877,7 +986,7 @@ var PROFILES_RAW = [
|
|
|
877
986
|
},
|
|
878
987
|
{
|
|
879
988
|
id: "deepseek-v4-pro",
|
|
880
|
-
verifiedAgainstDocs: "2026-
|
|
989
|
+
verifiedAgainstDocs: "2026-06-22",
|
|
881
990
|
provider: "deepseek",
|
|
882
991
|
status: "current",
|
|
883
992
|
maxContextTokens: 1e6,
|
|
@@ -895,10 +1004,14 @@ var PROFILES_RAW = [
|
|
|
895
1004
|
reason: "Sequential tool calls only \u2014 L-040"
|
|
896
1005
|
}
|
|
897
1006
|
],
|
|
898
|
-
//
|
|
899
|
-
//
|
|
900
|
-
|
|
901
|
-
|
|
1007
|
+
// Verified against the live DeepSeek pricing page 2026-06-22: the 75%-off
|
|
1008
|
+
// launch "promo" ($0.435/$0.87) did NOT revert on 2026-05-31 — it became the
|
|
1009
|
+
// standing price (docs show $0.435 in / $0.87 out cache-miss, $0.003625
|
|
1010
|
+
// cache-hit in; no promo caveat). The old $1.74/$3.48 "regular post-promo"
|
|
1011
|
+
// was a projected revert that never happened (L-073/L-081) — it 4×-overstated
|
|
1012
|
+
// cost and suppressed V4-Pro in cost-aware ranking.
|
|
1013
|
+
costInputPer1m: 0.435,
|
|
1014
|
+
costOutputPer1m: 0.87,
|
|
902
1015
|
lowering: {
|
|
903
1016
|
system: { mode: "inline" },
|
|
904
1017
|
cache: { strategy: "unsupported" },
|
|
@@ -914,7 +1027,7 @@ var PROFILES_RAW = [
|
|
|
914
1027
|
// of the served set. Tag derivation would say 'medium'; the measurement says
|
|
915
1028
|
// otherwise.
|
|
916
1029
|
latencyTier: "slow",
|
|
917
|
-
notes: "Pro tier. 1M context, 384k max output.
|
|
1030
|
+
notes: "Pro tier. 1M context, 384k max output. $0.435/$0.87 per 1M (cache-hit in $0.003625) \u2014 verified live 2026-06-22; the launch promo became the standing price (no revert). Default mode = thinking.",
|
|
918
1031
|
// Master plan §3.3: tier 3 cross-provider for plan chain. Reasoning
|
|
919
1032
|
// bumped one notch over V4-Flash; same parallel-tool ceiling.
|
|
920
1033
|
archetypePerf: {
|
|
@@ -4589,7 +4702,10 @@ function buildShadowProbeRow(input) {
|
|
|
4589
4702
|
// Full IR was replayed (not a truncated preview), so fidelity is 1.0 — the
|
|
4590
4703
|
// prompt-fidelity guard never fires on these rows.
|
|
4591
4704
|
prompt_fidelity: 1,
|
|
4592
|
-
replay_source: "inline-full-ir"
|
|
4705
|
+
replay_source: "inline-full-ir",
|
|
4706
|
+
// alpha — migration 029. 'completed' is the default completed-probe shape;
|
|
4707
|
+
// diagnostic rows (aborted/skipped) pass the explicit class.
|
|
4708
|
+
outcome: input.outcome ?? "completed"
|
|
4593
4709
|
};
|
|
4594
4710
|
}
|
|
4595
4711
|
async function recordShadowProbe(input) {
|
|
@@ -6201,15 +6317,54 @@ function hashForProbe(s) {
|
|
|
6201
6317
|
for (let i = 0; i < s.length; i++) h = (h << 5) - h + s.charCodeAt(i) | 0;
|
|
6202
6318
|
return `h${(h >>> 0).toString(16)}`;
|
|
6203
6319
|
}
|
|
6320
|
+
var DEFAULT_PROBE_MAX_LATENCY_MS = 15e3;
|
|
6321
|
+
function isSlowTierCandidate(candidate) {
|
|
6322
|
+
const profile = tryGetProfile(candidate);
|
|
6323
|
+
if (!profile) return false;
|
|
6324
|
+
return latencyTierOf(profile) === "slow";
|
|
6325
|
+
}
|
|
6204
6326
|
async function runProbeCandidates(args) {
|
|
6205
6327
|
try {
|
|
6206
6328
|
if (!shouldSampleProbe(args.cfg.sampleRate ?? 0.05)) return;
|
|
6207
6329
|
const candidates = normalizeProbeCandidates(args.cfg.candidates);
|
|
6208
6330
|
const promptHash = hashForProbe(args.ir.currentTurn?.content ?? "");
|
|
6331
|
+
const budgetMs = args.cfg.maxLatencyMs ?? DEFAULT_PROBE_MAX_LATENCY_MS;
|
|
6332
|
+
const deadline = args.sync ? Date.now() + budgetMs : Number.POSITIVE_INFINITY;
|
|
6333
|
+
const skipSlow = args.sync && (args.cfg.skipSlowTierInSync ?? true);
|
|
6334
|
+
const recordDiagnostic = async (candidate, outcome) => {
|
|
6335
|
+
await recordShadowProbe({
|
|
6336
|
+
appId: args.ir.appId,
|
|
6337
|
+
archetype: args.ir.intent.archetype,
|
|
6338
|
+
family: deriveFamilyFromModelId(candidate) ?? "unknown",
|
|
6339
|
+
candidateModel: candidate,
|
|
6340
|
+
currentModel: args.servedModel,
|
|
6341
|
+
promptHash,
|
|
6342
|
+
// Served leg only — the candidate never produced a comparable response.
|
|
6343
|
+
currentResponsePreview: args.servedResponse.text.slice(0, 2e3),
|
|
6344
|
+
tokensCurrentIn: args.servedResponse.tokens.input,
|
|
6345
|
+
tokensCurrentOut: args.servedResponse.tokens.output,
|
|
6346
|
+
latencyCurrentMs: args.servedLatencyMs,
|
|
6347
|
+
outcome
|
|
6348
|
+
});
|
|
6349
|
+
};
|
|
6209
6350
|
for (const candidate of candidates) {
|
|
6210
6351
|
if (candidate === args.servedModel) continue;
|
|
6211
6352
|
if (deriveFamilyFromModelId(candidate) === "claude-opus") continue;
|
|
6212
6353
|
if (!isModelReachable(candidate, { apiKeys: args.exec.apiKeys })) continue;
|
|
6354
|
+
if (skipSlow && isSlowTierCandidate(candidate)) {
|
|
6355
|
+
try {
|
|
6356
|
+
await recordDiagnostic(candidate, "skipped_slow_tier_sync");
|
|
6357
|
+
} catch {
|
|
6358
|
+
}
|
|
6359
|
+
continue;
|
|
6360
|
+
}
|
|
6361
|
+
if (args.sync && Date.now() >= deadline) {
|
|
6362
|
+
try {
|
|
6363
|
+
await recordDiagnostic(candidate, "aborted_latency_budget");
|
|
6364
|
+
} catch {
|
|
6365
|
+
}
|
|
6366
|
+
break;
|
|
6367
|
+
}
|
|
6213
6368
|
try {
|
|
6214
6369
|
const candCompile = compileAndRegister(
|
|
6215
6370
|
{
|
|
@@ -6220,11 +6375,32 @@ async function runProbeCandidates(args) {
|
|
|
6220
6375
|
args.exec
|
|
6221
6376
|
);
|
|
6222
6377
|
const candStart = Date.now();
|
|
6223
|
-
|
|
6378
|
+
let timer;
|
|
6379
|
+
const execPromise = execute(candCompile.request, {
|
|
6224
6380
|
apiKeys: args.exec.apiKeys,
|
|
6225
6381
|
fetchImpl: args.exec.fetchImpl,
|
|
6226
6382
|
providerOverrides: args.exec.providerOverrides
|
|
6227
|
-
});
|
|
6383
|
+
}).then((exec2) => ({ kind: "exec", exec: exec2 })).catch(() => ({ kind: "failed" }));
|
|
6384
|
+
let raced;
|
|
6385
|
+
if (args.sync) {
|
|
6386
|
+
const remaining = Math.max(0, deadline - Date.now());
|
|
6387
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
6388
|
+
timer = setTimeout(() => resolve({ kind: "timeout" }), remaining);
|
|
6389
|
+
});
|
|
6390
|
+
raced = await Promise.race([execPromise, timeoutPromise]);
|
|
6391
|
+
if (timer) clearTimeout(timer);
|
|
6392
|
+
} else {
|
|
6393
|
+
raced = await execPromise;
|
|
6394
|
+
}
|
|
6395
|
+
if (raced.kind === "timeout") {
|
|
6396
|
+
try {
|
|
6397
|
+
await recordDiagnostic(candidate, "aborted_latency_budget");
|
|
6398
|
+
} catch {
|
|
6399
|
+
}
|
|
6400
|
+
break;
|
|
6401
|
+
}
|
|
6402
|
+
if (raced.kind === "failed") continue;
|
|
6403
|
+
const exec = raced.exec;
|
|
6228
6404
|
const candidateLatencyMs = Date.now() - candStart;
|
|
6229
6405
|
if (!exec.ok) continue;
|
|
6230
6406
|
await recordShadowProbe({
|
|
@@ -6244,7 +6420,8 @@ async function runProbeCandidates(args) {
|
|
|
6244
6420
|
// mirrors the user's actual wait; candidate latency is measured only
|
|
6245
6421
|
// here and stored nowhere else (irrecoverable if not captured now).
|
|
6246
6422
|
latencyCurrentMs: args.servedLatencyMs,
|
|
6247
|
-
latencyCandidateMs: candidateLatencyMs
|
|
6423
|
+
latencyCandidateMs: candidateLatencyMs,
|
|
6424
|
+
outcome: "completed"
|
|
6248
6425
|
});
|
|
6249
6426
|
} catch {
|
|
6250
6427
|
}
|
|
@@ -6262,7 +6439,11 @@ async function runShadowProbe(args) {
|
|
|
6262
6439
|
servedResponse: args.servedResponse,
|
|
6263
6440
|
servedLatencyMs: args.servedLatencyMs,
|
|
6264
6441
|
// CallOptions is a structural superset of ProbeExecOpts.
|
|
6265
|
-
exec: args.opts
|
|
6442
|
+
exec: args.opts,
|
|
6443
|
+
// alpha — the call()-inline path is on the user's critical path exactly when
|
|
6444
|
+
// BrainConfig.sync is set (call() awaits the probe before returning). That's
|
|
6445
|
+
// the only case where the latency budget + slow-tier skip must fire.
|
|
6446
|
+
sync: isBrainSync()
|
|
6266
6447
|
});
|
|
6267
6448
|
}
|
|
6268
6449
|
async function probeShadow(ir, opts) {
|
|
@@ -6295,7 +6476,15 @@ async function probeShadow(ir, opts) {
|
|
|
6295
6476
|
apiKeys: opts.apiKeys,
|
|
6296
6477
|
fetchImpl: opts.fetchImpl,
|
|
6297
6478
|
providerOverrides: opts.providerOverrides
|
|
6298
|
-
}
|
|
6479
|
+
},
|
|
6480
|
+
// alpha — probeShadow() is ALWAYS off the user-facing critical path: the
|
|
6481
|
+
// consumer fires it from `onFinish` wrapped in `after()`/`waitUntil()`,
|
|
6482
|
+
// after the response is flushed. So the sync-mode latency budget +
|
|
6483
|
+
// slow-tier skip are no-ops here — slow candidates run to completion (this
|
|
6484
|
+
// is in fact the right place to evaluate a slow reasoner). The
|
|
6485
|
+
// maxLatencyMs/skipSlowTierInSync knobs aren't even exposed on
|
|
6486
|
+
// ProbeShadowOptions for that reason.
|
|
6487
|
+
sync: false
|
|
6299
6488
|
});
|
|
6300
6489
|
} catch {
|
|
6301
6490
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
loadChainsFromBrain,
|
|
34
34
|
readBrainReadEnv,
|
|
35
35
|
resolveProviderKey
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-HHWBB46W.mjs";
|
|
37
37
|
import {
|
|
38
38
|
ALIASES,
|
|
39
39
|
LATENCY_TIER_MS,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
latencyTierOf,
|
|
45
45
|
profilesByProvider,
|
|
46
46
|
tryGetProfile
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-CXH7KC4D.mjs";
|
|
48
48
|
import {
|
|
49
49
|
emitAdvisoryFired,
|
|
50
50
|
emitCompileDone,
|
|
@@ -2914,7 +2914,10 @@ function buildShadowProbeRow(input) {
|
|
|
2914
2914
|
// Full IR was replayed (not a truncated preview), so fidelity is 1.0 — the
|
|
2915
2915
|
// prompt-fidelity guard never fires on these rows.
|
|
2916
2916
|
prompt_fidelity: 1,
|
|
2917
|
-
replay_source: "inline-full-ir"
|
|
2917
|
+
replay_source: "inline-full-ir",
|
|
2918
|
+
// alpha — migration 029. 'completed' is the default completed-probe shape;
|
|
2919
|
+
// diagnostic rows (aborted/skipped) pass the explicit class.
|
|
2920
|
+
outcome: input.outcome ?? "completed"
|
|
2918
2921
|
};
|
|
2919
2922
|
}
|
|
2920
2923
|
async function recordShadowProbe(input) {
|
|
@@ -3811,15 +3814,54 @@ function hashForProbe(s) {
|
|
|
3811
3814
|
for (let i = 0; i < s.length; i++) h = (h << 5) - h + s.charCodeAt(i) | 0;
|
|
3812
3815
|
return `h${(h >>> 0).toString(16)}`;
|
|
3813
3816
|
}
|
|
3817
|
+
var DEFAULT_PROBE_MAX_LATENCY_MS = 15e3;
|
|
3818
|
+
function isSlowTierCandidate(candidate) {
|
|
3819
|
+
const profile = tryGetProfile(candidate);
|
|
3820
|
+
if (!profile) return false;
|
|
3821
|
+
return latencyTierOf(profile) === "slow";
|
|
3822
|
+
}
|
|
3814
3823
|
async function runProbeCandidates(args) {
|
|
3815
3824
|
try {
|
|
3816
3825
|
if (!shouldSampleProbe(args.cfg.sampleRate ?? 0.05)) return;
|
|
3817
3826
|
const candidates = normalizeProbeCandidates(args.cfg.candidates);
|
|
3818
3827
|
const promptHash = hashForProbe(args.ir.currentTurn?.content ?? "");
|
|
3828
|
+
const budgetMs = args.cfg.maxLatencyMs ?? DEFAULT_PROBE_MAX_LATENCY_MS;
|
|
3829
|
+
const deadline = args.sync ? Date.now() + budgetMs : Number.POSITIVE_INFINITY;
|
|
3830
|
+
const skipSlow = args.sync && (args.cfg.skipSlowTierInSync ?? true);
|
|
3831
|
+
const recordDiagnostic = async (candidate, outcome) => {
|
|
3832
|
+
await recordShadowProbe({
|
|
3833
|
+
appId: args.ir.appId,
|
|
3834
|
+
archetype: args.ir.intent.archetype,
|
|
3835
|
+
family: deriveFamilyFromModelId(candidate) ?? "unknown",
|
|
3836
|
+
candidateModel: candidate,
|
|
3837
|
+
currentModel: args.servedModel,
|
|
3838
|
+
promptHash,
|
|
3839
|
+
// Served leg only — the candidate never produced a comparable response.
|
|
3840
|
+
currentResponsePreview: args.servedResponse.text.slice(0, 2e3),
|
|
3841
|
+
tokensCurrentIn: args.servedResponse.tokens.input,
|
|
3842
|
+
tokensCurrentOut: args.servedResponse.tokens.output,
|
|
3843
|
+
latencyCurrentMs: args.servedLatencyMs,
|
|
3844
|
+
outcome
|
|
3845
|
+
});
|
|
3846
|
+
};
|
|
3819
3847
|
for (const candidate of candidates) {
|
|
3820
3848
|
if (candidate === args.servedModel) continue;
|
|
3821
3849
|
if (deriveFamilyFromModelId(candidate) === "claude-opus") continue;
|
|
3822
3850
|
if (!isModelReachable(candidate, { apiKeys: args.exec.apiKeys })) continue;
|
|
3851
|
+
if (skipSlow && isSlowTierCandidate(candidate)) {
|
|
3852
|
+
try {
|
|
3853
|
+
await recordDiagnostic(candidate, "skipped_slow_tier_sync");
|
|
3854
|
+
} catch {
|
|
3855
|
+
}
|
|
3856
|
+
continue;
|
|
3857
|
+
}
|
|
3858
|
+
if (args.sync && Date.now() >= deadline) {
|
|
3859
|
+
try {
|
|
3860
|
+
await recordDiagnostic(candidate, "aborted_latency_budget");
|
|
3861
|
+
} catch {
|
|
3862
|
+
}
|
|
3863
|
+
break;
|
|
3864
|
+
}
|
|
3823
3865
|
try {
|
|
3824
3866
|
const candCompile = compileAndRegister(
|
|
3825
3867
|
{
|
|
@@ -3830,11 +3872,32 @@ async function runProbeCandidates(args) {
|
|
|
3830
3872
|
args.exec
|
|
3831
3873
|
);
|
|
3832
3874
|
const candStart = Date.now();
|
|
3833
|
-
|
|
3875
|
+
let timer;
|
|
3876
|
+
const execPromise = execute(candCompile.request, {
|
|
3834
3877
|
apiKeys: args.exec.apiKeys,
|
|
3835
3878
|
fetchImpl: args.exec.fetchImpl,
|
|
3836
3879
|
providerOverrides: args.exec.providerOverrides
|
|
3837
|
-
});
|
|
3880
|
+
}).then((exec2) => ({ kind: "exec", exec: exec2 })).catch(() => ({ kind: "failed" }));
|
|
3881
|
+
let raced;
|
|
3882
|
+
if (args.sync) {
|
|
3883
|
+
const remaining = Math.max(0, deadline - Date.now());
|
|
3884
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
3885
|
+
timer = setTimeout(() => resolve({ kind: "timeout" }), remaining);
|
|
3886
|
+
});
|
|
3887
|
+
raced = await Promise.race([execPromise, timeoutPromise]);
|
|
3888
|
+
if (timer) clearTimeout(timer);
|
|
3889
|
+
} else {
|
|
3890
|
+
raced = await execPromise;
|
|
3891
|
+
}
|
|
3892
|
+
if (raced.kind === "timeout") {
|
|
3893
|
+
try {
|
|
3894
|
+
await recordDiagnostic(candidate, "aborted_latency_budget");
|
|
3895
|
+
} catch {
|
|
3896
|
+
}
|
|
3897
|
+
break;
|
|
3898
|
+
}
|
|
3899
|
+
if (raced.kind === "failed") continue;
|
|
3900
|
+
const exec = raced.exec;
|
|
3838
3901
|
const candidateLatencyMs = Date.now() - candStart;
|
|
3839
3902
|
if (!exec.ok) continue;
|
|
3840
3903
|
await recordShadowProbe({
|
|
@@ -3854,7 +3917,8 @@ async function runProbeCandidates(args) {
|
|
|
3854
3917
|
// mirrors the user's actual wait; candidate latency is measured only
|
|
3855
3918
|
// here and stored nowhere else (irrecoverable if not captured now).
|
|
3856
3919
|
latencyCurrentMs: args.servedLatencyMs,
|
|
3857
|
-
latencyCandidateMs: candidateLatencyMs
|
|
3920
|
+
latencyCandidateMs: candidateLatencyMs,
|
|
3921
|
+
outcome: "completed"
|
|
3858
3922
|
});
|
|
3859
3923
|
} catch {
|
|
3860
3924
|
}
|
|
@@ -3872,7 +3936,11 @@ async function runShadowProbe(args) {
|
|
|
3872
3936
|
servedResponse: args.servedResponse,
|
|
3873
3937
|
servedLatencyMs: args.servedLatencyMs,
|
|
3874
3938
|
// CallOptions is a structural superset of ProbeExecOpts.
|
|
3875
|
-
exec: args.opts
|
|
3939
|
+
exec: args.opts,
|
|
3940
|
+
// alpha — the call()-inline path is on the user's critical path exactly when
|
|
3941
|
+
// BrainConfig.sync is set (call() awaits the probe before returning). That's
|
|
3942
|
+
// the only case where the latency budget + slow-tier skip must fire.
|
|
3943
|
+
sync: isBrainSync()
|
|
3876
3944
|
});
|
|
3877
3945
|
}
|
|
3878
3946
|
async function probeShadow(ir, opts) {
|
|
@@ -3905,7 +3973,15 @@ async function probeShadow(ir, opts) {
|
|
|
3905
3973
|
apiKeys: opts.apiKeys,
|
|
3906
3974
|
fetchImpl: opts.fetchImpl,
|
|
3907
3975
|
providerOverrides: opts.providerOverrides
|
|
3908
|
-
}
|
|
3976
|
+
},
|
|
3977
|
+
// alpha — probeShadow() is ALWAYS off the user-facing critical path: the
|
|
3978
|
+
// consumer fires it from `onFinish` wrapped in `after()`/`waitUntil()`,
|
|
3979
|
+
// after the response is flushed. So the sync-mode latency budget +
|
|
3980
|
+
// slow-tier skip are no-ops here — slow candidates run to completion (this
|
|
3981
|
+
// is in fact the right place to evaluate a slow reasoner). The
|
|
3982
|
+
// maxLatencyMs/skipSlowTierInSync knobs aren't even exposed on
|
|
3983
|
+
// ProbeShadowOptions for that reason.
|
|
3984
|
+
sync: false
|
|
3909
3985
|
});
|
|
3910
3986
|
} catch {
|
|
3911
3987
|
}
|
|
@@ -830,6 +830,49 @@ interface ShadowProbeConfig {
|
|
|
830
830
|
judge?: 'opus' | 'off';
|
|
831
831
|
/** Per-(appId, archetype, candidate) daily cap. Phase 2 (needs brain-count read). Default 20. */
|
|
832
832
|
maxPerDay?: number;
|
|
833
|
+
/**
|
|
834
|
+
* alpha — latency budget (ms) for the probe leg. **Only enforced in sync mode**
|
|
835
|
+
* (`BrainConfig.sync === true`), where `call()` awaits the probe before returning
|
|
836
|
+
* the served response (PB-class Edge consumers, L-086) and the candidate's
|
|
837
|
+
* latency therefore lands on the user-facing critical path.
|
|
838
|
+
*
|
|
839
|
+
* The probe path races against this budget: if the candidate(s) don't finish
|
|
840
|
+
* within `maxLatencyMs`, the probe aborts, `call()` returns the already-served
|
|
841
|
+
* response immediately, and an `outcome='aborted_latency_budget'` row is
|
|
842
|
+
* recorded (no verdict, no false quality data). **The user is never delayed
|
|
843
|
+
* past the budget.** Default `15000`.
|
|
844
|
+
*
|
|
845
|
+
* No-op for fire-and-forget consumers (`sync` unset/false, and every
|
|
846
|
+
* `probeShadow()` caller fired from `after()`/`waitUntil()`) — they don't block
|
|
847
|
+
* the user, so there's no latency to bound. The probe runs to completion there.
|
|
848
|
+
*
|
|
849
|
+
* Root cause this closes (PB s57 dogfood incident, 2026-06-05): PB arms the
|
|
850
|
+
* probe inline with `sync:true` on a `maxDuration:120` analyze route; arming a
|
|
851
|
+
* `latency_tier='slow'` candidate (deepseek-v4-pro, ~78–84s) pushed total
|
|
852
|
+
* handler time past the frontend's patience and the served analysis came back
|
|
853
|
+
* empty. A sync probe had no latency budget — this field is the budget.
|
|
854
|
+
*/
|
|
855
|
+
maxLatencyMs?: number;
|
|
856
|
+
/**
|
|
857
|
+
* alpha — when true (the default) **in sync mode**, skip a candidate whose
|
|
858
|
+
* registry `latency_tier === 'slow'` *before starting it*: a slow-tier model
|
|
859
|
+
* (e.g. deepseek-v4-pro / deepseek-v4-flash, both `slow` = ~24s+ p50) cannot
|
|
860
|
+
* fit a sane inline budget, so starting it just to abort it wastes a provider
|
|
861
|
+
* call. An `outcome='skipped_slow_tier_sync'` row is recorded so the offline
|
|
862
|
+
* rollup can see the skip (not a silent drop, not a false verdict). Fail-safe.
|
|
863
|
+
*
|
|
864
|
+
* **The deeper rule (slow-reasoner → offline-mode):** inline shadow-probing is
|
|
865
|
+
* for FAST candidates only. To evaluate a slow reasoner you need the offline /
|
|
866
|
+
* async probe path (the Phase-2 batch direction) which runs off the response
|
|
867
|
+
* entirely — never the inline sync probe. Set this `false` only if you've moved
|
|
868
|
+
* the budget high enough that a slow candidate genuinely fits (rare), or you're
|
|
869
|
+
* in a non-sync consumer where this flag is a no-op anyway.
|
|
870
|
+
*
|
|
871
|
+
* No-op for fire-and-forget consumers (`sync` unset/false; `probeShadow()`):
|
|
872
|
+
* there's no user latency to protect, so slow candidates run normally. Default
|
|
873
|
+
* `true`.
|
|
874
|
+
*/
|
|
875
|
+
skipSlowTierInSync?: boolean;
|
|
833
876
|
}
|
|
834
877
|
interface CallOptions {
|
|
835
878
|
/** Forwarded to compile(). */
|
|
@@ -830,6 +830,49 @@ interface ShadowProbeConfig {
|
|
|
830
830
|
judge?: 'opus' | 'off';
|
|
831
831
|
/** Per-(appId, archetype, candidate) daily cap. Phase 2 (needs brain-count read). Default 20. */
|
|
832
832
|
maxPerDay?: number;
|
|
833
|
+
/**
|
|
834
|
+
* alpha — latency budget (ms) for the probe leg. **Only enforced in sync mode**
|
|
835
|
+
* (`BrainConfig.sync === true`), where `call()` awaits the probe before returning
|
|
836
|
+
* the served response (PB-class Edge consumers, L-086) and the candidate's
|
|
837
|
+
* latency therefore lands on the user-facing critical path.
|
|
838
|
+
*
|
|
839
|
+
* The probe path races against this budget: if the candidate(s) don't finish
|
|
840
|
+
* within `maxLatencyMs`, the probe aborts, `call()` returns the already-served
|
|
841
|
+
* response immediately, and an `outcome='aborted_latency_budget'` row is
|
|
842
|
+
* recorded (no verdict, no false quality data). **The user is never delayed
|
|
843
|
+
* past the budget.** Default `15000`.
|
|
844
|
+
*
|
|
845
|
+
* No-op for fire-and-forget consumers (`sync` unset/false, and every
|
|
846
|
+
* `probeShadow()` caller fired from `after()`/`waitUntil()`) — they don't block
|
|
847
|
+
* the user, so there's no latency to bound. The probe runs to completion there.
|
|
848
|
+
*
|
|
849
|
+
* Root cause this closes (PB s57 dogfood incident, 2026-06-05): PB arms the
|
|
850
|
+
* probe inline with `sync:true` on a `maxDuration:120` analyze route; arming a
|
|
851
|
+
* `latency_tier='slow'` candidate (deepseek-v4-pro, ~78–84s) pushed total
|
|
852
|
+
* handler time past the frontend's patience and the served analysis came back
|
|
853
|
+
* empty. A sync probe had no latency budget — this field is the budget.
|
|
854
|
+
*/
|
|
855
|
+
maxLatencyMs?: number;
|
|
856
|
+
/**
|
|
857
|
+
* alpha — when true (the default) **in sync mode**, skip a candidate whose
|
|
858
|
+
* registry `latency_tier === 'slow'` *before starting it*: a slow-tier model
|
|
859
|
+
* (e.g. deepseek-v4-pro / deepseek-v4-flash, both `slow` = ~24s+ p50) cannot
|
|
860
|
+
* fit a sane inline budget, so starting it just to abort it wastes a provider
|
|
861
|
+
* call. An `outcome='skipped_slow_tier_sync'` row is recorded so the offline
|
|
862
|
+
* rollup can see the skip (not a silent drop, not a false verdict). Fail-safe.
|
|
863
|
+
*
|
|
864
|
+
* **The deeper rule (slow-reasoner → offline-mode):** inline shadow-probing is
|
|
865
|
+
* for FAST candidates only. To evaluate a slow reasoner you need the offline /
|
|
866
|
+
* async probe path (the Phase-2 batch direction) which runs off the response
|
|
867
|
+
* entirely — never the inline sync probe. Set this `false` only if you've moved
|
|
868
|
+
* the budget high enough that a slow candidate genuinely fits (rare), or you're
|
|
869
|
+
* in a non-sync consumer where this flag is a no-op anyway.
|
|
870
|
+
*
|
|
871
|
+
* No-op for fire-and-forget consumers (`sync` unset/false; `probeShadow()`):
|
|
872
|
+
* there's no user latency to protect, so slow candidates run normally. Default
|
|
873
|
+
* `true`.
|
|
874
|
+
*/
|
|
875
|
+
skipSlowTierInSync?: boolean;
|
|
833
876
|
}
|
|
834
877
|
interface CallOptions {
|
|
835
878
|
/** Forwarded to compile(). */
|
package/dist/profiles.d.mts
CHANGED
package/dist/profiles.d.ts
CHANGED