@warmdrift/kgauto-compiler 2.0.0-alpha.75 → 2.0.0-alpha.77
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/dist/{chunk-WP22F3CX.mjs → chunk-GB7VJQ6C.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 +1 -0
- package/dist/glassbox-routes/index.mjs +1 -0
- 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 +141 -5
- package/dist/index.d.ts +141 -5
- package/dist/index.js +256 -8
- package/dist/index.mjs +252 -8
- package/dist/{ir-B0v2f9NY.d.ts → ir-C02EIElQ.d.ts} +16 -6
- package/dist/{ir-CZukZvDn.d.mts → ir-CRV1_taF.d.mts} +16 -6
- 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-Ba68lIs1.d.ts → types-3WdGMqin.d.mts} +2 -9
- package/dist/{types-D-PuKCWk.d.ts → types-C5BX9bzi.d.ts} +1 -1
- package/dist/{types-BgJ5iQB_.d.mts → types-Caq-hOIj.d.mts} +1 -1
- package/dist/{types-BOTLfJOp.d.mts → types-CkRvjxrI.d.ts} +2 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25,7 +25,10 @@ __export(index_exports, {
|
|
|
25
25
|
ALL_ARCHETYPES: () => ALL_ARCHETYPES,
|
|
26
26
|
ARCHETYPE_FAMILY_FITS: () => ARCHETYPE_FAMILY_FITS,
|
|
27
27
|
ARCHETYPE_FLOOR_DEFAULT: () => ARCHETYPE_FLOOR_DEFAULT,
|
|
28
|
+
COACH_CFG: () => COACH_CFG,
|
|
28
29
|
CallError: () => CallError,
|
|
30
|
+
DECOMPOSITION_TEMPLATES: () => DECOMPOSITION_TEMPLATES,
|
|
31
|
+
DECOMPOSITION_TEMPLATES_VERSION: () => DECOMPOSITION_TEMPLATES_VERSION,
|
|
29
32
|
DEFAULT_FINDINGS_ENDPOINT: () => DEFAULT_FINDINGS_ENDPOINT,
|
|
30
33
|
DEFAULT_MEASURED_FAILURE_ENDPOINT: () => DEFAULT_MEASURED_FAILURE_ENDPOINT,
|
|
31
34
|
DEFAULT_PROMOTIONS_ENDPOINT: () => DEFAULT_PROMOTIONS_ENDPOINT,
|
|
@@ -125,6 +128,7 @@ __export(index_exports, {
|
|
|
125
128
|
parseGoldenCaptureRate: () => parseGoldenCaptureRate,
|
|
126
129
|
parseJudgeVerdict: () => parseJudgeVerdict,
|
|
127
130
|
peekBrainDeadLetter: () => peekBrainDeadLetter,
|
|
131
|
+
planDecomposition: () => planDecomposition,
|
|
128
132
|
prefetchMeasuredFailure: () => prefetchMeasuredFailure,
|
|
129
133
|
probeShadow: () => probeShadow,
|
|
130
134
|
profileToRow: () => profileToRow,
|
|
@@ -6422,8 +6426,33 @@ function isAuthSignatureBody(body, message) {
|
|
|
6422
6426
|
const m = message.toLowerCase();
|
|
6423
6427
|
return m.includes("api key not valid") || m.includes("invalid api key") || m.includes("invalid x-api-key") || m.includes("incorrect api key");
|
|
6424
6428
|
}
|
|
6429
|
+
function isBillingExhaustedBody(status, body, message) {
|
|
6430
|
+
if (status === 402) return true;
|
|
6431
|
+
const m = message.toLowerCase();
|
|
6432
|
+
if (m.includes("free_tier") || m.includes("free tier")) return false;
|
|
6433
|
+
if (m.includes("credit balance is too low")) return true;
|
|
6434
|
+
if (m.includes("insufficient balance")) return true;
|
|
6435
|
+
if (m.includes("billingnotenabled") || m.includes("billing not enabled")) return true;
|
|
6436
|
+
if (body && typeof body === "object") {
|
|
6437
|
+
const err = body.error;
|
|
6438
|
+
if (err && typeof err === "object") {
|
|
6439
|
+
const e = err;
|
|
6440
|
+
if (e.code === "insufficient_quota" || e.type === "insufficient_quota") return true;
|
|
6441
|
+
if (Array.isArray(e.details)) {
|
|
6442
|
+
for (const d of e.details) {
|
|
6443
|
+
if (d && typeof d === "object" && d.reason === "BILLING_DISABLED") {
|
|
6444
|
+
return true;
|
|
6445
|
+
}
|
|
6446
|
+
}
|
|
6447
|
+
}
|
|
6448
|
+
}
|
|
6449
|
+
}
|
|
6450
|
+
return false;
|
|
6451
|
+
}
|
|
6425
6452
|
function classifyHttpError(status, body) {
|
|
6426
6453
|
const message = extractErrorMessage(body) ?? `HTTP ${status}`;
|
|
6454
|
+
if (isBillingExhaustedBody(status, body, message))
|
|
6455
|
+
return { ok: false, status, errorType: "terminal", errorCode: "billing_exhausted", message, raw: body };
|
|
6427
6456
|
if (status === 429)
|
|
6428
6457
|
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
6429
6458
|
if (status === 408)
|
|
@@ -6687,6 +6716,9 @@ function applyOverrides(request, overrides) {
|
|
|
6687
6716
|
}
|
|
6688
6717
|
function classifyHttpError2(status, body) {
|
|
6689
6718
|
const message = extractErrorMessage2(body) ?? `HTTP ${status}`;
|
|
6719
|
+
if (isBillingExhaustedBody(status, body, message)) {
|
|
6720
|
+
return { ok: false, status, errorType: "terminal", errorCode: "billing_exhausted", message, raw: body };
|
|
6721
|
+
}
|
|
6690
6722
|
if (status === 429) {
|
|
6691
6723
|
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
6692
6724
|
}
|
|
@@ -7529,18 +7561,19 @@ async function call(ir, opts = {}) {
|
|
|
7529
7561
|
}
|
|
7530
7562
|
let activeCompile = initial;
|
|
7531
7563
|
let lastErr;
|
|
7532
|
-
const failedProviders = /* @__PURE__ */ new
|
|
7564
|
+
const failedProviders = /* @__PURE__ */ new Map();
|
|
7533
7565
|
const sameModelRetryEnabled = opts.sameModelRetry ?? isSameModelRetryEnabledFromEnv();
|
|
7534
7566
|
let retriedSameModel = false;
|
|
7535
7567
|
for (let i = 0; i < targetsToTry.length; i++) {
|
|
7536
7568
|
const targetModel = targetsToTry[i];
|
|
7537
7569
|
const targetProfile = tryGetProfile(targetModel);
|
|
7538
|
-
|
|
7570
|
+
const providerFailReason = targetProfile ? failedProviders.get(targetProfile.provider) : void 0;
|
|
7571
|
+
if (targetProfile && providerFailReason && !opts.noFallback) {
|
|
7539
7572
|
attempts.push({
|
|
7540
7573
|
model: targetModel,
|
|
7541
7574
|
status: "terminal",
|
|
7542
|
-
errorCode:
|
|
7543
|
-
message: `Skipped \u2014 provider ${targetProfile.provider} returned 401/403 earlier in this call; same key inferred to fail`
|
|
7575
|
+
errorCode: `${providerFailReason}_inferred`,
|
|
7576
|
+
message: providerFailReason === "billing_exhausted" ? `Skipped \u2014 provider ${targetProfile.provider} is out of credits (seen earlier in this call); a billing failure is account-wide, so this attempt cannot succeed until a human funds the account` : `Skipped \u2014 provider ${targetProfile.provider} returned 401/403 earlier in this call; same key inferred to fail`
|
|
7544
7577
|
});
|
|
7545
7578
|
continue;
|
|
7546
7579
|
}
|
|
@@ -7722,15 +7755,18 @@ async function call(ir, opts = {}) {
|
|
|
7722
7755
|
});
|
|
7723
7756
|
lastErr = validated;
|
|
7724
7757
|
if (validated.errorType === "terminal" || opts.noFallback) {
|
|
7725
|
-
if (validated.errorCode === "auth" && !opts.noFallback && activeCompile.provider) {
|
|
7726
|
-
failedProviders.
|
|
7758
|
+
if ((validated.errorCode === "auth" || validated.errorCode === "billing_exhausted") && !opts.noFallback && activeCompile.provider) {
|
|
7759
|
+
failedProviders.set(
|
|
7760
|
+
activeCompile.provider,
|
|
7761
|
+
validated.errorCode === "billing_exhausted" ? "billing_exhausted" : "auth"
|
|
7762
|
+
);
|
|
7727
7763
|
continue;
|
|
7728
7764
|
}
|
|
7729
7765
|
break;
|
|
7730
7766
|
}
|
|
7731
7767
|
}
|
|
7732
7768
|
const lastAttempted = [...attempts].reverse().find(
|
|
7733
|
-
(a) => a.status !== "success" && a.errorCode !== "auth_inferred" && a.errorCode !== "compile_error"
|
|
7769
|
+
(a) => a.status !== "success" && a.errorCode !== "auth_inferred" && a.errorCode !== "billing_exhausted_inferred" && a.errorCode !== "compile_error"
|
|
7734
7770
|
);
|
|
7735
7771
|
const latencyMs = Date.now() - start;
|
|
7736
7772
|
await record({
|
|
@@ -8052,6 +8088,9 @@ function normalizeFallbackReason(attempts) {
|
|
|
8052
8088
|
}
|
|
8053
8089
|
if (code === "cost_cap_exceeded") return "cost_cap";
|
|
8054
8090
|
if (code === "auth" || code === "auth_inferred") return "provider_auth_failed";
|
|
8091
|
+
if (code === "billing_exhausted" || code === "billing_exhausted_inferred") {
|
|
8092
|
+
return "provider_billing_exhausted";
|
|
8093
|
+
}
|
|
8055
8094
|
return "provider_error";
|
|
8056
8095
|
}
|
|
8057
8096
|
function generateTraceId() {
|
|
@@ -9039,7 +9078,7 @@ function createBrainForwardRoutes(config) {
|
|
|
9039
9078
|
}
|
|
9040
9079
|
|
|
9041
9080
|
// src/version.ts
|
|
9042
|
-
var LIBRARY_VERSION = "2.0.0-alpha.
|
|
9081
|
+
var LIBRARY_VERSION = "2.0.0-alpha.77";
|
|
9043
9082
|
|
|
9044
9083
|
// src/key-health.ts
|
|
9045
9084
|
var JSON_HEADERS2 = { "Content-Type": "application/json" };
|
|
@@ -9657,6 +9696,211 @@ async function markExclusionFindingHandled(opts) {
|
|
|
9657
9696
|
return { ok: true };
|
|
9658
9697
|
}
|
|
9659
9698
|
|
|
9699
|
+
// src/decomposition.ts
|
|
9700
|
+
var DECOMPOSITION_TEMPLATES_VERSION = "decomposition-templates-v1";
|
|
9701
|
+
var DECOMPOSITION_TEMPLATES = {
|
|
9702
|
+
summarize: {
|
|
9703
|
+
archetype: "summarize",
|
|
9704
|
+
version: DECOMPOSITION_TEMPLATES_VERSION,
|
|
9705
|
+
rationale: "A long-input summarize is mostly reading. A cheap extractor reads the full payload and emits compressed notes; the incumbent composes the summary from the notes \u2014 frontier tokens are spent only on the part that needs frontier judgment.",
|
|
9706
|
+
steps: [
|
|
9707
|
+
{
|
|
9708
|
+
role: "chunk-extract",
|
|
9709
|
+
archetype: "extract",
|
|
9710
|
+
tier: "delegate",
|
|
9711
|
+
inputShare: 1,
|
|
9712
|
+
emitsShareOfInput: 0.15,
|
|
9713
|
+
outputShare: 0
|
|
9714
|
+
},
|
|
9715
|
+
{
|
|
9716
|
+
role: "compose",
|
|
9717
|
+
archetype: "summarize",
|
|
9718
|
+
tier: "anchor",
|
|
9719
|
+
inputShare: 0.15,
|
|
9720
|
+
// reads the notes, not the raw payload
|
|
9721
|
+
emitsShareOfInput: 0,
|
|
9722
|
+
outputShare: 1
|
|
9723
|
+
}
|
|
9724
|
+
]
|
|
9725
|
+
},
|
|
9726
|
+
hunt: {
|
|
9727
|
+
archetype: "hunt",
|
|
9728
|
+
version: DECOMPOSITION_TEMPLATES_VERSION,
|
|
9729
|
+
rationale: "Hunt decomposes into breadth (search sweeps), mechanical harvesting (extraction), and judgment (dedupe + compose). The sweeps and the harvest are grunt work; the composition anchors on the incumbent.",
|
|
9730
|
+
steps: [
|
|
9731
|
+
{
|
|
9732
|
+
role: "search-sweep",
|
|
9733
|
+
archetype: "hunt",
|
|
9734
|
+
tier: "delegate",
|
|
9735
|
+
inputShare: 0.5,
|
|
9736
|
+
emitsShareOfInput: 0.2,
|
|
9737
|
+
outputShare: 0
|
|
9738
|
+
},
|
|
9739
|
+
{
|
|
9740
|
+
role: "harvest",
|
|
9741
|
+
archetype: "extract",
|
|
9742
|
+
tier: "delegate",
|
|
9743
|
+
inputShare: 0.35,
|
|
9744
|
+
emitsShareOfInput: 0.1,
|
|
9745
|
+
outputShare: 0
|
|
9746
|
+
},
|
|
9747
|
+
{
|
|
9748
|
+
role: "dedupe-compose",
|
|
9749
|
+
archetype: "judge",
|
|
9750
|
+
tier: "anchor",
|
|
9751
|
+
inputShare: 0.3,
|
|
9752
|
+
// sweep notes + harvest notes
|
|
9753
|
+
emitsShareOfInput: 0,
|
|
9754
|
+
outputShare: 1
|
|
9755
|
+
}
|
|
9756
|
+
]
|
|
9757
|
+
},
|
|
9758
|
+
plan: {
|
|
9759
|
+
archetype: "plan",
|
|
9760
|
+
version: DECOMPOSITION_TEMPLATES_VERSION,
|
|
9761
|
+
rationale: "Planning splits into context-gathering (mechanical reading) and the plan itself (judgment). The gatherer reads the corpus and briefs; the incumbent plans from the brief.",
|
|
9762
|
+
steps: [
|
|
9763
|
+
{
|
|
9764
|
+
role: "gather-brief",
|
|
9765
|
+
archetype: "extract",
|
|
9766
|
+
tier: "delegate",
|
|
9767
|
+
inputShare: 1,
|
|
9768
|
+
emitsShareOfInput: 0.2,
|
|
9769
|
+
outputShare: 0
|
|
9770
|
+
},
|
|
9771
|
+
{
|
|
9772
|
+
role: "draft-plan",
|
|
9773
|
+
archetype: "plan",
|
|
9774
|
+
tier: "anchor",
|
|
9775
|
+
inputShare: 0.2,
|
|
9776
|
+
emitsShareOfInput: 0,
|
|
9777
|
+
outputShare: 1
|
|
9778
|
+
}
|
|
9779
|
+
]
|
|
9780
|
+
}
|
|
9781
|
+
};
|
|
9782
|
+
var COACH_CFG = {
|
|
9783
|
+
/** Same felt-utility floor as promotions (alpha.67 family). */
|
|
9784
|
+
minMonthlySavingUsd: 5,
|
|
9785
|
+
/** Executor must clear this archetypePerf on the step's archetype —
|
|
9786
|
+
* same floor as the translator/advisor (ARCHETYPE_FLOOR_DEFAULT). */
|
|
9787
|
+
executorPerfFloor: 6,
|
|
9788
|
+
daysPerMonth: 30
|
|
9789
|
+
};
|
|
9790
|
+
function planDecomposition(args) {
|
|
9791
|
+
const { stats, incumbentPricing, pickExecutor } = args;
|
|
9792
|
+
const cfg = { ...COACH_CFG, ...args.cfg ?? {} };
|
|
9793
|
+
const template = DECOMPOSITION_TEMPLATES[stats.archetype];
|
|
9794
|
+
if (!template) return void 0;
|
|
9795
|
+
const perCall = (pricing, tokensIn, tokensOut) => tokensIn / 1e6 * pricing.costInputPer1m + tokensOut / 1e6 * pricing.costOutputPer1m;
|
|
9796
|
+
const monolithCostPerCallUsd = perCall(
|
|
9797
|
+
incumbentPricing,
|
|
9798
|
+
stats.avgTokensIn,
|
|
9799
|
+
stats.avgTokensOut
|
|
9800
|
+
);
|
|
9801
|
+
const assumptions = [
|
|
9802
|
+
`per-step token shares are ${template.version} JUDGMENT numbers \u2014 no fan-out traffic exists to measure them from yet; they graduate per surface when branch traffic lands`
|
|
9803
|
+
];
|
|
9804
|
+
const steps = [];
|
|
9805
|
+
let splitCostPerCallUsd = 0;
|
|
9806
|
+
for (const step of template.steps) {
|
|
9807
|
+
const stepTokensIn = stats.avgTokensIn * step.inputShare;
|
|
9808
|
+
const stepTokensOut = stats.avgTokensIn * step.emitsShareOfInput + stats.avgTokensOut * step.outputShare;
|
|
9809
|
+
if (step.tier === "anchor") {
|
|
9810
|
+
const cost2 = perCall(incumbentPricing, stepTokensIn, stepTokensOut);
|
|
9811
|
+
splitCostPerCallUsd += cost2;
|
|
9812
|
+
steps.push({
|
|
9813
|
+
...step,
|
|
9814
|
+
executorModel: stats.incumbentModel,
|
|
9815
|
+
executorGrounding: "measured",
|
|
9816
|
+
// the incumbent IS the measured baseline
|
|
9817
|
+
executorPerfScore: null,
|
|
9818
|
+
projectedCostPerCallUsd: cost2
|
|
9819
|
+
});
|
|
9820
|
+
continue;
|
|
9821
|
+
}
|
|
9822
|
+
const candidate = pickExecutor(step.archetype);
|
|
9823
|
+
if (candidate && candidate.modelId === stats.incumbentModel) {
|
|
9824
|
+
const cost2 = perCall(incumbentPricing, stepTokensIn, stepTokensOut);
|
|
9825
|
+
splitCostPerCallUsd += cost2;
|
|
9826
|
+
steps.push({
|
|
9827
|
+
...step,
|
|
9828
|
+
tier: "anchor",
|
|
9829
|
+
executorModel: stats.incumbentModel,
|
|
9830
|
+
executorGrounding: candidate.grounding,
|
|
9831
|
+
executorPerfScore: candidate.perfScore,
|
|
9832
|
+
projectedCostPerCallUsd: cost2
|
|
9833
|
+
});
|
|
9834
|
+
assumptions.push(
|
|
9835
|
+
`step '${step.role}': the cheapest qualified executor IS the incumbent \u2014 nothing to delegate to`
|
|
9836
|
+
);
|
|
9837
|
+
continue;
|
|
9838
|
+
}
|
|
9839
|
+
if (!candidate || candidate.perfScore < cfg.executorPerfFloor) {
|
|
9840
|
+
const cost2 = perCall(incumbentPricing, stepTokensIn, stepTokensOut);
|
|
9841
|
+
splitCostPerCallUsd += cost2;
|
|
9842
|
+
steps.push({
|
|
9843
|
+
...step,
|
|
9844
|
+
tier: "anchor",
|
|
9845
|
+
executorModel: stats.incumbentModel,
|
|
9846
|
+
executorGrounding: "measured",
|
|
9847
|
+
executorPerfScore: candidate?.perfScore ?? null,
|
|
9848
|
+
projectedCostPerCallUsd: cost2
|
|
9849
|
+
});
|
|
9850
|
+
assumptions.push(
|
|
9851
|
+
`step '${step.role}' (${step.archetype}): no executor clears the perf floor ${cfg.executorPerfFloor} \u2014 kept on the incumbent`
|
|
9852
|
+
);
|
|
9853
|
+
continue;
|
|
9854
|
+
}
|
|
9855
|
+
const cost = perCall(candidate, stepTokensIn, stepTokensOut);
|
|
9856
|
+
splitCostPerCallUsd += cost;
|
|
9857
|
+
steps.push({
|
|
9858
|
+
...step,
|
|
9859
|
+
executorModel: candidate.modelId,
|
|
9860
|
+
executorGrounding: candidate.grounding,
|
|
9861
|
+
executorPerfScore: candidate.perfScore,
|
|
9862
|
+
projectedCostPerCallUsd: cost
|
|
9863
|
+
});
|
|
9864
|
+
if (candidate.grounding === "judgment") {
|
|
9865
|
+
assumptions.push(
|
|
9866
|
+
`step '${step.role}': ${candidate.modelId} perf ${candidate.perfScore}/10 on ${step.archetype} is JUDGMENT-grounded (no measured portfolio outcomes for the tuple yet)`
|
|
9867
|
+
);
|
|
9868
|
+
}
|
|
9869
|
+
}
|
|
9870
|
+
const savingPerCallUsd = monolithCostPerCallUsd - splitCostPerCallUsd;
|
|
9871
|
+
const monthlyCalls = stats.nCalls / stats.windowDays * cfg.daysPerMonth;
|
|
9872
|
+
const projectedMonthlySavingUsd = savingPerCallUsd * monthlyCalls;
|
|
9873
|
+
const breakEvenMonthlyCalls = savingPerCallUsd > 0 ? cfg.minMonthlySavingUsd / savingPerCallUsd : null;
|
|
9874
|
+
let verdict;
|
|
9875
|
+
let verdictReason;
|
|
9876
|
+
if (savingPerCallUsd <= 0) {
|
|
9877
|
+
verdict = "keep-monolith";
|
|
9878
|
+
verdictReason = "the split costs MORE per call than the monolith at current pricing \u2014 no volume makes it pay";
|
|
9879
|
+
} else if (projectedMonthlySavingUsd < cfg.minMonthlySavingUsd) {
|
|
9880
|
+
verdict = "keep-monolith";
|
|
9881
|
+
verdictReason = `the split pays $${projectedMonthlySavingUsd.toFixed(2)}/mo at your ~${Math.round(monthlyCalls)} calls/mo \u2014 below the $${cfg.minMonthlySavingUsd} felt-utility floor. Break-even is ~${Math.ceil(breakEvenMonthlyCalls ?? 0)} calls/mo; revisit when volume gets there. Decomposition adds moving parts, and a saving you can't feel doesn't buy them`;
|
|
9882
|
+
} else {
|
|
9883
|
+
verdict = "split-pays";
|
|
9884
|
+
verdictReason = `projected $${projectedMonthlySavingUsd.toFixed(2)}/mo saving at ~${Math.round(monthlyCalls)} calls/mo (monolith $${monolithCostPerCallUsd.toFixed(4)}/call \u2192 split $${splitCostPerCallUsd.toFixed(4)}/call)`;
|
|
9885
|
+
}
|
|
9886
|
+
return {
|
|
9887
|
+
appId: stats.appId,
|
|
9888
|
+
archetype: String(stats.archetype),
|
|
9889
|
+
incumbentModel: stats.incumbentModel,
|
|
9890
|
+
template,
|
|
9891
|
+
steps,
|
|
9892
|
+
monolithCostPerCallUsd,
|
|
9893
|
+
splitCostPerCallUsd,
|
|
9894
|
+
savingPerCallUsd,
|
|
9895
|
+
monthlyCalls,
|
|
9896
|
+
projectedMonthlySavingUsd,
|
|
9897
|
+
breakEvenMonthlyCalls,
|
|
9898
|
+
verdict,
|
|
9899
|
+
verdictReason,
|
|
9900
|
+
assumptions
|
|
9901
|
+
};
|
|
9902
|
+
}
|
|
9903
|
+
|
|
9660
9904
|
// src/index.ts
|
|
9661
9905
|
function compile2(ir, opts) {
|
|
9662
9906
|
const result = compile(ir, opts);
|
|
@@ -9670,7 +9914,10 @@ function compile2(ir, opts) {
|
|
|
9670
9914
|
ALL_ARCHETYPES,
|
|
9671
9915
|
ARCHETYPE_FAMILY_FITS,
|
|
9672
9916
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
9917
|
+
COACH_CFG,
|
|
9673
9918
|
CallError,
|
|
9919
|
+
DECOMPOSITION_TEMPLATES,
|
|
9920
|
+
DECOMPOSITION_TEMPLATES_VERSION,
|
|
9674
9921
|
DEFAULT_FINDINGS_ENDPOINT,
|
|
9675
9922
|
DEFAULT_MEASURED_FAILURE_ENDPOINT,
|
|
9676
9923
|
DEFAULT_PROMOTIONS_ENDPOINT,
|
|
@@ -9770,6 +10017,7 @@ function compile2(ir, opts) {
|
|
|
9770
10017
|
parseGoldenCaptureRate,
|
|
9771
10018
|
parseJudgeVerdict,
|
|
9772
10019
|
peekBrainDeadLetter,
|
|
10020
|
+
planDecomposition,
|
|
9773
10021
|
prefetchMeasuredFailure,
|
|
9774
10022
|
probeShadow,
|
|
9775
10023
|
profileToRow,
|
package/dist/index.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
LIBRARY_VERSION,
|
|
18
18
|
createKeyHealthRoute
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-GB7VJQ6C.mjs";
|
|
20
20
|
import {
|
|
21
21
|
ABSOLUTE_FLOOR,
|
|
22
22
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
@@ -4222,8 +4222,33 @@ function isAuthSignatureBody(body, message) {
|
|
|
4222
4222
|
const m = message.toLowerCase();
|
|
4223
4223
|
return m.includes("api key not valid") || m.includes("invalid api key") || m.includes("invalid x-api-key") || m.includes("incorrect api key");
|
|
4224
4224
|
}
|
|
4225
|
+
function isBillingExhaustedBody(status, body, message) {
|
|
4226
|
+
if (status === 402) return true;
|
|
4227
|
+
const m = message.toLowerCase();
|
|
4228
|
+
if (m.includes("free_tier") || m.includes("free tier")) return false;
|
|
4229
|
+
if (m.includes("credit balance is too low")) return true;
|
|
4230
|
+
if (m.includes("insufficient balance")) return true;
|
|
4231
|
+
if (m.includes("billingnotenabled") || m.includes("billing not enabled")) return true;
|
|
4232
|
+
if (body && typeof body === "object") {
|
|
4233
|
+
const err = body.error;
|
|
4234
|
+
if (err && typeof err === "object") {
|
|
4235
|
+
const e = err;
|
|
4236
|
+
if (e.code === "insufficient_quota" || e.type === "insufficient_quota") return true;
|
|
4237
|
+
if (Array.isArray(e.details)) {
|
|
4238
|
+
for (const d of e.details) {
|
|
4239
|
+
if (d && typeof d === "object" && d.reason === "BILLING_DISABLED") {
|
|
4240
|
+
return true;
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
return false;
|
|
4247
|
+
}
|
|
4225
4248
|
function classifyHttpError(status, body) {
|
|
4226
4249
|
const message = extractErrorMessage(body) ?? `HTTP ${status}`;
|
|
4250
|
+
if (isBillingExhaustedBody(status, body, message))
|
|
4251
|
+
return { ok: false, status, errorType: "terminal", errorCode: "billing_exhausted", message, raw: body };
|
|
4227
4252
|
if (status === 429)
|
|
4228
4253
|
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
4229
4254
|
if (status === 408)
|
|
@@ -4487,6 +4512,9 @@ function applyOverrides(request, overrides) {
|
|
|
4487
4512
|
}
|
|
4488
4513
|
function classifyHttpError2(status, body) {
|
|
4489
4514
|
const message = extractErrorMessage2(body) ?? `HTTP ${status}`;
|
|
4515
|
+
if (isBillingExhaustedBody(status, body, message)) {
|
|
4516
|
+
return { ok: false, status, errorType: "terminal", errorCode: "billing_exhausted", message, raw: body };
|
|
4517
|
+
}
|
|
4490
4518
|
if (status === 429) {
|
|
4491
4519
|
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
4492
4520
|
}
|
|
@@ -4675,18 +4703,19 @@ async function call(ir, opts = {}) {
|
|
|
4675
4703
|
}
|
|
4676
4704
|
let activeCompile = initial;
|
|
4677
4705
|
let lastErr;
|
|
4678
|
-
const failedProviders = /* @__PURE__ */ new
|
|
4706
|
+
const failedProviders = /* @__PURE__ */ new Map();
|
|
4679
4707
|
const sameModelRetryEnabled = opts.sameModelRetry ?? isSameModelRetryEnabledFromEnv();
|
|
4680
4708
|
let retriedSameModel = false;
|
|
4681
4709
|
for (let i = 0; i < targetsToTry.length; i++) {
|
|
4682
4710
|
const targetModel = targetsToTry[i];
|
|
4683
4711
|
const targetProfile = tryGetProfile(targetModel);
|
|
4684
|
-
|
|
4712
|
+
const providerFailReason = targetProfile ? failedProviders.get(targetProfile.provider) : void 0;
|
|
4713
|
+
if (targetProfile && providerFailReason && !opts.noFallback) {
|
|
4685
4714
|
attempts.push({
|
|
4686
4715
|
model: targetModel,
|
|
4687
4716
|
status: "terminal",
|
|
4688
|
-
errorCode:
|
|
4689
|
-
message: `Skipped \u2014 provider ${targetProfile.provider} returned 401/403 earlier in this call; same key inferred to fail`
|
|
4717
|
+
errorCode: `${providerFailReason}_inferred`,
|
|
4718
|
+
message: providerFailReason === "billing_exhausted" ? `Skipped \u2014 provider ${targetProfile.provider} is out of credits (seen earlier in this call); a billing failure is account-wide, so this attempt cannot succeed until a human funds the account` : `Skipped \u2014 provider ${targetProfile.provider} returned 401/403 earlier in this call; same key inferred to fail`
|
|
4690
4719
|
});
|
|
4691
4720
|
continue;
|
|
4692
4721
|
}
|
|
@@ -4868,15 +4897,18 @@ async function call(ir, opts = {}) {
|
|
|
4868
4897
|
});
|
|
4869
4898
|
lastErr = validated;
|
|
4870
4899
|
if (validated.errorType === "terminal" || opts.noFallback) {
|
|
4871
|
-
if (validated.errorCode === "auth" && !opts.noFallback && activeCompile.provider) {
|
|
4872
|
-
failedProviders.
|
|
4900
|
+
if ((validated.errorCode === "auth" || validated.errorCode === "billing_exhausted") && !opts.noFallback && activeCompile.provider) {
|
|
4901
|
+
failedProviders.set(
|
|
4902
|
+
activeCompile.provider,
|
|
4903
|
+
validated.errorCode === "billing_exhausted" ? "billing_exhausted" : "auth"
|
|
4904
|
+
);
|
|
4873
4905
|
continue;
|
|
4874
4906
|
}
|
|
4875
4907
|
break;
|
|
4876
4908
|
}
|
|
4877
4909
|
}
|
|
4878
4910
|
const lastAttempted = [...attempts].reverse().find(
|
|
4879
|
-
(a) => a.status !== "success" && a.errorCode !== "auth_inferred" && a.errorCode !== "compile_error"
|
|
4911
|
+
(a) => a.status !== "success" && a.errorCode !== "auth_inferred" && a.errorCode !== "billing_exhausted_inferred" && a.errorCode !== "compile_error"
|
|
4880
4912
|
);
|
|
4881
4913
|
const latencyMs = Date.now() - start;
|
|
4882
4914
|
await record({
|
|
@@ -5198,6 +5230,9 @@ function normalizeFallbackReason(attempts) {
|
|
|
5198
5230
|
}
|
|
5199
5231
|
if (code === "cost_cap_exceeded") return "cost_cap";
|
|
5200
5232
|
if (code === "auth" || code === "auth_inferred") return "provider_auth_failed";
|
|
5233
|
+
if (code === "billing_exhausted" || code === "billing_exhausted_inferred") {
|
|
5234
|
+
return "provider_billing_exhausted";
|
|
5235
|
+
}
|
|
5201
5236
|
return "provider_error";
|
|
5202
5237
|
}
|
|
5203
5238
|
function generateTraceId() {
|
|
@@ -6438,6 +6473,211 @@ async function markExclusionFindingHandled(opts) {
|
|
|
6438
6473
|
return { ok: true };
|
|
6439
6474
|
}
|
|
6440
6475
|
|
|
6476
|
+
// src/decomposition.ts
|
|
6477
|
+
var DECOMPOSITION_TEMPLATES_VERSION = "decomposition-templates-v1";
|
|
6478
|
+
var DECOMPOSITION_TEMPLATES = {
|
|
6479
|
+
summarize: {
|
|
6480
|
+
archetype: "summarize",
|
|
6481
|
+
version: DECOMPOSITION_TEMPLATES_VERSION,
|
|
6482
|
+
rationale: "A long-input summarize is mostly reading. A cheap extractor reads the full payload and emits compressed notes; the incumbent composes the summary from the notes \u2014 frontier tokens are spent only on the part that needs frontier judgment.",
|
|
6483
|
+
steps: [
|
|
6484
|
+
{
|
|
6485
|
+
role: "chunk-extract",
|
|
6486
|
+
archetype: "extract",
|
|
6487
|
+
tier: "delegate",
|
|
6488
|
+
inputShare: 1,
|
|
6489
|
+
emitsShareOfInput: 0.15,
|
|
6490
|
+
outputShare: 0
|
|
6491
|
+
},
|
|
6492
|
+
{
|
|
6493
|
+
role: "compose",
|
|
6494
|
+
archetype: "summarize",
|
|
6495
|
+
tier: "anchor",
|
|
6496
|
+
inputShare: 0.15,
|
|
6497
|
+
// reads the notes, not the raw payload
|
|
6498
|
+
emitsShareOfInput: 0,
|
|
6499
|
+
outputShare: 1
|
|
6500
|
+
}
|
|
6501
|
+
]
|
|
6502
|
+
},
|
|
6503
|
+
hunt: {
|
|
6504
|
+
archetype: "hunt",
|
|
6505
|
+
version: DECOMPOSITION_TEMPLATES_VERSION,
|
|
6506
|
+
rationale: "Hunt decomposes into breadth (search sweeps), mechanical harvesting (extraction), and judgment (dedupe + compose). The sweeps and the harvest are grunt work; the composition anchors on the incumbent.",
|
|
6507
|
+
steps: [
|
|
6508
|
+
{
|
|
6509
|
+
role: "search-sweep",
|
|
6510
|
+
archetype: "hunt",
|
|
6511
|
+
tier: "delegate",
|
|
6512
|
+
inputShare: 0.5,
|
|
6513
|
+
emitsShareOfInput: 0.2,
|
|
6514
|
+
outputShare: 0
|
|
6515
|
+
},
|
|
6516
|
+
{
|
|
6517
|
+
role: "harvest",
|
|
6518
|
+
archetype: "extract",
|
|
6519
|
+
tier: "delegate",
|
|
6520
|
+
inputShare: 0.35,
|
|
6521
|
+
emitsShareOfInput: 0.1,
|
|
6522
|
+
outputShare: 0
|
|
6523
|
+
},
|
|
6524
|
+
{
|
|
6525
|
+
role: "dedupe-compose",
|
|
6526
|
+
archetype: "judge",
|
|
6527
|
+
tier: "anchor",
|
|
6528
|
+
inputShare: 0.3,
|
|
6529
|
+
// sweep notes + harvest notes
|
|
6530
|
+
emitsShareOfInput: 0,
|
|
6531
|
+
outputShare: 1
|
|
6532
|
+
}
|
|
6533
|
+
]
|
|
6534
|
+
},
|
|
6535
|
+
plan: {
|
|
6536
|
+
archetype: "plan",
|
|
6537
|
+
version: DECOMPOSITION_TEMPLATES_VERSION,
|
|
6538
|
+
rationale: "Planning splits into context-gathering (mechanical reading) and the plan itself (judgment). The gatherer reads the corpus and briefs; the incumbent plans from the brief.",
|
|
6539
|
+
steps: [
|
|
6540
|
+
{
|
|
6541
|
+
role: "gather-brief",
|
|
6542
|
+
archetype: "extract",
|
|
6543
|
+
tier: "delegate",
|
|
6544
|
+
inputShare: 1,
|
|
6545
|
+
emitsShareOfInput: 0.2,
|
|
6546
|
+
outputShare: 0
|
|
6547
|
+
},
|
|
6548
|
+
{
|
|
6549
|
+
role: "draft-plan",
|
|
6550
|
+
archetype: "plan",
|
|
6551
|
+
tier: "anchor",
|
|
6552
|
+
inputShare: 0.2,
|
|
6553
|
+
emitsShareOfInput: 0,
|
|
6554
|
+
outputShare: 1
|
|
6555
|
+
}
|
|
6556
|
+
]
|
|
6557
|
+
}
|
|
6558
|
+
};
|
|
6559
|
+
var COACH_CFG = {
|
|
6560
|
+
/** Same felt-utility floor as promotions (alpha.67 family). */
|
|
6561
|
+
minMonthlySavingUsd: 5,
|
|
6562
|
+
/** Executor must clear this archetypePerf on the step's archetype —
|
|
6563
|
+
* same floor as the translator/advisor (ARCHETYPE_FLOOR_DEFAULT). */
|
|
6564
|
+
executorPerfFloor: 6,
|
|
6565
|
+
daysPerMonth: 30
|
|
6566
|
+
};
|
|
6567
|
+
function planDecomposition(args) {
|
|
6568
|
+
const { stats, incumbentPricing, pickExecutor } = args;
|
|
6569
|
+
const cfg = { ...COACH_CFG, ...args.cfg ?? {} };
|
|
6570
|
+
const template = DECOMPOSITION_TEMPLATES[stats.archetype];
|
|
6571
|
+
if (!template) return void 0;
|
|
6572
|
+
const perCall = (pricing, tokensIn, tokensOut) => tokensIn / 1e6 * pricing.costInputPer1m + tokensOut / 1e6 * pricing.costOutputPer1m;
|
|
6573
|
+
const monolithCostPerCallUsd = perCall(
|
|
6574
|
+
incumbentPricing,
|
|
6575
|
+
stats.avgTokensIn,
|
|
6576
|
+
stats.avgTokensOut
|
|
6577
|
+
);
|
|
6578
|
+
const assumptions = [
|
|
6579
|
+
`per-step token shares are ${template.version} JUDGMENT numbers \u2014 no fan-out traffic exists to measure them from yet; they graduate per surface when branch traffic lands`
|
|
6580
|
+
];
|
|
6581
|
+
const steps = [];
|
|
6582
|
+
let splitCostPerCallUsd = 0;
|
|
6583
|
+
for (const step of template.steps) {
|
|
6584
|
+
const stepTokensIn = stats.avgTokensIn * step.inputShare;
|
|
6585
|
+
const stepTokensOut = stats.avgTokensIn * step.emitsShareOfInput + stats.avgTokensOut * step.outputShare;
|
|
6586
|
+
if (step.tier === "anchor") {
|
|
6587
|
+
const cost2 = perCall(incumbentPricing, stepTokensIn, stepTokensOut);
|
|
6588
|
+
splitCostPerCallUsd += cost2;
|
|
6589
|
+
steps.push({
|
|
6590
|
+
...step,
|
|
6591
|
+
executorModel: stats.incumbentModel,
|
|
6592
|
+
executorGrounding: "measured",
|
|
6593
|
+
// the incumbent IS the measured baseline
|
|
6594
|
+
executorPerfScore: null,
|
|
6595
|
+
projectedCostPerCallUsd: cost2
|
|
6596
|
+
});
|
|
6597
|
+
continue;
|
|
6598
|
+
}
|
|
6599
|
+
const candidate = pickExecutor(step.archetype);
|
|
6600
|
+
if (candidate && candidate.modelId === stats.incumbentModel) {
|
|
6601
|
+
const cost2 = perCall(incumbentPricing, stepTokensIn, stepTokensOut);
|
|
6602
|
+
splitCostPerCallUsd += cost2;
|
|
6603
|
+
steps.push({
|
|
6604
|
+
...step,
|
|
6605
|
+
tier: "anchor",
|
|
6606
|
+
executorModel: stats.incumbentModel,
|
|
6607
|
+
executorGrounding: candidate.grounding,
|
|
6608
|
+
executorPerfScore: candidate.perfScore,
|
|
6609
|
+
projectedCostPerCallUsd: cost2
|
|
6610
|
+
});
|
|
6611
|
+
assumptions.push(
|
|
6612
|
+
`step '${step.role}': the cheapest qualified executor IS the incumbent \u2014 nothing to delegate to`
|
|
6613
|
+
);
|
|
6614
|
+
continue;
|
|
6615
|
+
}
|
|
6616
|
+
if (!candidate || candidate.perfScore < cfg.executorPerfFloor) {
|
|
6617
|
+
const cost2 = perCall(incumbentPricing, stepTokensIn, stepTokensOut);
|
|
6618
|
+
splitCostPerCallUsd += cost2;
|
|
6619
|
+
steps.push({
|
|
6620
|
+
...step,
|
|
6621
|
+
tier: "anchor",
|
|
6622
|
+
executorModel: stats.incumbentModel,
|
|
6623
|
+
executorGrounding: "measured",
|
|
6624
|
+
executorPerfScore: candidate?.perfScore ?? null,
|
|
6625
|
+
projectedCostPerCallUsd: cost2
|
|
6626
|
+
});
|
|
6627
|
+
assumptions.push(
|
|
6628
|
+
`step '${step.role}' (${step.archetype}): no executor clears the perf floor ${cfg.executorPerfFloor} \u2014 kept on the incumbent`
|
|
6629
|
+
);
|
|
6630
|
+
continue;
|
|
6631
|
+
}
|
|
6632
|
+
const cost = perCall(candidate, stepTokensIn, stepTokensOut);
|
|
6633
|
+
splitCostPerCallUsd += cost;
|
|
6634
|
+
steps.push({
|
|
6635
|
+
...step,
|
|
6636
|
+
executorModel: candidate.modelId,
|
|
6637
|
+
executorGrounding: candidate.grounding,
|
|
6638
|
+
executorPerfScore: candidate.perfScore,
|
|
6639
|
+
projectedCostPerCallUsd: cost
|
|
6640
|
+
});
|
|
6641
|
+
if (candidate.grounding === "judgment") {
|
|
6642
|
+
assumptions.push(
|
|
6643
|
+
`step '${step.role}': ${candidate.modelId} perf ${candidate.perfScore}/10 on ${step.archetype} is JUDGMENT-grounded (no measured portfolio outcomes for the tuple yet)`
|
|
6644
|
+
);
|
|
6645
|
+
}
|
|
6646
|
+
}
|
|
6647
|
+
const savingPerCallUsd = monolithCostPerCallUsd - splitCostPerCallUsd;
|
|
6648
|
+
const monthlyCalls = stats.nCalls / stats.windowDays * cfg.daysPerMonth;
|
|
6649
|
+
const projectedMonthlySavingUsd = savingPerCallUsd * monthlyCalls;
|
|
6650
|
+
const breakEvenMonthlyCalls = savingPerCallUsd > 0 ? cfg.minMonthlySavingUsd / savingPerCallUsd : null;
|
|
6651
|
+
let verdict;
|
|
6652
|
+
let verdictReason;
|
|
6653
|
+
if (savingPerCallUsd <= 0) {
|
|
6654
|
+
verdict = "keep-monolith";
|
|
6655
|
+
verdictReason = "the split costs MORE per call than the monolith at current pricing \u2014 no volume makes it pay";
|
|
6656
|
+
} else if (projectedMonthlySavingUsd < cfg.minMonthlySavingUsd) {
|
|
6657
|
+
verdict = "keep-monolith";
|
|
6658
|
+
verdictReason = `the split pays $${projectedMonthlySavingUsd.toFixed(2)}/mo at your ~${Math.round(monthlyCalls)} calls/mo \u2014 below the $${cfg.minMonthlySavingUsd} felt-utility floor. Break-even is ~${Math.ceil(breakEvenMonthlyCalls ?? 0)} calls/mo; revisit when volume gets there. Decomposition adds moving parts, and a saving you can't feel doesn't buy them`;
|
|
6659
|
+
} else {
|
|
6660
|
+
verdict = "split-pays";
|
|
6661
|
+
verdictReason = `projected $${projectedMonthlySavingUsd.toFixed(2)}/mo saving at ~${Math.round(monthlyCalls)} calls/mo (monolith $${monolithCostPerCallUsd.toFixed(4)}/call \u2192 split $${splitCostPerCallUsd.toFixed(4)}/call)`;
|
|
6662
|
+
}
|
|
6663
|
+
return {
|
|
6664
|
+
appId: stats.appId,
|
|
6665
|
+
archetype: String(stats.archetype),
|
|
6666
|
+
incumbentModel: stats.incumbentModel,
|
|
6667
|
+
template,
|
|
6668
|
+
steps,
|
|
6669
|
+
monolithCostPerCallUsd,
|
|
6670
|
+
splitCostPerCallUsd,
|
|
6671
|
+
savingPerCallUsd,
|
|
6672
|
+
monthlyCalls,
|
|
6673
|
+
projectedMonthlySavingUsd,
|
|
6674
|
+
breakEvenMonthlyCalls,
|
|
6675
|
+
verdict,
|
|
6676
|
+
verdictReason,
|
|
6677
|
+
assumptions
|
|
6678
|
+
};
|
|
6679
|
+
}
|
|
6680
|
+
|
|
6441
6681
|
// src/index.ts
|
|
6442
6682
|
function compile2(ir, opts) {
|
|
6443
6683
|
const result = compile(ir, opts);
|
|
@@ -6450,7 +6690,10 @@ export {
|
|
|
6450
6690
|
ALL_ARCHETYPES,
|
|
6451
6691
|
ARCHETYPE_FAMILY_FITS,
|
|
6452
6692
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
6693
|
+
COACH_CFG,
|
|
6453
6694
|
CallError,
|
|
6695
|
+
DECOMPOSITION_TEMPLATES,
|
|
6696
|
+
DECOMPOSITION_TEMPLATES_VERSION,
|
|
6454
6697
|
DEFAULT_FINDINGS_ENDPOINT,
|
|
6455
6698
|
DEFAULT_MEASURED_FAILURE_ENDPOINT,
|
|
6456
6699
|
DEFAULT_PROMOTIONS_ENDPOINT,
|
|
@@ -6550,6 +6793,7 @@ export {
|
|
|
6550
6793
|
parseGoldenCaptureRate,
|
|
6551
6794
|
parseJudgeVerdict,
|
|
6552
6795
|
peekBrainDeadLetter,
|
|
6796
|
+
planDecomposition,
|
|
6553
6797
|
prefetchMeasuredFailure,
|
|
6554
6798
|
probeShadow,
|
|
6555
6799
|
profileToRow,
|