@warmdrift/kgauto-compiler 2.0.0-alpha.4 → 2.0.0-alpha.42
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 +87 -3
- package/dist/chunk-JQGRWJZO.mjs +1216 -0
- package/dist/chunk-KCNQUMHN.mjs +694 -0
- package/dist/chunk-NBO4R5PC.mjs +313 -0
- package/dist/chunk-P3TOAEG4.mjs +56 -0
- package/dist/chunk-RO22VFIF.mjs +29 -0
- package/dist/glassbox/index.d.mts +59 -0
- package/dist/glassbox/index.d.ts +59 -0
- package/dist/glassbox/index.js +312 -0
- package/dist/glassbox/index.mjs +12 -0
- package/dist/glassbox-routes/format.d.mts +24 -0
- package/dist/glassbox-routes/format.d.ts +24 -0
- package/dist/glassbox-routes/format.js +86 -0
- package/dist/glassbox-routes/format.mjs +18 -0
- package/dist/glassbox-routes/index.d.mts +191 -0
- package/dist/glassbox-routes/index.d.ts +191 -0
- package/dist/glassbox-routes/index.js +2473 -0
- package/dist/glassbox-routes/index.mjs +663 -0
- package/dist/glassbox-routes/react/index.d.mts +74 -0
- package/dist/glassbox-routes/react/index.d.ts +74 -0
- package/dist/glassbox-routes/react/index.js +819 -0
- package/dist/glassbox-routes/react/index.mjs +754 -0
- package/dist/index.d.mts +1796 -9
- package/dist/index.d.ts +1796 -9
- package/dist/index.js +4771 -312
- package/dist/index.mjs +2951 -275
- package/dist/ir-BG70lg9b.d.mts +1229 -0
- package/dist/ir-BmumS1Du.d.ts +1229 -0
- package/dist/profiles.d.mts +164 -2
- package/dist/profiles.d.ts +164 -2
- package/dist/profiles.js +820 -11
- package/dist/profiles.mjs +5 -1
- package/dist/types-Cs_hsBGm.d.ts +131 -0
- package/dist/types-DIl0tCDX.d.mts +149 -0
- package/dist/types-eKKDeADI.d.mts +131 -0
- package/dist/types-kFsIDC_5.d.ts +149 -0
- package/package.json +42 -6
- package/dist/chunk-MBEI5UOM.mjs +0 -409
- package/dist/profiles-CDttLtaD.d.ts +0 -521
- package/dist/profiles-CH_nKPjp.d.mts +0 -521
package/dist/index.mjs
CHANGED
|
@@ -9,13 +9,48 @@ import {
|
|
|
9
9
|
isArchetype,
|
|
10
10
|
learningKey
|
|
11
11
|
} from "./chunk-5TI6PNSK.mjs";
|
|
12
|
+
import {
|
|
13
|
+
ABSOLUTE_FLOOR,
|
|
14
|
+
ARCHETYPE_FLOOR_DEFAULT,
|
|
15
|
+
PROVIDER_ENV_KEYS,
|
|
16
|
+
configureBrainQuery,
|
|
17
|
+
createBrainQueryCache,
|
|
18
|
+
ensureCrossProviderTail,
|
|
19
|
+
getAllStarterChains,
|
|
20
|
+
getAllStarterChainsWithGrounding,
|
|
21
|
+
getDefaultFallbackChain,
|
|
22
|
+
getDefaultFallbackChainWithGrounding,
|
|
23
|
+
getModelCompatibility,
|
|
24
|
+
getPerAxisMetrics,
|
|
25
|
+
getReachabilityDiagnostic,
|
|
26
|
+
getSequentialStarterChain,
|
|
27
|
+
getSequentialStarterChainWithGrounding,
|
|
28
|
+
getStarterChain,
|
|
29
|
+
getStarterChainWithGrounding,
|
|
30
|
+
isBrainQueryActiveFor,
|
|
31
|
+
isModelReachable,
|
|
32
|
+
isProviderReachable,
|
|
33
|
+
loadChainsFromBrain,
|
|
34
|
+
readBrainReadEnv,
|
|
35
|
+
resolveProviderKey
|
|
36
|
+
} from "./chunk-KCNQUMHN.mjs";
|
|
12
37
|
import {
|
|
13
38
|
ALIASES,
|
|
39
|
+
_setProfileBrainHook,
|
|
14
40
|
allProfiles,
|
|
41
|
+
allProfilesRaw,
|
|
15
42
|
getProfile,
|
|
16
43
|
profilesByProvider,
|
|
17
44
|
tryGetProfile
|
|
18
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-JQGRWJZO.mjs";
|
|
46
|
+
import {
|
|
47
|
+
emitAdvisoryFired,
|
|
48
|
+
emitCompileDone,
|
|
49
|
+
emitCompileStart,
|
|
50
|
+
emitExecuteAttempt,
|
|
51
|
+
emitExecuteSuccess,
|
|
52
|
+
emitFallbackWalked
|
|
53
|
+
} from "./chunk-NBO4R5PC.mjs";
|
|
19
54
|
|
|
20
55
|
// src/tokenizer.ts
|
|
21
56
|
var tokenizerImpl = defaultCharBasedCounter;
|
|
@@ -120,38 +155,96 @@ function passToolRelevance(ir, opts = {}) {
|
|
|
120
155
|
]
|
|
121
156
|
};
|
|
122
157
|
}
|
|
158
|
+
function totalHistoryTokens(history) {
|
|
159
|
+
let total = 0;
|
|
160
|
+
for (const m of history) {
|
|
161
|
+
if (typeof m.content === "string") total += countTokens(m.content);
|
|
162
|
+
}
|
|
163
|
+
return total;
|
|
164
|
+
}
|
|
123
165
|
function passCompressHistory(ir, opts = {}) {
|
|
124
166
|
const history = ir.history;
|
|
125
|
-
if (!history || history.length === 0)
|
|
167
|
+
if (!history || history.length === 0) {
|
|
168
|
+
return { value: ir, mutations: [], historyTokensTotal: 0 };
|
|
169
|
+
}
|
|
126
170
|
const keepRecent = opts.keepRecent ?? 4;
|
|
127
171
|
const summarizeOlderThan = opts.summarizeOlderThan ?? 8;
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
172
|
+
const summarizeAboveTokens = opts.summarizeAboveTokens;
|
|
173
|
+
const historyTokensTotal = totalHistoryTokens(history);
|
|
174
|
+
const countThresholdHit = history.length > summarizeOlderThan;
|
|
175
|
+
const tokenThresholdHit = summarizeAboveTokens !== void 0 && historyTokensTotal > summarizeAboveTokens;
|
|
176
|
+
if (!countThresholdHit && !tokenThresholdHit) {
|
|
177
|
+
return { value: ir, mutations: [], historyTokensTotal };
|
|
178
|
+
}
|
|
179
|
+
if (history.length > keepRecent) {
|
|
180
|
+
const cutIndex = history.length - keepRecent;
|
|
181
|
+
const old = history.slice(0, cutIndex);
|
|
182
|
+
const recent = history.slice(cutIndex);
|
|
183
|
+
const userTurns = old.filter((m) => m.role === "user");
|
|
184
|
+
const firstUserLine = userTurns[0]?.content.split("\n")[0]?.slice(0, 200) ?? "";
|
|
185
|
+
const oldTokens = totalHistoryTokens(old);
|
|
186
|
+
const trigger = tokenThresholdHit && !countThresholdHit ? "tokens" : "count";
|
|
187
|
+
const summary = {
|
|
188
|
+
role: "system",
|
|
189
|
+
content: `[Earlier conversation: ${old.length} turns omitted (~${oldTokens} tokens). First user message: "${firstUserLine}"]`
|
|
190
|
+
};
|
|
191
|
+
return {
|
|
192
|
+
value: { ...ir, history: [summary, ...recent] },
|
|
193
|
+
mutations: [
|
|
194
|
+
{
|
|
195
|
+
id: `compress-history-${old.length}`,
|
|
196
|
+
source: "static_pass",
|
|
197
|
+
passName: "compress_history",
|
|
198
|
+
description: trigger === "tokens" ? `Compressed ${old.length} old turns (~${oldTokens} tokens) into 1 summary \u2014 token threshold ${summarizeAboveTokens} exceeded (kept ${keepRecent} recent)` : `Compressed ${old.length} old turns into 1 summary (kept ${keepRecent} recent)`
|
|
199
|
+
}
|
|
200
|
+
],
|
|
201
|
+
historyTokensTotal
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (tokenThresholdHit) {
|
|
205
|
+
let fattestIdx = -1;
|
|
206
|
+
let fattestTokens = 0;
|
|
207
|
+
for (let i = 0; i < history.length; i++) {
|
|
208
|
+
const m = history[i];
|
|
209
|
+
if (!m || typeof m.content !== "string") continue;
|
|
210
|
+
const t = countTokens(m.content);
|
|
211
|
+
if (t > fattestTokens) {
|
|
212
|
+
fattestTokens = t;
|
|
213
|
+
fattestIdx = i;
|
|
146
214
|
}
|
|
147
|
-
|
|
148
|
-
|
|
215
|
+
}
|
|
216
|
+
const FAT_DOMINANCE_FLOOR = 0.3;
|
|
217
|
+
const fattest = fattestIdx >= 0 ? history[fattestIdx] : void 0;
|
|
218
|
+
if (fattest && historyTokensTotal > 0 && fattestTokens / historyTokensTotal >= FAT_DOMINANCE_FLOOR) {
|
|
219
|
+
const firstLine = fattest.content.split("\n")[0]?.slice(0, 200) ?? "";
|
|
220
|
+
const newContent = `[Earlier ${fattest.role} message content omitted: ~${fattestTokens} tokens. Preview: "${firstLine}"]`;
|
|
221
|
+
const newHistory = history.slice();
|
|
222
|
+
newHistory[fattestIdx] = { ...fattest, content: newContent };
|
|
223
|
+
return {
|
|
224
|
+
value: { ...ir, history: newHistory },
|
|
225
|
+
mutations: [
|
|
226
|
+
{
|
|
227
|
+
id: `compress-fat-message-${fattestIdx}`,
|
|
228
|
+
source: "static_pass",
|
|
229
|
+
passName: "compress_history",
|
|
230
|
+
description: `Replaced fat ${fattest.role} message #${fattestIdx} content (~${fattestTokens} of ${historyTokensTotal} tokens, ${Math.round(fattestTokens / historyTokensTotal * 100)}% of history) with summary stub \u2014 token threshold ${summarizeAboveTokens} exceeded (history.length ${history.length} <= keepRecent ${keepRecent}, slice not possible)`
|
|
231
|
+
}
|
|
232
|
+
],
|
|
233
|
+
historyTokensTotal
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { value: ir, mutations: [], historyTokensTotal };
|
|
149
238
|
}
|
|
150
239
|
function passApplyCliffs(ir, profile, estimatedInputTokens) {
|
|
151
240
|
const mutations = [];
|
|
152
241
|
const hints = { qualityWarning: [] };
|
|
153
242
|
let nextIR = ir;
|
|
243
|
+
const sequentialMode = nextIR.constraints?.toolOrchestration === "sequential";
|
|
154
244
|
for (const cliff of profile.cliffs) {
|
|
245
|
+
if (sequentialMode && cliff.reason.includes("L-040")) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
155
248
|
let triggered = false;
|
|
156
249
|
switch (cliff.metric) {
|
|
157
250
|
case "input_tokens":
|
|
@@ -250,7 +343,8 @@ function passScoreTargets(ir, opts) {
|
|
|
250
343
|
const preferredSet = new Set(policy.preferredModels ?? []);
|
|
251
344
|
const scores = [];
|
|
252
345
|
const policyMutations = [];
|
|
253
|
-
|
|
346
|
+
const modelIds = ir.models.filter((m) => typeof m === "string");
|
|
347
|
+
for (const modelId of modelIds) {
|
|
254
348
|
let profile;
|
|
255
349
|
try {
|
|
256
350
|
profile = opts.profilesById(modelId);
|
|
@@ -294,7 +388,7 @@ function passScoreTargets(ir, opts) {
|
|
|
294
388
|
}
|
|
295
389
|
const baseQuality = profile.strengths.includes("reasoning") ? 0.85 : profile.strengths.includes("quality") ? 0.8 : 0.6;
|
|
296
390
|
const qualityScore = Math.max(0, baseQuality - qualityPenalty);
|
|
297
|
-
const callerOrderBoost = (
|
|
391
|
+
const callerOrderBoost = (modelIds.length - modelIds.indexOf(modelId)) * 0.1;
|
|
298
392
|
const costPenalty = estimatedCostUsd * 5;
|
|
299
393
|
const preferredBoost = preferredSet.has(modelId) ? 0.5 : 0;
|
|
300
394
|
const rank = qualityScore + callerOrderBoost - costPenalty - reasons.length * 10 + preferredBoost;
|
|
@@ -374,10 +468,16 @@ function lower(ir, profile, hints = {}) {
|
|
|
374
468
|
}
|
|
375
469
|
function lowerAnthropic(ir, profile, hints) {
|
|
376
470
|
const systemBlocks = buildAnthropicSystemBlocks(ir.sections, profile);
|
|
377
|
-
const
|
|
471
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
472
|
+
const policy = ir.historyCachePolicy;
|
|
473
|
+
const markIndex = resolveHistoryMarkIndex(history.length, policy);
|
|
474
|
+
const messages = buildAnthropicMessages(history, ir.currentTurn, markIndex);
|
|
378
475
|
const tools = ir.tools ? toAnthropicTools(ir.tools) : void 0;
|
|
379
476
|
const cacheableTokens = computeCacheableTokens(systemBlocks);
|
|
380
|
-
const
|
|
477
|
+
const historyCacheableTokens = markIndex >= 0 ? sumHistoryTokens(history, markIndex) : 0;
|
|
478
|
+
const totalCacheableTokens = cacheableTokens + historyCacheableTokens;
|
|
479
|
+
const cacheSavings = totalCacheableTokens / 1e6 * profile.costInputPer1m * (1 - (profile.lowering.cache.discount ?? 0.1));
|
|
480
|
+
const toolChoice = hints.wireOverrides?.parallelToolCalls === false && tools && tools.length > 0 ? { type: "auto", disable_parallel_tool_use: true } : void 0;
|
|
381
481
|
return {
|
|
382
482
|
request: {
|
|
383
483
|
provider: "anthropic",
|
|
@@ -385,10 +485,16 @@ function lowerAnthropic(ir, profile, hints) {
|
|
|
385
485
|
system: systemBlocks,
|
|
386
486
|
messages,
|
|
387
487
|
tools,
|
|
388
|
-
|
|
488
|
+
// alpha.8: trust profile.maxOutputTokens. The historical Math.min(_, 4096)
|
|
489
|
+
// floor surprised every consumer once (PB-Cairn contract-gaps brief, Gap 3).
|
|
490
|
+
// Profile is the single source of truth; consumers wanting a tighter
|
|
491
|
+
// budget can pass providerOverrides.anthropic.max_tokens explicitly.
|
|
492
|
+
max_tokens: hints.forceTerseOutput ? 200 : profile.maxOutputTokens,
|
|
493
|
+
tool_choice: toolChoice
|
|
389
494
|
},
|
|
390
495
|
diagnostics: {
|
|
391
496
|
cacheableTokens,
|
|
497
|
+
historyCacheableTokens,
|
|
392
498
|
estimatedCacheSavingsUsd: cacheSavings
|
|
393
499
|
}
|
|
394
500
|
};
|
|
@@ -421,17 +527,64 @@ function buildAnthropicSystemBlocks(sections, profile) {
|
|
|
421
527
|
}
|
|
422
528
|
return blocks;
|
|
423
529
|
}
|
|
424
|
-
function buildAnthropicMessages(history, currentTurn) {
|
|
530
|
+
function buildAnthropicMessages(history, currentTurn, markIndex) {
|
|
425
531
|
const out = [];
|
|
426
|
-
for (
|
|
532
|
+
for (let i = 0; i < history.length; i++) {
|
|
533
|
+
const m = history[i];
|
|
427
534
|
if (m.role === "system") continue;
|
|
428
|
-
|
|
535
|
+
const shouldMark = i === markIndex;
|
|
536
|
+
out.push({
|
|
537
|
+
role: m.role,
|
|
538
|
+
content: shouldMark ? attachAnthropicCacheControl(m) : m.parts ?? m.content
|
|
539
|
+
});
|
|
429
540
|
}
|
|
430
541
|
if (currentTurn && currentTurn.role !== "system") {
|
|
431
542
|
out.push({ role: currentTurn.role, content: currentTurn.parts ?? currentTurn.content });
|
|
432
543
|
}
|
|
433
544
|
return out;
|
|
434
545
|
}
|
|
546
|
+
function attachAnthropicCacheControl(m) {
|
|
547
|
+
if (Array.isArray(m.parts) && m.parts.length > 0) {
|
|
548
|
+
const blocks = m.parts;
|
|
549
|
+
const last = blocks[blocks.length - 1];
|
|
550
|
+
const withMarker = {
|
|
551
|
+
...last,
|
|
552
|
+
cache_control: { type: "ephemeral" }
|
|
553
|
+
};
|
|
554
|
+
return [...blocks.slice(0, -1), withMarker];
|
|
555
|
+
}
|
|
556
|
+
return [
|
|
557
|
+
{
|
|
558
|
+
type: "text",
|
|
559
|
+
text: m.content,
|
|
560
|
+
cache_control: { type: "ephemeral" }
|
|
561
|
+
}
|
|
562
|
+
];
|
|
563
|
+
}
|
|
564
|
+
function resolveHistoryMarkIndex(historyLen, policy) {
|
|
565
|
+
if (!policy || policy.strategy === "none") return -1;
|
|
566
|
+
if (historyLen === 0) return -1;
|
|
567
|
+
if (policy.strategy === "all-but-latest") {
|
|
568
|
+
return historyLen - 1;
|
|
569
|
+
}
|
|
570
|
+
const idx = historyLen - 1 - policy.suffix;
|
|
571
|
+
return idx >= 0 ? idx : -1;
|
|
572
|
+
}
|
|
573
|
+
function sumHistoryTokens(history, throughIndex) {
|
|
574
|
+
let total = 0;
|
|
575
|
+
for (let i = 0; i <= throughIndex && i < history.length; i++) {
|
|
576
|
+
const m = history[i];
|
|
577
|
+
if (m.role === "system") continue;
|
|
578
|
+
if (Array.isArray(m.parts)) {
|
|
579
|
+
for (const p of m.parts) {
|
|
580
|
+
if (typeof p.text === "string") total += countTokens(p.text);
|
|
581
|
+
}
|
|
582
|
+
} else if (typeof m.content === "string") {
|
|
583
|
+
total += countTokens(m.content);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return total;
|
|
587
|
+
}
|
|
435
588
|
function toAnthropicTools(tools) {
|
|
436
589
|
return tools.map((t) => ({
|
|
437
590
|
name: t.name,
|
|
@@ -466,6 +619,9 @@ function lowerGoogle(ir, profile, hints) {
|
|
|
466
619
|
const minTokens = profile.lowering.cache.minTokens ?? 4096;
|
|
467
620
|
const meetsMin = cacheableTokens >= minTokens;
|
|
468
621
|
const cacheSavings = meetsMin ? cacheableTokens / 1e6 * profile.costInputPer1m * (1 - (profile.lowering.cache.discount ?? 0.25)) : 0;
|
|
622
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
623
|
+
const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
|
|
624
|
+
const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
|
|
469
625
|
return {
|
|
470
626
|
request: {
|
|
471
627
|
provider: "google",
|
|
@@ -477,6 +633,7 @@ function lowerGoogle(ir, profile, hints) {
|
|
|
477
633
|
},
|
|
478
634
|
diagnostics: {
|
|
479
635
|
cacheableTokens: meetsMin ? cacheableTokens : 0,
|
|
636
|
+
historyCacheableTokens,
|
|
480
637
|
estimatedCacheSavingsUsd: cacheSavings
|
|
481
638
|
}
|
|
482
639
|
};
|
|
@@ -524,6 +681,10 @@ function lowerOpenAI(ir, profile, hints) {
|
|
|
524
681
|
content: ir.currentTurn.parts ?? ir.currentTurn.content
|
|
525
682
|
});
|
|
526
683
|
}
|
|
684
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
685
|
+
const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
|
|
686
|
+
const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
|
|
687
|
+
const openaiParallelToolCalls = hints.wireOverrides?.parallelToolCalls === false && ir.tools && ir.tools.length > 0 ? false : void 0;
|
|
527
688
|
return {
|
|
528
689
|
request: {
|
|
529
690
|
provider: "openai",
|
|
@@ -531,9 +692,14 @@ function lowerOpenAI(ir, profile, hints) {
|
|
|
531
692
|
messages,
|
|
532
693
|
tools: ir.tools && ir.tools.length > 0 ? toOpenAITools(ir.tools) : void 0,
|
|
533
694
|
response_format: ir.constraints?.structuredOutput ? { type: "json_object" } : void 0,
|
|
534
|
-
reasoning_effort: hints.forceTerseOutput ? "low" : void 0
|
|
695
|
+
reasoning_effort: hints.forceTerseOutput ? "low" : void 0,
|
|
696
|
+
parallel_tool_calls: openaiParallelToolCalls
|
|
535
697
|
},
|
|
536
|
-
diagnostics: {
|
|
698
|
+
diagnostics: {
|
|
699
|
+
cacheableTokens: 0,
|
|
700
|
+
historyCacheableTokens,
|
|
701
|
+
estimatedCacheSavingsUsd: 0
|
|
702
|
+
}
|
|
537
703
|
};
|
|
538
704
|
}
|
|
539
705
|
function toOpenAITools(tools) {
|
|
@@ -560,6 +726,9 @@ function lowerDeepSeek(ir, profile) {
|
|
|
560
726
|
content: ir.currentTurn.parts ?? ir.currentTurn.content
|
|
561
727
|
});
|
|
562
728
|
}
|
|
729
|
+
const history = (ir.history ?? []).filter((m) => m.role !== "system");
|
|
730
|
+
const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
|
|
731
|
+
const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
|
|
563
732
|
return {
|
|
564
733
|
request: {
|
|
565
734
|
provider: "deepseek",
|
|
@@ -574,7 +743,11 @@ function lowerDeepSeek(ir, profile) {
|
|
|
574
743
|
}
|
|
575
744
|
})) : void 0
|
|
576
745
|
},
|
|
577
|
-
diagnostics: {
|
|
746
|
+
diagnostics: {
|
|
747
|
+
cacheableTokens: 0,
|
|
748
|
+
historyCacheableTokens,
|
|
749
|
+
estimatedCacheSavingsUsd: 0
|
|
750
|
+
}
|
|
578
751
|
};
|
|
579
752
|
}
|
|
580
753
|
function sortSections(sections) {
|
|
@@ -597,204 +770,1667 @@ function setNestedField(obj, path, value) {
|
|
|
597
770
|
cursor[parts[parts.length - 1]] = value;
|
|
598
771
|
}
|
|
599
772
|
|
|
600
|
-
// src/
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
return
|
|
773
|
+
// src/archetype-perf-brain.ts
|
|
774
|
+
function isPerfRow(x) {
|
|
775
|
+
if (!x || typeof x !== "object") return false;
|
|
776
|
+
const r = x;
|
|
777
|
+
return typeof r.model_id === "string" && typeof r.archetype === "string" && typeof r.perf_score === "number";
|
|
778
|
+
}
|
|
779
|
+
function mapRowsToPerfMap(rows) {
|
|
780
|
+
const out = /* @__PURE__ */ new Map();
|
|
781
|
+
for (const row of rows) {
|
|
782
|
+
if (!isPerfRow(row)) continue;
|
|
783
|
+
const existing = out.get(row.model_id) ?? {};
|
|
784
|
+
existing[row.archetype] = row.perf_score;
|
|
785
|
+
out.set(row.model_id, existing);
|
|
786
|
+
}
|
|
787
|
+
return out;
|
|
605
788
|
}
|
|
606
|
-
function
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const compressed = passCompressHistory(toolFiltered.value, {
|
|
615
|
-
summarizeOlderThan: opts.compressHistoryAfter
|
|
616
|
-
});
|
|
617
|
-
let workingIR = compressed.value;
|
|
618
|
-
const accumulatedMutations = [
|
|
619
|
-
...sliced.mutations,
|
|
620
|
-
...deduped.mutations,
|
|
621
|
-
...toolFiltered.mutations,
|
|
622
|
-
...compressed.mutations
|
|
623
|
-
];
|
|
624
|
-
const inputTokens = estimateInputTokens(workingIR);
|
|
625
|
-
const scores = passScoreTargets(workingIR, {
|
|
626
|
-
estimatedInputTokens: inputTokens,
|
|
627
|
-
profilesById: resolver,
|
|
628
|
-
policy: opts.policy
|
|
629
|
-
});
|
|
630
|
-
accumulatedMutations.push(...scores.mutations);
|
|
631
|
-
const target = pickTarget(workingIR, scores.value);
|
|
632
|
-
if (!target) {
|
|
633
|
-
throw new Error(
|
|
634
|
-
`compile(): no allowed model fits the request. Scores: ${JSON.stringify(scores.value, null, 2)}`
|
|
635
|
-
);
|
|
789
|
+
function mapRowsToNMap(rows) {
|
|
790
|
+
const out = /* @__PURE__ */ new Map();
|
|
791
|
+
for (const row of rows) {
|
|
792
|
+
if (!isPerfRow(row)) continue;
|
|
793
|
+
if (typeof row.n !== "number") continue;
|
|
794
|
+
const existing = out.get(row.model_id) ?? {};
|
|
795
|
+
existing[row.archetype] = row.n;
|
|
796
|
+
out.set(row.model_id, existing);
|
|
636
797
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
798
|
+
return out;
|
|
799
|
+
}
|
|
800
|
+
function bundledArchetypePerf() {
|
|
801
|
+
const out = /* @__PURE__ */ new Map();
|
|
802
|
+
for (const profile of allProfiles()) {
|
|
803
|
+
if (profile.archetypePerf) out.set(profile.id, profile.archetypePerf);
|
|
804
|
+
}
|
|
805
|
+
return out;
|
|
806
|
+
}
|
|
807
|
+
function bundledArchetypePerfN() {
|
|
808
|
+
return /* @__PURE__ */ new Map();
|
|
809
|
+
}
|
|
810
|
+
var loadArchetypePerfFromBrain = createBrainQueryCache({
|
|
811
|
+
table: "kgauto_archetype_perf",
|
|
812
|
+
mapRows: mapRowsToPerfMap,
|
|
813
|
+
bundledFallback: bundledArchetypePerf
|
|
814
|
+
});
|
|
815
|
+
var loadArchetypePerfNFromBrain = createBrainQueryCache(
|
|
816
|
+
{
|
|
817
|
+
table: "kgauto_archetype_perf",
|
|
818
|
+
mapRows: mapRowsToNMap,
|
|
819
|
+
bundledFallback: bundledArchetypePerfN
|
|
820
|
+
}
|
|
821
|
+
);
|
|
822
|
+
var MEASURED_GROUNDING_MIN_N = 10;
|
|
823
|
+
function getArchetypePerfScore(modelId, archetype) {
|
|
824
|
+
const score = loadArchetypePerfFromBrain().get(modelId)?.[archetype] ?? 5;
|
|
825
|
+
const n = loadArchetypePerfNFromBrain().get(modelId)?.[archetype] ?? 0;
|
|
826
|
+
const grounding = n >= MEASURED_GROUNDING_MIN_N ? "measured" : "judgment";
|
|
827
|
+
return { score, n, grounding };
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/exclusion-findings-brain.ts
|
|
831
|
+
function isValidVerdict(v) {
|
|
832
|
+
return v === "recommend-probe" || v === "unblock" || v === "stay-excluded" || v === "inconclusive";
|
|
833
|
+
}
|
|
834
|
+
function isValidConfidence(v) {
|
|
835
|
+
return v === "high" || v === "medium" || v === "low";
|
|
836
|
+
}
|
|
837
|
+
function isRawFindingRow(x) {
|
|
838
|
+
if (!x || typeof x !== "object") return false;
|
|
839
|
+
const r = x;
|
|
840
|
+
return typeof r.intent_archetype === "string" && typeof r.excluded_model === "string" && typeof r.excluded_provider === "string" && typeof r.verdict === "string" && typeof r.message === "string" && typeof r.suggestion === "string" && typeof r.confidence === "string";
|
|
841
|
+
}
|
|
842
|
+
function mapRowsToFindings(rows) {
|
|
843
|
+
const out = [];
|
|
844
|
+
for (const row of rows) {
|
|
845
|
+
if (!isRawFindingRow(row)) continue;
|
|
846
|
+
if (!isValidVerdict(row.verdict)) continue;
|
|
847
|
+
if (!isValidConfidence(row.confidence)) continue;
|
|
848
|
+
let savings = null;
|
|
849
|
+
if (typeof row.estimated_savings_usd_30d === "number") {
|
|
850
|
+
savings = Number.isFinite(row.estimated_savings_usd_30d) ? row.estimated_savings_usd_30d : null;
|
|
851
|
+
} else if (typeof row.estimated_savings_usd_30d === "string") {
|
|
852
|
+
const n = Number(row.estimated_savings_usd_30d);
|
|
853
|
+
savings = Number.isFinite(n) ? n : null;
|
|
668
854
|
}
|
|
669
|
-
|
|
855
|
+
out.push({
|
|
856
|
+
archetype: row.intent_archetype,
|
|
857
|
+
excludedModel: row.excluded_model,
|
|
858
|
+
excludedProvider: row.excluded_provider,
|
|
859
|
+
verdict: row.verdict,
|
|
860
|
+
estimatedSavingsUsd30d: savings,
|
|
861
|
+
message: row.message,
|
|
862
|
+
suggestion: row.suggestion,
|
|
863
|
+
confidence: row.confidence,
|
|
864
|
+
evidence: row.evidence && typeof row.evidence === "object" ? row.evidence : void 0
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
return out;
|
|
670
868
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
869
|
+
var snapshots = /* @__PURE__ */ new Map();
|
|
870
|
+
var runtime;
|
|
871
|
+
var warnedOnce = false;
|
|
872
|
+
var DEFAULT_FINDINGS_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions";
|
|
873
|
+
function configureExclusionFindingsBrain(rt) {
|
|
874
|
+
runtime = rt;
|
|
875
|
+
snapshots.clear();
|
|
876
|
+
warnedOnce = false;
|
|
877
|
+
}
|
|
878
|
+
function isExclusionFindingsBrainActive() {
|
|
879
|
+
return runtime !== void 0;
|
|
880
|
+
}
|
|
881
|
+
function getStaleExclusionFindings(opts) {
|
|
882
|
+
const rt = runtime;
|
|
883
|
+
if (!rt) return [];
|
|
884
|
+
const appId = opts.appId;
|
|
885
|
+
if (!appId) return [];
|
|
886
|
+
let snap = snapshots.get(appId);
|
|
887
|
+
if (!snap) {
|
|
888
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
889
|
+
snapshots.set(appId, snap);
|
|
675
890
|
}
|
|
676
|
-
|
|
677
|
-
|
|
891
|
+
const now = Date.now();
|
|
892
|
+
const stale = snap.expiresAt <= now;
|
|
893
|
+
if (stale && !snap.refreshing) {
|
|
894
|
+
snap.refreshing = true;
|
|
895
|
+
void asyncRefresh(rt, appId);
|
|
678
896
|
}
|
|
679
|
-
if (
|
|
680
|
-
|
|
897
|
+
if (opts.archetype) {
|
|
898
|
+
return snap.data.filter((f) => f.archetype === opts.archetype);
|
|
681
899
|
}
|
|
900
|
+
return snap.data;
|
|
682
901
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
902
|
+
var pendingRefreshes = /* @__PURE__ */ new Map();
|
|
903
|
+
async function asyncRefresh(rt, appId) {
|
|
904
|
+
const promise = doRefresh(rt, appId);
|
|
905
|
+
pendingRefreshes.set(appId, promise);
|
|
906
|
+
try {
|
|
907
|
+
await promise;
|
|
908
|
+
} finally {
|
|
909
|
+
if (pendingRefreshes.get(appId) === promise) {
|
|
910
|
+
pendingRefreshes.delete(appId);
|
|
691
911
|
}
|
|
692
912
|
}
|
|
693
|
-
const fitting = scores.filter((s) => s.fits).sort((a, b) => b.rank - a.rank);
|
|
694
|
-
return fitting[0];
|
|
695
913
|
}
|
|
696
|
-
function
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
914
|
+
async function doRefresh(rt, appId) {
|
|
915
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
916
|
+
let snap = snapshots.get(appId);
|
|
917
|
+
if (!snap) {
|
|
918
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
919
|
+
snapshots.set(appId, snap);
|
|
701
920
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
921
|
+
try {
|
|
922
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
923
|
+
if (!res.ok) {
|
|
924
|
+
throw new Error(`findings ${res.status}: ${res.statusText}`);
|
|
925
|
+
}
|
|
926
|
+
const body = await res.json();
|
|
927
|
+
if (runtime !== rt) return;
|
|
928
|
+
const rows = Array.isArray(body) ? mapRowsToFindings(body) : [];
|
|
929
|
+
snap.data = rows;
|
|
930
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
931
|
+
snap.refreshing = false;
|
|
932
|
+
} catch (err) {
|
|
933
|
+
if (runtime !== rt) return;
|
|
934
|
+
snap.refreshing = false;
|
|
935
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
936
|
+
if (!warnedOnce) {
|
|
937
|
+
warnedOnce = true;
|
|
938
|
+
(rt.onError ?? defaultOnError)(err);
|
|
939
|
+
}
|
|
706
940
|
}
|
|
707
941
|
}
|
|
942
|
+
function defaultOnError(err) {
|
|
943
|
+
console.warn(
|
|
944
|
+
"[kgauto] exclusion-findings fetch failed (using empty fallback):",
|
|
945
|
+
err
|
|
946
|
+
);
|
|
947
|
+
}
|
|
708
948
|
|
|
709
|
-
// src/brain.ts
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const
|
|
713
|
-
|
|
949
|
+
// src/promote-ready-brain.ts
|
|
950
|
+
function isRawPromoteReadyRow(x) {
|
|
951
|
+
if (!x || typeof x !== "object") return false;
|
|
952
|
+
const r = x;
|
|
953
|
+
return typeof r.intent_archetype === "string" && typeof r.family === "string" && typeof r.candidate_model === "string" && typeof r.current_model === "string" && typeof r.detected_at === "string";
|
|
954
|
+
}
|
|
955
|
+
function coerceNumber(v) {
|
|
956
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
957
|
+
if (typeof v === "string") {
|
|
958
|
+
const n = Number(v);
|
|
959
|
+
return Number.isFinite(n) ? n : null;
|
|
960
|
+
}
|
|
961
|
+
return null;
|
|
714
962
|
}
|
|
715
|
-
function
|
|
716
|
-
|
|
963
|
+
function mapRowsToFindings2(rows) {
|
|
964
|
+
const out = [];
|
|
965
|
+
for (const row of rows) {
|
|
966
|
+
if (!isRawPromoteReadyRow(row)) continue;
|
|
967
|
+
const sampleN = coerceNumber(row.sample_n);
|
|
968
|
+
const passRate = coerceNumber(row.judge_pass_rate);
|
|
969
|
+
const avgScore = coerceNumber(row.judge_avg_score);
|
|
970
|
+
if (sampleN === null || passRate === null || avgScore === null) continue;
|
|
971
|
+
out.push({
|
|
972
|
+
archetype: row.intent_archetype,
|
|
973
|
+
family: row.family,
|
|
974
|
+
candidateModel: row.candidate_model,
|
|
975
|
+
currentModel: row.current_model,
|
|
976
|
+
sampleN,
|
|
977
|
+
judgePassRate: passRate,
|
|
978
|
+
judgeAvgScore: avgScore,
|
|
979
|
+
costDeltaPct: coerceNumber(row.cost_delta_pct),
|
|
980
|
+
detectedAt: row.detected_at
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
return out;
|
|
717
984
|
}
|
|
718
|
-
var
|
|
719
|
-
var
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
985
|
+
var snapshots2 = /* @__PURE__ */ new Map();
|
|
986
|
+
var runtime2;
|
|
987
|
+
var warnedOnce2 = false;
|
|
988
|
+
function isPromoteReadyBrainActive() {
|
|
989
|
+
return runtime2 !== void 0;
|
|
990
|
+
}
|
|
991
|
+
function loadPromoteReadyFindings(opts) {
|
|
992
|
+
const rt = runtime2;
|
|
993
|
+
if (!rt) return [];
|
|
994
|
+
const appId = opts.appId;
|
|
995
|
+
if (!appId) return [];
|
|
996
|
+
let snap = snapshots2.get(appId);
|
|
997
|
+
if (!snap) {
|
|
998
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
999
|
+
snapshots2.set(appId, snap);
|
|
1000
|
+
}
|
|
1001
|
+
const now = Date.now();
|
|
1002
|
+
const stale = snap.expiresAt <= now;
|
|
1003
|
+
if (stale && !snap.refreshing) {
|
|
1004
|
+
snap.refreshing = true;
|
|
1005
|
+
void asyncRefresh2(rt, appId);
|
|
1006
|
+
}
|
|
1007
|
+
let rows = snap.data;
|
|
1008
|
+
if (opts.archetype) {
|
|
1009
|
+
rows = rows.filter((f) => f.archetype === opts.archetype);
|
|
1010
|
+
}
|
|
1011
|
+
if (opts.family) {
|
|
1012
|
+
rows = rows.filter((f) => f.family === opts.family);
|
|
1013
|
+
}
|
|
1014
|
+
return rows;
|
|
1015
|
+
}
|
|
1016
|
+
var pendingRefreshes2 = /* @__PURE__ */ new Map();
|
|
1017
|
+
async function asyncRefresh2(rt, appId) {
|
|
1018
|
+
const promise = doRefresh2(rt, appId);
|
|
1019
|
+
pendingRefreshes2.set(appId, promise);
|
|
1020
|
+
try {
|
|
1021
|
+
await promise;
|
|
1022
|
+
} finally {
|
|
1023
|
+
if (pendingRefreshes2.get(appId) === promise) {
|
|
1024
|
+
pendingRefreshes2.delete(appId);
|
|
727
1025
|
}
|
|
728
1026
|
}
|
|
729
|
-
const tokens = result.tokensIn;
|
|
730
|
-
const shape = computeShape(
|
|
731
|
-
{
|
|
732
|
-
appId,
|
|
733
|
-
intent: { name: archetype, archetype },
|
|
734
|
-
sections: ir.sections,
|
|
735
|
-
tools: ir.tools,
|
|
736
|
-
history: ir.history,
|
|
737
|
-
models: ir.models,
|
|
738
|
-
constraints: ir.constraints
|
|
739
|
-
},
|
|
740
|
-
tokens
|
|
741
|
-
);
|
|
742
|
-
const shapeKey = `${shape.contextBucket}-${shape.toolCountBucket}-${shape.historyDepth}-${shape.outputMode}`;
|
|
743
|
-
compileRegistry.set(result.handle, {
|
|
744
|
-
appId,
|
|
745
|
-
archetype,
|
|
746
|
-
model: result.target,
|
|
747
|
-
provider: result.provider,
|
|
748
|
-
shapeKey,
|
|
749
|
-
learningKey: learningKey(archetype, result.target, shape),
|
|
750
|
-
estimatedTokensIn: tokens,
|
|
751
|
-
mutationsApplied: result.mutationsApplied.map((m) => m.id),
|
|
752
|
-
startedAt: Date.now()
|
|
753
|
-
});
|
|
754
1027
|
}
|
|
755
|
-
async function
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
if (!
|
|
759
|
-
|
|
1028
|
+
async function doRefresh2(rt, appId) {
|
|
1029
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1030
|
+
let snap = snapshots2.get(appId);
|
|
1031
|
+
if (!snap) {
|
|
1032
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1033
|
+
snapshots2.set(appId, snap);
|
|
760
1034
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
(config.onError ?? defaultOnError)(err);
|
|
1035
|
+
try {
|
|
1036
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1037
|
+
if (!res.ok) {
|
|
1038
|
+
throw new Error(`promote-ready ${res.status}: ${res.statusText}`);
|
|
1039
|
+
}
|
|
1040
|
+
const body = await res.json();
|
|
1041
|
+
if (runtime2 !== rt) return;
|
|
1042
|
+
const rows = Array.isArray(body) ? mapRowsToFindings2(body) : [];
|
|
1043
|
+
snap.data = rows;
|
|
1044
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1045
|
+
snap.refreshing = false;
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
if (runtime2 !== rt) return;
|
|
1048
|
+
snap.refreshing = false;
|
|
1049
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1050
|
+
if (!warnedOnce2) {
|
|
1051
|
+
warnedOnce2 = true;
|
|
1052
|
+
(rt.onError ?? defaultOnError2)(err);
|
|
780
1053
|
}
|
|
781
|
-
};
|
|
782
|
-
if (config.sync) {
|
|
783
|
-
await send();
|
|
784
|
-
} else {
|
|
785
|
-
void send();
|
|
786
1054
|
}
|
|
787
1055
|
}
|
|
788
|
-
function
|
|
789
|
-
console.warn(
|
|
1056
|
+
function defaultOnError2(err) {
|
|
1057
|
+
console.warn(
|
|
1058
|
+
"[kgauto] promote-ready fetch failed (using empty fallback):",
|
|
1059
|
+
err
|
|
1060
|
+
);
|
|
790
1061
|
}
|
|
791
|
-
function
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1062
|
+
function resolveFetchImpl(injected) {
|
|
1063
|
+
return injected ?? ((...args) => globalThis.fetch(...args));
|
|
1064
|
+
}
|
|
1065
|
+
function normalizeEndpoint(endpoint) {
|
|
1066
|
+
return endpoint.replace(/\/+$/, "");
|
|
1067
|
+
}
|
|
1068
|
+
async function markPromoteReadyHandled(opts) {
|
|
1069
|
+
const {
|
|
1070
|
+
appId,
|
|
1071
|
+
archetype,
|
|
1072
|
+
family,
|
|
1073
|
+
resolution,
|
|
1074
|
+
resolutionNote,
|
|
1075
|
+
brainEndpoint,
|
|
1076
|
+
brainJwt,
|
|
1077
|
+
brainAnonKey,
|
|
1078
|
+
fetch: injectedFetch
|
|
1079
|
+
} = opts;
|
|
1080
|
+
if (!appId) return { ok: false, reason: "app_id_required" };
|
|
1081
|
+
if (!archetype) return { ok: false, reason: "archetype_required" };
|
|
1082
|
+
if (!family) return { ok: false, reason: "family_required" };
|
|
1083
|
+
if (resolution !== "promoted" && resolution !== "declined" && resolution !== "still-evaluating") {
|
|
1084
|
+
return { ok: false, reason: "resolution_invalid" };
|
|
1085
|
+
}
|
|
1086
|
+
const doFetch = resolveFetchImpl(injectedFetch);
|
|
1087
|
+
const base = normalizeEndpoint(brainEndpoint);
|
|
1088
|
+
const url = `${base}/rest/v1/promote_ready_findings?app_id=eq.${encodeURIComponent(appId)}&intent_archetype=eq.${encodeURIComponent(archetype)}&family=eq.${encodeURIComponent(family)}&resolved_at=is.null`;
|
|
1089
|
+
const patchBody = {
|
|
1090
|
+
resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1091
|
+
resolution
|
|
1092
|
+
};
|
|
1093
|
+
if (resolutionNote !== void 0) {
|
|
1094
|
+
patchBody.resolution_note = resolutionNote;
|
|
1095
|
+
}
|
|
1096
|
+
let res;
|
|
1097
|
+
try {
|
|
1098
|
+
res = await doFetch(url, {
|
|
1099
|
+
method: "PATCH",
|
|
1100
|
+
headers: {
|
|
1101
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
1102
|
+
apikey: brainAnonKey,
|
|
1103
|
+
"Content-Type": "application/json",
|
|
1104
|
+
Accept: "application/json",
|
|
1105
|
+
Prefer: "return=minimal"
|
|
1106
|
+
},
|
|
1107
|
+
body: JSON.stringify(patchBody)
|
|
1108
|
+
});
|
|
1109
|
+
} catch (err) {
|
|
1110
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1111
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
1112
|
+
}
|
|
1113
|
+
if (res.status === 401 || res.status === 403) {
|
|
1114
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
1115
|
+
}
|
|
1116
|
+
if (res.status >= 500) {
|
|
1117
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
1118
|
+
}
|
|
1119
|
+
if (!res.ok) {
|
|
1120
|
+
return { ok: false, reason: `patch_failed:${res.status}` };
|
|
1121
|
+
}
|
|
1122
|
+
return { ok: true };
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// src/advisor-rules/promote-ready.ts
|
|
1126
|
+
var PROMOTE_READY_THRESHOLDS = {
|
|
1127
|
+
minPassRate: 0.8,
|
|
1128
|
+
minAvgScore: 4
|
|
1129
|
+
};
|
|
1130
|
+
function shouldFirePromoteReady(finding, resolvedPrimary) {
|
|
1131
|
+
if (finding.currentModel !== resolvedPrimary) return false;
|
|
1132
|
+
if (finding.judgePassRate < PROMOTE_READY_THRESHOLDS.minPassRate) return false;
|
|
1133
|
+
if (finding.judgeAvgScore < PROMOTE_READY_THRESHOLDS.minAvgScore) return false;
|
|
1134
|
+
return true;
|
|
1135
|
+
}
|
|
1136
|
+
function deriveFamilyLocal(modelId) {
|
|
1137
|
+
if (modelId.startsWith("claude-opus-")) return "claude-opus";
|
|
1138
|
+
if (modelId.startsWith("claude-sonnet-")) return "claude-sonnet";
|
|
1139
|
+
if (modelId.startsWith("claude-haiku-")) return "claude-haiku";
|
|
1140
|
+
if (/^gemini-.*-flash-lite/.test(modelId)) return "gemini-flash-lite";
|
|
1141
|
+
if (/^gemini-.*-flash/.test(modelId)) return "gemini-flash";
|
|
1142
|
+
if (/^gemini-.*-pro/.test(modelId)) return "gemini-pro";
|
|
1143
|
+
if (/^deepseek-.*-pro/.test(modelId)) return "deepseek-reasoner";
|
|
1144
|
+
if (modelId.startsWith("deepseek-")) return "deepseek-chat";
|
|
1145
|
+
if (modelId.startsWith("gpt-")) return "openai-gpt";
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
function advisorRulePromoteReady(ctx) {
|
|
1149
|
+
if (!isPromoteReadyBrainActive()) return [];
|
|
1150
|
+
if (!ctx.appId) return [];
|
|
1151
|
+
if (!ctx.resolvedPrimary) return [];
|
|
1152
|
+
const family = deriveFamilyLocal(ctx.resolvedPrimary);
|
|
1153
|
+
if (!family) return [];
|
|
1154
|
+
const findings = loadPromoteReadyFindings({
|
|
1155
|
+
appId: ctx.appId,
|
|
1156
|
+
archetype: ctx.archetype,
|
|
1157
|
+
family
|
|
1158
|
+
});
|
|
1159
|
+
if (findings.length === 0) return [];
|
|
1160
|
+
const qualifying = findings.filter(
|
|
1161
|
+
(f) => shouldFirePromoteReady(f, ctx.resolvedPrimary)
|
|
1162
|
+
);
|
|
1163
|
+
if (qualifying.length === 0) return [];
|
|
1164
|
+
qualifying.sort((a, b) => {
|
|
1165
|
+
if (a.judgeAvgScore !== b.judgeAvgScore) {
|
|
1166
|
+
return b.judgeAvgScore - a.judgeAvgScore;
|
|
1167
|
+
}
|
|
1168
|
+
return b.judgePassRate - a.judgePassRate;
|
|
1169
|
+
});
|
|
1170
|
+
const top = qualifying[0];
|
|
1171
|
+
const pctPass = Math.round(top.judgePassRate * 100);
|
|
1172
|
+
const score = top.judgeAvgScore.toFixed(2);
|
|
1173
|
+
let costClause = "";
|
|
1174
|
+
if (top.costDeltaPct !== null) {
|
|
1175
|
+
const sign = top.costDeltaPct < 0 ? "cheaper" : "more expensive";
|
|
1176
|
+
const magnitude = Math.abs(top.costDeltaPct * 100).toFixed(1);
|
|
1177
|
+
costClause = `, cost ${magnitude}% ${sign}`;
|
|
1178
|
+
}
|
|
1179
|
+
const message = `Probe found ${top.candidateModel} produces equivalent-or-better outputs vs ${top.currentModel} on ${top.sampleN} recent ${top.archetype} prompts (pass rate ${pctPass}%, avg score ${score}/5${costClause}). Consider promoting via markPromoteReadyHandled.`;
|
|
1180
|
+
return [
|
|
1181
|
+
{
|
|
1182
|
+
level: "info",
|
|
1183
|
+
code: "promote-ready",
|
|
1184
|
+
message,
|
|
1185
|
+
suggestion: `Migrate ${top.archetype} traffic from ${top.currentModel} to ${top.candidateModel}, then call markPromoteReadyHandled({ appId, archetype: '${top.archetype}', family: '${top.family}', resolution: 'promoted' }) to silence this advisory.`,
|
|
1186
|
+
// alpha.36 architectural field — not a no-ai-needed case.
|
|
1187
|
+
recommendedArchitecture: void 0,
|
|
1188
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1189
|
+
}
|
|
1190
|
+
];
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// src/advisor-rules/consumer-on-stale-model.ts
|
|
1194
|
+
function isStaleStatus(v) {
|
|
1195
|
+
return v === "legacy" || v === "deprecated";
|
|
1196
|
+
}
|
|
1197
|
+
function asString(v) {
|
|
1198
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
1199
|
+
}
|
|
1200
|
+
function mapRowsToFindings3(rows) {
|
|
1201
|
+
const out = [];
|
|
1202
|
+
for (const raw of rows) {
|
|
1203
|
+
if (!raw || typeof raw !== "object") continue;
|
|
1204
|
+
const r = raw;
|
|
1205
|
+
const archetype = asString(r.intent_archetype) ?? asString(r.applies_to_archetype);
|
|
1206
|
+
const staleModel = asString(r.stale_model) ?? asString(r.applies_to_model);
|
|
1207
|
+
const staleProvider = asString(r.stale_provider);
|
|
1208
|
+
const recommendedModel = asString(r.recommended_model);
|
|
1209
|
+
const family = asString(r.family);
|
|
1210
|
+
const message = asString(r.message);
|
|
1211
|
+
if (!archetype || !staleModel || !recommendedModel || !family || !message) {
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
if (!isStaleStatus(r.stale_status)) continue;
|
|
1215
|
+
const row = {
|
|
1216
|
+
archetype,
|
|
1217
|
+
staleModel,
|
|
1218
|
+
staleProvider: staleProvider ?? "unknown",
|
|
1219
|
+
staleStatus: r.stale_status,
|
|
1220
|
+
recommendedModel,
|
|
1221
|
+
family,
|
|
1222
|
+
message
|
|
1223
|
+
};
|
|
1224
|
+
const suggestion = asString(r.suggestion);
|
|
1225
|
+
if (suggestion) row.suggestion = suggestion;
|
|
1226
|
+
if (typeof r.observation_count === "number" && Number.isFinite(r.observation_count)) {
|
|
1227
|
+
row.observationCount = r.observation_count;
|
|
1228
|
+
}
|
|
1229
|
+
out.push(row);
|
|
1230
|
+
}
|
|
1231
|
+
return out;
|
|
1232
|
+
}
|
|
1233
|
+
var snapshots3 = /* @__PURE__ */ new Map();
|
|
1234
|
+
var runtime3;
|
|
1235
|
+
var warnedOnce3 = false;
|
|
1236
|
+
var pendingRefreshes3 = /* @__PURE__ */ new Map();
|
|
1237
|
+
function isStaleModelFindingsBrainActive() {
|
|
1238
|
+
return runtime3 !== void 0;
|
|
1239
|
+
}
|
|
1240
|
+
function getStaleModelFindings(opts) {
|
|
1241
|
+
const rt = runtime3;
|
|
1242
|
+
if (!rt) return [];
|
|
1243
|
+
const appId = opts.appId;
|
|
1244
|
+
if (!appId) return [];
|
|
1245
|
+
let snap = snapshots3.get(appId);
|
|
1246
|
+
if (!snap) {
|
|
1247
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1248
|
+
snapshots3.set(appId, snap);
|
|
1249
|
+
}
|
|
1250
|
+
const now = Date.now();
|
|
1251
|
+
const stale = snap.expiresAt <= now;
|
|
1252
|
+
if (stale && !snap.refreshing) {
|
|
1253
|
+
snap.refreshing = true;
|
|
1254
|
+
void asyncRefresh3(rt, appId);
|
|
1255
|
+
}
|
|
1256
|
+
if (opts.archetype) {
|
|
1257
|
+
return snap.data.filter((f) => f.archetype === opts.archetype);
|
|
1258
|
+
}
|
|
1259
|
+
return snap.data;
|
|
1260
|
+
}
|
|
1261
|
+
async function asyncRefresh3(rt, appId) {
|
|
1262
|
+
const promise = doRefresh3(rt, appId);
|
|
1263
|
+
pendingRefreshes3.set(appId, promise);
|
|
1264
|
+
try {
|
|
1265
|
+
await promise;
|
|
1266
|
+
} finally {
|
|
1267
|
+
if (pendingRefreshes3.get(appId) === promise) {
|
|
1268
|
+
pendingRefreshes3.delete(appId);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
async function doRefresh3(rt, appId) {
|
|
1273
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
1274
|
+
let snap = snapshots3.get(appId);
|
|
1275
|
+
if (!snap) {
|
|
1276
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
1277
|
+
snapshots3.set(appId, snap);
|
|
1278
|
+
}
|
|
1279
|
+
try {
|
|
1280
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
1281
|
+
if (!res.ok) {
|
|
1282
|
+
throw new Error(`stale-model findings ${res.status}: ${res.statusText}`);
|
|
1283
|
+
}
|
|
1284
|
+
const body = await res.json();
|
|
1285
|
+
if (runtime3 !== rt) return;
|
|
1286
|
+
const rows = Array.isArray(body) ? mapRowsToFindings3(body) : [];
|
|
1287
|
+
snap.data = rows;
|
|
1288
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1289
|
+
snap.refreshing = false;
|
|
1290
|
+
} catch (err) {
|
|
1291
|
+
if (runtime3 !== rt) return;
|
|
1292
|
+
snap.refreshing = false;
|
|
1293
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
1294
|
+
if (!warnedOnce3) {
|
|
1295
|
+
warnedOnce3 = true;
|
|
1296
|
+
(rt.onError ?? defaultOnError3)(err);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
function defaultOnError3(err) {
|
|
1301
|
+
console.warn(
|
|
1302
|
+
"[kgauto] stale-model findings fetch failed (using empty fallback):",
|
|
1303
|
+
err
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
var CONSUMER_ON_STALE_MODEL_RULE_CODE = "consumer-on-stale-model";
|
|
1307
|
+
function advisorRuleConsumerOnStaleModel(ir) {
|
|
1308
|
+
if (!isStaleModelFindingsBrainActive()) return [];
|
|
1309
|
+
if (!ir.appId) return [];
|
|
1310
|
+
const findings = getStaleModelFindings({
|
|
1311
|
+
appId: ir.appId,
|
|
1312
|
+
archetype: ir.intent.archetype
|
|
1313
|
+
});
|
|
1314
|
+
if (findings.length === 0) return [];
|
|
1315
|
+
const ranked = [...findings].sort((a, b) => {
|
|
1316
|
+
if (a.staleStatus !== b.staleStatus) {
|
|
1317
|
+
return a.staleStatus === "deprecated" ? -1 : 1;
|
|
1318
|
+
}
|
|
1319
|
+
return a.staleModel.localeCompare(b.staleModel);
|
|
1320
|
+
});
|
|
1321
|
+
const top = ranked[0];
|
|
1322
|
+
const extraCount = findings.length - 1;
|
|
1323
|
+
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more stale model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
1324
|
+
return [
|
|
1325
|
+
{
|
|
1326
|
+
level: "warn",
|
|
1327
|
+
code: CONSUMER_ON_STALE_MODEL_RULE_CODE,
|
|
1328
|
+
message: `${top.message}${extraNote}`,
|
|
1329
|
+
suggestion: top.suggestion ?? `Migrate ${top.staleModel} \u2192 ${top.recommendedModel} for archetype "${top.archetype}". The newer model is the current latest in the "${top.family}" family; the stale one is ${top.staleStatus}.`,
|
|
1330
|
+
recommendationType: "model-swap",
|
|
1331
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1332
|
+
}
|
|
1333
|
+
];
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// src/advisor.ts
|
|
1337
|
+
var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
|
|
1338
|
+
var TIER_DOWN_COST_RATIO = 0.5;
|
|
1339
|
+
var COST_MISMATCHED_CHOSEN_SCORE_CEILING = 7;
|
|
1340
|
+
var PRODUCER_OWNED_RULE_CODES = Object.freeze(
|
|
1341
|
+
/* @__PURE__ */ new Set(["model-stale-evidence", "promote-ready"])
|
|
1342
|
+
);
|
|
1343
|
+
function deriveOwnership(code, selfDeclared) {
|
|
1344
|
+
if (selfDeclared) return selfDeclared;
|
|
1345
|
+
return PRODUCER_OWNED_RULE_CODES.has(code) ? "producer-owned" : "consumer-actionable";
|
|
1346
|
+
}
|
|
1347
|
+
function runAdvisor(ir, result, profile, policy, phase2) {
|
|
1348
|
+
const out = [];
|
|
1349
|
+
out.push(...detectCachingOff(ir, profile));
|
|
1350
|
+
out.push(...detectSingleChunkSystem(ir, profile));
|
|
1351
|
+
out.push(...detectToolBloat(ir, result));
|
|
1352
|
+
out.push(...detectHistoryUncached(ir, profile));
|
|
1353
|
+
out.push(...detectSingleModelArray(ir, policy));
|
|
1354
|
+
if (policy?.posture !== "locked") {
|
|
1355
|
+
out.push(...detectCostMismatchedArchetype(ir, profile, phase2));
|
|
1356
|
+
out.push(...detectModelStaleEvidence(ir, profile));
|
|
1357
|
+
out.push(...detectTierDown(ir, profile, phase2));
|
|
1358
|
+
}
|
|
1359
|
+
if (!translatorClearedToolCallCliff(phase2)) {
|
|
1360
|
+
out.push(...detectArchetypePerfFloorBreach(ir, profile));
|
|
1361
|
+
}
|
|
1362
|
+
if (policy?.posture !== "locked") {
|
|
1363
|
+
out.push(...detectStaleExclusionCandidate(ir));
|
|
1364
|
+
}
|
|
1365
|
+
if (policy?.posture !== "locked" && ir.appId) {
|
|
1366
|
+
out.push(
|
|
1367
|
+
...advisorRulePromoteReady({
|
|
1368
|
+
appId: ir.appId,
|
|
1369
|
+
archetype: ir.intent.archetype,
|
|
1370
|
+
resolvedPrimary: profile.id
|
|
1371
|
+
})
|
|
1372
|
+
);
|
|
1373
|
+
out.push(...advisorRuleConsumerOnStaleModel(ir));
|
|
1374
|
+
}
|
|
1375
|
+
return out;
|
|
1376
|
+
}
|
|
1377
|
+
function translatorClearedToolCallCliff(phase2) {
|
|
1378
|
+
const rewrites = phase2?.sectionRewritesApplied;
|
|
1379
|
+
if (!rewrites || rewrites.length === 0) return false;
|
|
1380
|
+
for (const rw of rewrites) {
|
|
1381
|
+
if (rw.kind === "tool_call_contract") return true;
|
|
1382
|
+
}
|
|
1383
|
+
return false;
|
|
1384
|
+
}
|
|
1385
|
+
function detectCachingOff(ir, profile) {
|
|
1386
|
+
if (profile.provider !== "anthropic") return [];
|
|
1387
|
+
const totalChars = ir.sections.reduce((s, sec) => s + sec.text.length, 0);
|
|
1388
|
+
if (totalChars < 2e3) return [];
|
|
1389
|
+
const anyCacheable = ir.sections.some((s) => s.cacheable === true);
|
|
1390
|
+
if (anyCacheable) return [];
|
|
1391
|
+
return [
|
|
1392
|
+
{
|
|
1393
|
+
level: "warn",
|
|
1394
|
+
code: "caching-off-on-claude",
|
|
1395
|
+
message: `System prompt is ${totalChars} chars on Anthropic but no PromptSection has cacheable=true. Anthropic prompt caching cuts cached-prefix input cost by ~90% on subsequent calls; without it, every turn re-pays full price for the static system context.`,
|
|
1396
|
+
suggestion: "Mark stable system sections (role, persona, tool policy) with `cacheable: true`. The lowering pass concatenates cacheable sections into a single cache-controlled block before the dynamic ones.",
|
|
1397
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1398
|
+
}
|
|
1399
|
+
];
|
|
1400
|
+
}
|
|
1401
|
+
function detectSingleChunkSystem(ir, profile) {
|
|
1402
|
+
if (profile.provider !== "anthropic") return [];
|
|
1403
|
+
if (ir.sections.length !== 1) return [];
|
|
1404
|
+
const only = ir.sections[0];
|
|
1405
|
+
if (!only || only.text.length <= 1e3) return [];
|
|
1406
|
+
return [
|
|
1407
|
+
{
|
|
1408
|
+
level: "info",
|
|
1409
|
+
code: "single-chunk-system",
|
|
1410
|
+
message: `System prompt is a single ${only.text.length}-char chunk. Splitting into NamedChunks (static role/persona vs dynamic context) gives the lowering pass a finer cache-marker boundary \u2014 only the static portion needs to be byte-stable for the cache to hit.`,
|
|
1411
|
+
suggestion: "Refactor the system builder to return an array of `PromptSection` shaped { id, text, cacheable?: boolean }. Static chunks (role, persona, tool policy) get `cacheable: true`; dynamic ones (current context, today's date) don't.",
|
|
1412
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1413
|
+
}
|
|
1414
|
+
];
|
|
1415
|
+
}
|
|
1416
|
+
function detectToolBloat(ir, result) {
|
|
1417
|
+
const SHORT_OUTPUT = /* @__PURE__ */ new Set([
|
|
1418
|
+
"classify",
|
|
1419
|
+
"extract",
|
|
1420
|
+
"summarize",
|
|
1421
|
+
"transform",
|
|
1422
|
+
"critique"
|
|
1423
|
+
]);
|
|
1424
|
+
if (!ir.tools || ir.tools.length === 0) return [];
|
|
1425
|
+
const toolsKept = result.diagnostics.toolsKept;
|
|
1426
|
+
if (toolsKept <= 10) return [];
|
|
1427
|
+
if (!SHORT_OUTPUT.has(ir.intent.archetype)) return [];
|
|
1428
|
+
return [
|
|
1429
|
+
{
|
|
1430
|
+
level: "warn",
|
|
1431
|
+
code: "tool-bloat",
|
|
1432
|
+
message: `${toolsKept} tools kept after the relevance pass for archetype="${ir.intent.archetype}" (consumer declared ${ir.tools.length}). This archetype is short-output and rarely needs more than 3 tools; each tool definition eats ~350 tokens of context budget.`,
|
|
1433
|
+
suggestion: "Tighten `relevanceByIntent: { [archetype]: 0..1 }` per ToolDefinition. Tools below `toolRelevanceThreshold` (default 0.2) get dropped. Without `relevanceByIntent`, every tool defaults to neutral (0.5) and stays.",
|
|
1434
|
+
docsUrl: "https://github.com/stue/kgauto/blob/main/v2/README.md#tools"
|
|
1435
|
+
}
|
|
1436
|
+
];
|
|
1437
|
+
}
|
|
1438
|
+
function detectHistoryUncached(ir, profile) {
|
|
1439
|
+
if (profile.provider !== "anthropic") return [];
|
|
1440
|
+
if (!ir.history || ir.history.length < 2) return [];
|
|
1441
|
+
if (ir.historyCachePolicy && ir.historyCachePolicy.strategy !== "none") {
|
|
1442
|
+
return [];
|
|
1443
|
+
}
|
|
1444
|
+
return [
|
|
1445
|
+
{
|
|
1446
|
+
level: "warn",
|
|
1447
|
+
code: "history-uncached-on-claude",
|
|
1448
|
+
message: `${ir.history.length} history messages on Anthropic with no historyCachePolicy. Every turn re-pays for the full conversation context; with caching, subsequent turns hit the cache at ~10% the input cost.`,
|
|
1449
|
+
suggestion: "Set `historyCachePolicy: { strategy: 'all-but-latest' }` on this IR. The lowering pass marks the message immediately preceding currentTurn with cache_control; subsequent turns whose history prefix matches byte-for-byte hit the cache.",
|
|
1450
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1451
|
+
}
|
|
1452
|
+
];
|
|
1453
|
+
}
|
|
1454
|
+
function detectSingleModelArray(ir, policy) {
|
|
1455
|
+
if (ir.models.length !== 1) return [];
|
|
1456
|
+
if (policy?.posture === "locked") return [];
|
|
1457
|
+
const only = ir.models[0];
|
|
1458
|
+
return [
|
|
1459
|
+
{
|
|
1460
|
+
level: "warn",
|
|
1461
|
+
code: "single-model-array",
|
|
1462
|
+
message: `\`ir.models\` has length 1 (only "${only}") and posture is not 'locked'. A single-model chain has no safety net \u2014 the first 429 / 5xx / cliff hits the user as a failure. Master plan \xA71.2 closes the reliability gap with a 2-step minimum.`,
|
|
1463
|
+
suggestion: "Use `getDefaultFallbackChain({ archetype: ir.intent.archetype, primary: '" + only + "', posture: 'preferred' })` for a user-anchored chain, or `getDefaultFallbackChain({ archetype, posture: 'open' })` for library-picked. If single-model is intentional (compliance/brand promise), set `policy.posture = 'locked'` to silence this rule.",
|
|
1464
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#single-model-array"
|
|
1465
|
+
}
|
|
1466
|
+
];
|
|
1467
|
+
}
|
|
1468
|
+
function detectCostMismatchedArchetype(ir, profile, phase2) {
|
|
1469
|
+
if (!phase2 || phase2.fallbackChain.length === 0) return [];
|
|
1470
|
+
if (!phase2.profileResolver) return [];
|
|
1471
|
+
const archetype = ir.intent.archetype;
|
|
1472
|
+
const chosenScore = getArchetypePerfScore(profile.id, archetype);
|
|
1473
|
+
const chosenHasRoomToGrow = chosenScore.grounding === "judgment" || chosenScore.score < COST_MISMATCHED_CHOSEN_SCORE_CEILING;
|
|
1474
|
+
if (!chosenHasRoomToGrow) return [];
|
|
1475
|
+
let bestAlt = null;
|
|
1476
|
+
for (const altId of phase2.fallbackChain) {
|
|
1477
|
+
const altProfile = phase2.profileResolver(altId);
|
|
1478
|
+
if (!altProfile) continue;
|
|
1479
|
+
if (altProfile.id === profile.id) continue;
|
|
1480
|
+
const altScore = getArchetypePerfScore(altProfile.id, archetype);
|
|
1481
|
+
if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
|
|
1482
|
+
if (altScore.score < chosenScore.score) continue;
|
|
1483
|
+
if (altProfile.costInputPer1m >= profile.costInputPer1m) continue;
|
|
1484
|
+
if (!bestAlt || altScore.score > bestAlt.score.score || altScore.score === bestAlt.score.score && altProfile.costInputPer1m < bestAlt.profile.costInputPer1m) {
|
|
1485
|
+
bestAlt = { id: altId, profile: altProfile, score: altScore };
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
if (!bestAlt) return [];
|
|
1489
|
+
const tierDownWouldFire = bestAlt.score.grounding === "measured" && bestAlt.profile.costInputPer1m <= profile.costInputPer1m * TIER_DOWN_COST_RATIO;
|
|
1490
|
+
if (tierDownWouldFire) return [];
|
|
1491
|
+
const chosenGrounding = chosenScore.grounding === "judgment" ? `archetypePerf.${archetype}=judgment` : `archetypePerf.${archetype}=${chosenScore.score}`;
|
|
1492
|
+
const altGrounding = bestAlt.score.grounding === "measured" ? `archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}` : `archetypePerf.${archetype}=${bestAlt.score.score}, judgment`;
|
|
1493
|
+
return [
|
|
1494
|
+
{
|
|
1495
|
+
level: "warn",
|
|
1496
|
+
code: "cost-mismatched-archetype",
|
|
1497
|
+
message: `Cost-mismatched-archetype: target=${profile.id} (${chosenGrounding}) selected for ${archetype}. Alternative ${bestAlt.id} (${altGrounding}) is cheaper ($${bestAlt.profile.costInputPer1m}/$${bestAlt.profile.costOutputPer1m} vs $${profile.costInputPer1m}/$${profile.costOutputPer1m} per 1M) at equal-or-better quality.`,
|
|
1498
|
+
suggestion: `Consider declaring \`${bestAlt.id}\` as the primary model for this archetype, or relax to posture='open' to let kgauto select among the chain. If the chosen model is required for compliance/brand reasons, set \`policy.posture = 'locked'\` to silence this rule.`,
|
|
1499
|
+
recommendationType: profile.provider === bestAlt.profile.provider ? "tier-down" : "model-swap",
|
|
1500
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1501
|
+
}
|
|
1502
|
+
];
|
|
1503
|
+
}
|
|
1504
|
+
function detectModelStaleEvidence(ir, profile) {
|
|
1505
|
+
if (!isBrainQueryActiveFor("kgauto_archetype_perf")) return [];
|
|
1506
|
+
const archetype = ir.intent.archetype;
|
|
1507
|
+
const chosen = getArchetypePerfScore(profile.id, archetype);
|
|
1508
|
+
if (chosen.grounding !== "judgment") return [];
|
|
1509
|
+
return [
|
|
1510
|
+
{
|
|
1511
|
+
level: "info",
|
|
1512
|
+
code: "model-stale-evidence",
|
|
1513
|
+
message: `Model-stale-evidence: target=${profile.id} archetype=${archetype} is judgment-grounded (n=${chosen.n}) despite brain-query mode being active. Measurement substrate is wired but the brain hasn't accumulated >=10 outcomes for this (model, archetype) tuple yet \u2014 routing decisions remain pre-measured for this slot.`,
|
|
1514
|
+
suggestion: "Verify that `record()` is being called on every call() outcome with the appropriate `actualModel` and `mutationsApplied` fields. Once the brain accumulates n>=10 rows on this tuple, the score promotes from judgment to measured automatically (5-min SWR cache). No code change required from your side \u2014 this is the substrate signaling the gap.",
|
|
1515
|
+
recommendationType: "prompt-fix",
|
|
1516
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1517
|
+
}
|
|
1518
|
+
];
|
|
1519
|
+
}
|
|
1520
|
+
function detectTierDown(ir, profile, phase2) {
|
|
1521
|
+
if (!phase2 || phase2.fallbackChain.length === 0) return [];
|
|
1522
|
+
if (!phase2.profileResolver) return [];
|
|
1523
|
+
const archetype = ir.intent.archetype;
|
|
1524
|
+
const chosenScore = getArchetypePerfScore(profile.id, archetype);
|
|
1525
|
+
const chosenCost = profile.costInputPer1m;
|
|
1526
|
+
let bestAlt = null;
|
|
1527
|
+
for (const altId of phase2.fallbackChain) {
|
|
1528
|
+
const altProfile = phase2.profileResolver(altId);
|
|
1529
|
+
if (!altProfile) continue;
|
|
1530
|
+
if (altProfile.id === profile.id) continue;
|
|
1531
|
+
const altScore = getArchetypePerfScore(altProfile.id, archetype);
|
|
1532
|
+
if (altScore.grounding !== "measured") continue;
|
|
1533
|
+
if (altScore.score < QUALITY_FLOOR_FOR_RECOMMENDATION) continue;
|
|
1534
|
+
if (altScore.score < chosenScore.score) continue;
|
|
1535
|
+
if (altProfile.costInputPer1m > chosenCost * TIER_DOWN_COST_RATIO) continue;
|
|
1536
|
+
if (!bestAlt || altProfile.costInputPer1m < bestAlt.profile.costInputPer1m || altProfile.costInputPer1m === bestAlt.profile.costInputPer1m && altScore.score > bestAlt.score.score) {
|
|
1537
|
+
bestAlt = { id: altId, profile: altProfile, score: altScore };
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (!bestAlt) return [];
|
|
1541
|
+
const chosenDesc = chosenScore.grounding === "measured" ? `archetypePerf.${archetype}=${chosenScore.score} (measured, n=${chosenScore.n})` : `archetypePerf.${archetype}=${chosenScore.score} (${chosenScore.grounding})`;
|
|
1542
|
+
return [
|
|
1543
|
+
{
|
|
1544
|
+
level: "warn",
|
|
1545
|
+
code: "tier-down",
|
|
1546
|
+
message: `Tier-down: target=${profile.id} (${chosenDesc}) selected for ${archetype}. Brain shows ${bestAlt.id} delivers equal-or-better quality (archetypePerf.${archetype}=${bestAlt.score.score}, measured, n=${bestAlt.score.n}) at $${bestAlt.profile.costInputPer1m}/$${bestAlt.profile.costOutputPer1m} per 1M vs $${profile.costInputPer1m}/$${profile.costOutputPer1m} \u2014 a measured tier-down opportunity.`,
|
|
1547
|
+
suggestion: `Move \`${bestAlt.id}\` to primary for this archetype. The brain has n=${bestAlt.score.n} measured outcomes backing the recommendation; this is data, not opinion. If posture='locked' is required (compliance/brand promise), set it explicitly to silence this rule.`,
|
|
1548
|
+
recommendationType: "tier-down",
|
|
1549
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1550
|
+
}
|
|
1551
|
+
];
|
|
1552
|
+
}
|
|
1553
|
+
function detectArchetypePerfFloorBreach(ir, profile) {
|
|
1554
|
+
const compat = getModelCompatibility(profile.id, {
|
|
1555
|
+
archetype: ir.intent.archetype,
|
|
1556
|
+
toolOrchestration: ir.constraints?.toolOrchestration
|
|
1557
|
+
});
|
|
1558
|
+
if (compat.status === "compatible") return [];
|
|
1559
|
+
if (compat.status === "requires-adapter") {
|
|
1560
|
+
return [
|
|
1561
|
+
{
|
|
1562
|
+
level: "warn",
|
|
1563
|
+
code: "archetype-perf-floor-breach",
|
|
1564
|
+
message: `${profile.id} sits below the archetype floor for ${ir.intent.archetype} (score ${compat.archetypePerf}/10, floor ${6}). A known adapter would lift it: ${compat.adapter.parameter}=${compat.adapter.value}. ${compat.adapter.consequence}`,
|
|
1565
|
+
suggestion: `Pass \`ir.constraints.${compat.adapter.parameter} = '${compat.adapter.value}'\` for this call, OR pick a model whose archetypePerf for ${ir.intent.archetype} already clears the floor (call \`getModelCompatibility(modelId, { archetype: '${ir.intent.archetype}' })\` to check). Estimated post-adapter score: ${compat.archetypePerfWithAdapter}/10.`,
|
|
1566
|
+
recommendationType: "prompt-fix",
|
|
1567
|
+
suggestedAdaptation: compat.adapter,
|
|
1568
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1569
|
+
}
|
|
1570
|
+
];
|
|
1571
|
+
}
|
|
1572
|
+
return [
|
|
1573
|
+
{
|
|
1574
|
+
level: "critical",
|
|
1575
|
+
code: "archetype-perf-floor-breach",
|
|
1576
|
+
message: `${profile.id} sits below the archetype floor for ${ir.intent.archetype} (score ${compat.archetypePerf}/10, floor ${6}) and no known adapter would lift it. ${compat.reason}`,
|
|
1577
|
+
suggestion: `Swap to a model whose archetypePerf for ${ir.intent.archetype} clears the floor. Use \`getModelCompatibility(candidateId, { archetype: '${ir.intent.archetype}' })\` to vet candidates, or \`getDefaultFallbackChain({ archetype: '${ir.intent.archetype}', posture: 'open' })\` for a library-picked chain that respects the floor by construction.`,
|
|
1578
|
+
recommendationType: "model-swap",
|
|
1579
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1580
|
+
}
|
|
1581
|
+
];
|
|
1582
|
+
}
|
|
1583
|
+
function detectStaleExclusionCandidate(ir) {
|
|
1584
|
+
if (!isExclusionFindingsBrainActive()) return [];
|
|
1585
|
+
if (!ir.appId) return [];
|
|
1586
|
+
const findings = getStaleExclusionFindings({
|
|
1587
|
+
appId: ir.appId,
|
|
1588
|
+
archetype: ir.intent.archetype
|
|
1589
|
+
});
|
|
1590
|
+
if (findings.length === 0) return [];
|
|
1591
|
+
const ranked = [...findings].sort((a, b) => {
|
|
1592
|
+
const sa = a.estimatedSavingsUsd30d ?? -Infinity;
|
|
1593
|
+
const sb = b.estimatedSavingsUsd30d ?? -Infinity;
|
|
1594
|
+
if (sa !== sb) return sb - sa;
|
|
1595
|
+
return confidenceRank(b.confidence) - confidenceRank(a.confidence);
|
|
1596
|
+
});
|
|
1597
|
+
const top = ranked[0];
|
|
1598
|
+
const extraCount = findings.length - 1;
|
|
1599
|
+
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more excluded model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
1600
|
+
return [
|
|
1601
|
+
{
|
|
1602
|
+
level: "info",
|
|
1603
|
+
code: "stale-exclusion-candidate",
|
|
1604
|
+
message: `${top.message}${extraNote}`,
|
|
1605
|
+
suggestion: top.suggestion,
|
|
1606
|
+
recommendationType: "tier-down",
|
|
1607
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1608
|
+
}
|
|
1609
|
+
];
|
|
1610
|
+
}
|
|
1611
|
+
function confidenceRank(c) {
|
|
1612
|
+
if (c === "high") return 3;
|
|
1613
|
+
if (c === "medium") return 2;
|
|
1614
|
+
return 1;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
// src/translator.ts
|
|
1618
|
+
var TRANSLATOR_FLOOR = ARCHETYPE_FLOOR_DEFAULT;
|
|
1619
|
+
var RULE_SEQUENTIAL_TOOL_CLIFF = "sequential-tool-cliff-below-floor";
|
|
1620
|
+
var RULE_NARRATION_DRIFT_ANTHROPIC = "narration-drift-anthropic";
|
|
1621
|
+
var RULE_NARRATION_THINKING_LEAK_DEEPSEEK = "narration-thinking-leak-deepseek";
|
|
1622
|
+
var SEQUENTIAL_TOOL_PREAMBLE = "IMPORTANT: Use one tool call per response. Wait for the tool result before deciding the next tool. Do NOT batch tool calls in parallel.";
|
|
1623
|
+
var NARRATION_DRIFT_ANTHROPIC_PREAMBLE = "Output ONLY the requested content. Do not narrate your thought process. Each line \u2264 12 words.";
|
|
1624
|
+
var NARRATION_THINKING_LEAK_DEEPSEEK_PREAMBLE = "Reasoning is internal. Output ONLY the requested content; do not emit <thinking> blocks or internal monologue as user-facing text.";
|
|
1625
|
+
function matchRule(kind, profile, archetype) {
|
|
1626
|
+
if (kind === "tool_call_contract") {
|
|
1627
|
+
if (!profile.archetypePerf) return null;
|
|
1628
|
+
const archetypeScore = profile.archetypePerf[archetype];
|
|
1629
|
+
if (typeof archetypeScore !== "number" || archetypeScore >= TRANSLATOR_FLOOR) {
|
|
1630
|
+
return null;
|
|
1631
|
+
}
|
|
1632
|
+
return {
|
|
1633
|
+
id: RULE_SEQUENTIAL_TOOL_CLIFF,
|
|
1634
|
+
preamble: SEQUENTIAL_TOOL_PREAMBLE,
|
|
1635
|
+
wireOverrides: { parallelToolCalls: false }
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
if (kind === "narration_contract") {
|
|
1639
|
+
if (profile.provider === "anthropic") {
|
|
1640
|
+
return {
|
|
1641
|
+
id: RULE_NARRATION_DRIFT_ANTHROPIC,
|
|
1642
|
+
preamble: NARRATION_DRIFT_ANTHROPIC_PREAMBLE
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
if (profile.provider === "deepseek") {
|
|
1646
|
+
return {
|
|
1647
|
+
id: RULE_NARRATION_THINKING_LEAK_DEEPSEEK,
|
|
1648
|
+
preamble: NARRATION_THINKING_LEAK_DEEPSEEK_PREAMBLE
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
return null;
|
|
1652
|
+
}
|
|
1653
|
+
return null;
|
|
1654
|
+
}
|
|
1655
|
+
function applySectionRewrites(args) {
|
|
1656
|
+
const { ir, profile, archetype } = args;
|
|
1657
|
+
if (!Array.isArray(ir.sections) || ir.sections.length === 0) {
|
|
1658
|
+
return { rewrittenIR: ir, rewrites: [] };
|
|
1659
|
+
}
|
|
1660
|
+
const rewrites = [];
|
|
1661
|
+
const newSections = ir.sections.map((section) => {
|
|
1662
|
+
if (!section.kind || section.kind === "arbitrary") return section;
|
|
1663
|
+
const rule = matchRule(section.kind, profile, archetype);
|
|
1664
|
+
if (!rule) return section;
|
|
1665
|
+
const originalText = section.text;
|
|
1666
|
+
const transformedText = `${rule.preamble}
|
|
1667
|
+
|
|
1668
|
+
${originalText}`;
|
|
1669
|
+
rewrites.push({
|
|
1670
|
+
sectionId: section.id,
|
|
1671
|
+
kind: section.kind,
|
|
1672
|
+
rule: rule.id,
|
|
1673
|
+
originalText,
|
|
1674
|
+
transformedText,
|
|
1675
|
+
...rule.wireOverrides ? { wireOverrides: rule.wireOverrides } : {}
|
|
1676
|
+
});
|
|
1677
|
+
return { ...section, text: transformedText };
|
|
1678
|
+
});
|
|
1679
|
+
if (rewrites.length === 0) {
|
|
1680
|
+
return { rewrittenIR: ir, rewrites: [] };
|
|
1681
|
+
}
|
|
1682
|
+
const rewrittenIR = { ...ir, sections: newSections };
|
|
1683
|
+
return { rewrittenIR, rewrites };
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// src/models-brain.ts
|
|
1687
|
+
function isModelRow(x) {
|
|
1688
|
+
if (!x || typeof x !== "object") return false;
|
|
1689
|
+
const r = x;
|
|
1690
|
+
return typeof r.model_id === "string" && typeof r.provider === "string";
|
|
1691
|
+
}
|
|
1692
|
+
function isAliasRow(x) {
|
|
1693
|
+
if (!x || typeof x !== "object") return false;
|
|
1694
|
+
const r = x;
|
|
1695
|
+
return typeof r.alias_id === "string" && typeof r.canonical_id === "string";
|
|
1696
|
+
}
|
|
1697
|
+
function rowToProfile(row) {
|
|
1698
|
+
try {
|
|
1699
|
+
if (row.cliffs !== void 0 && row.cliffs !== null && !Array.isArray(row.cliffs)) {
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1702
|
+
if (row.recovery !== void 0 && row.recovery !== null && !Array.isArray(row.recovery)) {
|
|
1703
|
+
return null;
|
|
1704
|
+
}
|
|
1705
|
+
if (row.lowering !== void 0 && row.lowering !== null && (typeof row.lowering !== "object" || Array.isArray(row.lowering))) {
|
|
1706
|
+
return null;
|
|
1707
|
+
}
|
|
1708
|
+
return {
|
|
1709
|
+
id: row.model_id,
|
|
1710
|
+
provider: row.provider,
|
|
1711
|
+
status: row.status ?? "current",
|
|
1712
|
+
maxContextTokens: row.max_context_tokens ?? 0,
|
|
1713
|
+
maxOutputTokens: row.max_output_tokens ?? 0,
|
|
1714
|
+
maxTools: row.max_tools ?? 0,
|
|
1715
|
+
parallelToolCalls: row.parallel_tool_calls ?? false,
|
|
1716
|
+
structuredOutput: row.structured_output ?? "none",
|
|
1717
|
+
systemPromptMode: row.system_prompt_mode ?? "inline",
|
|
1718
|
+
streaming: row.streaming ?? true,
|
|
1719
|
+
cliffs: row.cliffs ?? [],
|
|
1720
|
+
costInputPer1m: row.cost_input_per_1m ?? 0,
|
|
1721
|
+
costOutputPer1m: row.cost_output_per_1m ?? 0,
|
|
1722
|
+
lowering: row.lowering ?? { system: { mode: "inline" }, cache: { strategy: "unsupported" } },
|
|
1723
|
+
recovery: row.recovery ?? [],
|
|
1724
|
+
strengths: row.strengths ?? [],
|
|
1725
|
+
weaknesses: row.weaknesses ?? [],
|
|
1726
|
+
notes: row.notes ?? void 0,
|
|
1727
|
+
verifiedAgainstDocs: row.verified_against_docs ?? void 0,
|
|
1728
|
+
archetypePerf: row.archetype_perf ?? void 0,
|
|
1729
|
+
// alpha.41 — family-resolution fields. `family` may be null pre-
|
|
1730
|
+
// migration 024; runtime falls back to deriveFamilyFromModelId at
|
|
1731
|
+
// resolution time (see family-resolution.ts, G1).
|
|
1732
|
+
family: row.family ?? void 0,
|
|
1733
|
+
versionAdded: row.version_added ?? void 0,
|
|
1734
|
+
active: row.active ?? void 0
|
|
1735
|
+
};
|
|
1736
|
+
} catch {
|
|
1737
|
+
return null;
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
function profileToRow(profile, opts = {}) {
|
|
1741
|
+
const row = {
|
|
1742
|
+
model_id: profile.id,
|
|
1743
|
+
provider: profile.provider,
|
|
1744
|
+
status: profile.status,
|
|
1745
|
+
max_context_tokens: profile.maxContextTokens,
|
|
1746
|
+
max_output_tokens: profile.maxOutputTokens,
|
|
1747
|
+
max_tools: profile.maxTools,
|
|
1748
|
+
parallel_tool_calls: profile.parallelToolCalls,
|
|
1749
|
+
structured_output: profile.structuredOutput,
|
|
1750
|
+
system_prompt_mode: profile.systemPromptMode,
|
|
1751
|
+
streaming: profile.streaming,
|
|
1752
|
+
cliffs: profile.cliffs,
|
|
1753
|
+
cost_input_per_1m: profile.costInputPer1m,
|
|
1754
|
+
cost_output_per_1m: profile.costOutputPer1m,
|
|
1755
|
+
lowering: profile.lowering,
|
|
1756
|
+
recovery: profile.recovery,
|
|
1757
|
+
strengths: profile.strengths,
|
|
1758
|
+
weaknesses: profile.weaknesses,
|
|
1759
|
+
notes: profile.notes ?? null,
|
|
1760
|
+
archetype_perf: profile.archetypePerf ?? null,
|
|
1761
|
+
active: opts.active ?? profile.active ?? true,
|
|
1762
|
+
// alpha.41 — round-trip family + version_added when present on profile.
|
|
1763
|
+
// version_added is operator-controlled via opts; profile-side value is
|
|
1764
|
+
// used only when opts didn't override.
|
|
1765
|
+
family: profile.family ?? null
|
|
1766
|
+
};
|
|
1767
|
+
if (opts.verifiedAgainstDocs !== void 0) {
|
|
1768
|
+
row.verified_against_docs = opts.verifiedAgainstDocs;
|
|
1769
|
+
} else if (profile.verifiedAgainstDocs !== void 0) {
|
|
1770
|
+
const v = profile.verifiedAgainstDocs;
|
|
1771
|
+
row.verified_against_docs = /^\d{4}-\d{2}-\d{2}/.test(v) ? v : null;
|
|
1772
|
+
}
|
|
1773
|
+
if (opts.versionAdded !== void 0) row.version_added = opts.versionAdded;
|
|
1774
|
+
else if (profile.versionAdded !== void 0) row.version_added = profile.versionAdded;
|
|
1775
|
+
if (opts.versionRemoved !== void 0) row.version_removed = opts.versionRemoved;
|
|
1776
|
+
return row;
|
|
1777
|
+
}
|
|
1778
|
+
function mapRowsToModels(rows) {
|
|
1779
|
+
const out = /* @__PURE__ */ new Map();
|
|
1780
|
+
for (const row of rows) {
|
|
1781
|
+
if (!isModelRow(row)) continue;
|
|
1782
|
+
const profile = rowToProfile(row);
|
|
1783
|
+
if (profile) out.set(profile.id, profile);
|
|
1784
|
+
}
|
|
1785
|
+
return out;
|
|
1786
|
+
}
|
|
1787
|
+
function mapRowsToAliases(rows) {
|
|
1788
|
+
const out = {};
|
|
1789
|
+
for (const row of rows) {
|
|
1790
|
+
if (!isAliasRow(row)) continue;
|
|
1791
|
+
out[row.alias_id] = row.canonical_id;
|
|
1792
|
+
}
|
|
1793
|
+
return out;
|
|
1794
|
+
}
|
|
1795
|
+
function bundledModels() {
|
|
1796
|
+
return new Map(allProfilesRaw().map((p) => [p.id, p]));
|
|
1797
|
+
}
|
|
1798
|
+
function bundledAliases() {
|
|
1799
|
+
return { ...ALIASES };
|
|
1800
|
+
}
|
|
1801
|
+
var loadModelsFromBrain = createBrainQueryCache({
|
|
1802
|
+
table: "kgauto_models",
|
|
1803
|
+
mapRows: mapRowsToModels,
|
|
1804
|
+
bundledFallback: bundledModels
|
|
1805
|
+
});
|
|
1806
|
+
var loadAliasesFromBrain = createBrainQueryCache({
|
|
1807
|
+
table: "kgauto_aliases",
|
|
1808
|
+
mapRows: mapRowsToAliases,
|
|
1809
|
+
bundledFallback: bundledAliases
|
|
1810
|
+
});
|
|
1811
|
+
_setProfileBrainHook({
|
|
1812
|
+
getProfile: (canonical) => loadModelsFromBrain().get(canonical),
|
|
1813
|
+
resolveAlias: (id) => loadAliasesFromBrain()[id]
|
|
1814
|
+
});
|
|
1815
|
+
|
|
1816
|
+
// src/family-resolution.ts
|
|
1817
|
+
var FamilyResolutionError = class extends Error {
|
|
1818
|
+
family;
|
|
1819
|
+
cause;
|
|
1820
|
+
constructor(family, cause) {
|
|
1821
|
+
super(
|
|
1822
|
+
`Family "${family}" did not resolve to any current+active model. ${cause}. Pass a literal model id in ir.models, or call getRecommendedPrimary({ family, fallback }) at IR-construction time.`
|
|
1823
|
+
);
|
|
1824
|
+
this.name = "FamilyResolutionError";
|
|
1825
|
+
this.family = family;
|
|
1826
|
+
this.cause = cause;
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
function deriveFamilyFromModelId(modelId) {
|
|
1830
|
+
if (typeof modelId !== "string" || modelId.length === 0) return null;
|
|
1831
|
+
if (modelId.startsWith("claude-opus-")) return "claude-opus";
|
|
1832
|
+
if (modelId.startsWith("claude-sonnet-")) return "claude-sonnet";
|
|
1833
|
+
if (modelId.startsWith("claude-haiku-")) return "claude-haiku";
|
|
1834
|
+
if (modelId.startsWith("gemini-") && modelId.includes("flash-lite")) {
|
|
1835
|
+
return "gemini-flash-lite";
|
|
1836
|
+
}
|
|
1837
|
+
if (modelId.startsWith("gemini-") && modelId.includes("flash")) {
|
|
1838
|
+
return "gemini-flash";
|
|
1839
|
+
}
|
|
1840
|
+
if (modelId.startsWith("gemini-") && modelId.includes("pro")) {
|
|
1841
|
+
return "gemini-pro";
|
|
1842
|
+
}
|
|
1843
|
+
if (modelId.startsWith("deepseek-") && modelId.includes("pro")) {
|
|
1844
|
+
return "deepseek-reasoner";
|
|
1845
|
+
}
|
|
1846
|
+
if (modelId.startsWith("deepseek-")) return "deepseek-chat";
|
|
1847
|
+
if (modelId.startsWith("gpt-")) return "openai-gpt";
|
|
1848
|
+
return null;
|
|
1849
|
+
}
|
|
1850
|
+
function familyOf(profile) {
|
|
1851
|
+
return profile.family ?? deriveFamilyFromModelId(profile.id);
|
|
1852
|
+
}
|
|
1853
|
+
function archetypePerfFor(profile, archetype) {
|
|
1854
|
+
if (!archetype) return 0;
|
|
1855
|
+
const score = profile.archetypePerf?.[archetype];
|
|
1856
|
+
return typeof score === "number" ? score : 5;
|
|
1857
|
+
}
|
|
1858
|
+
function selectCandidates(registry, family, archetype, appId) {
|
|
1859
|
+
const out = [];
|
|
1860
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
1861
|
+
if (appId && archetype) {
|
|
1862
|
+
const findings = getStaleExclusionFindings({ appId, archetype });
|
|
1863
|
+
for (const f of findings) {
|
|
1864
|
+
if (f.verdict === "stay-excluded") excluded.add(f.excludedModel);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
for (const profile of registry.values()) {
|
|
1868
|
+
if (familyOf(profile) !== family) continue;
|
|
1869
|
+
if (profile.status !== "current") continue;
|
|
1870
|
+
if (profile.active === false) continue;
|
|
1871
|
+
if (archetype) {
|
|
1872
|
+
if (archetypePerfFor(profile, archetype) < ARCHETYPE_FLOOR_DEFAULT) continue;
|
|
1873
|
+
}
|
|
1874
|
+
if (excluded.has(profile.id)) continue;
|
|
1875
|
+
out.push(profile);
|
|
1876
|
+
}
|
|
1877
|
+
return out;
|
|
1878
|
+
}
|
|
1879
|
+
function sortCandidates(candidates, archetype) {
|
|
1880
|
+
const sorted = [...candidates];
|
|
1881
|
+
sorted.sort((a, b) => {
|
|
1882
|
+
const perfA = archetypePerfFor(a, archetype);
|
|
1883
|
+
const perfB = archetypePerfFor(b, archetype);
|
|
1884
|
+
if (perfA !== perfB) return perfB - perfA;
|
|
1885
|
+
const vA = a.versionAdded ?? "";
|
|
1886
|
+
const vB = b.versionAdded ?? "";
|
|
1887
|
+
if (vA !== vB) return vA < vB ? 1 : -1;
|
|
1888
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
1889
|
+
});
|
|
1890
|
+
return sorted;
|
|
1891
|
+
}
|
|
1892
|
+
function getRecommendedPrimary(opts) {
|
|
1893
|
+
if (opts.posture === "locked") return opts.fallback;
|
|
1894
|
+
const registry = loadModelsFromBrain();
|
|
1895
|
+
const candidates = selectCandidates(
|
|
1896
|
+
registry,
|
|
1897
|
+
opts.family,
|
|
1898
|
+
opts.archetype,
|
|
1899
|
+
opts.appId
|
|
1900
|
+
);
|
|
1901
|
+
if (candidates.length === 0) return opts.fallback;
|
|
1902
|
+
const sorted = sortCandidates(candidates, opts.archetype);
|
|
1903
|
+
return sorted[0]?.id ?? opts.fallback;
|
|
1904
|
+
}
|
|
1905
|
+
function resolveFamilyEntry(family, ctx) {
|
|
1906
|
+
if (typeof family !== "string" || family.length === 0) {
|
|
1907
|
+
throw new FamilyResolutionError(
|
|
1908
|
+
String(family),
|
|
1909
|
+
"Family tag must be a non-empty string"
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
const registry = loadModelsFromBrain();
|
|
1913
|
+
const candidates = selectCandidates(
|
|
1914
|
+
registry,
|
|
1915
|
+
family,
|
|
1916
|
+
ctx.archetype,
|
|
1917
|
+
ctx.appId
|
|
1918
|
+
);
|
|
1919
|
+
if (candidates.length === 0) {
|
|
1920
|
+
let anyInFamily = false;
|
|
1921
|
+
for (const profile of registry.values()) {
|
|
1922
|
+
if (familyOf(profile) === family) {
|
|
1923
|
+
anyInFamily = true;
|
|
1924
|
+
break;
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
const cause = anyInFamily ? `Family has registered models but none are current+active${ctx.archetype ? ` at archetype-perf >= ${ARCHETYPE_FLOOR_DEFAULT} for "${ctx.archetype}"` : ""}${ctx.appId ? ` and not excluded for app "${ctx.appId}"` : ""}` : `No models in brain registry carry family="${family}". Check the taxonomy table in family-resolution.ts or migration 024`;
|
|
1928
|
+
throw new FamilyResolutionError(family, cause);
|
|
1929
|
+
}
|
|
1930
|
+
const sorted = sortCandidates(candidates, ctx.archetype);
|
|
1931
|
+
const winner = sorted[0];
|
|
1932
|
+
if (!winner) {
|
|
1933
|
+
throw new FamilyResolutionError(
|
|
1934
|
+
family,
|
|
1935
|
+
"Candidates non-empty but sort returned undefined \u2014 internal invariant violation"
|
|
1936
|
+
);
|
|
1937
|
+
}
|
|
1938
|
+
return winner.id;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
// src/compile.ts
|
|
1942
|
+
var counter = 0;
|
|
1943
|
+
function makeHandle() {
|
|
1944
|
+
counter = (counter + 1) % 1e6;
|
|
1945
|
+
return `c${Date.now().toString(36)}-${counter.toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
1946
|
+
}
|
|
1947
|
+
function compile(ir, opts = {}) {
|
|
1948
|
+
const resolver = opts.profileResolver ?? getProfile;
|
|
1949
|
+
validateIR(ir);
|
|
1950
|
+
ir = resolveModelEntries(ir);
|
|
1951
|
+
const sliced = passSlice(ir);
|
|
1952
|
+
const deduped = passDedupe(sliced.value);
|
|
1953
|
+
const toolFiltered = passToolRelevance(deduped.value, {
|
|
1954
|
+
threshold: opts.toolRelevanceThreshold
|
|
1955
|
+
});
|
|
1956
|
+
const compressed = passCompressHistory(toolFiltered.value, {
|
|
1957
|
+
summarizeOlderThan: opts.compressHistoryAfter,
|
|
1958
|
+
summarizeAboveTokens: opts.compressHistoryAboveTokens
|
|
1959
|
+
});
|
|
1960
|
+
let workingIR = compressed.value;
|
|
1961
|
+
const accumulatedMutations = [
|
|
1962
|
+
...sliced.mutations,
|
|
1963
|
+
...deduped.mutations,
|
|
1964
|
+
...toolFiltered.mutations,
|
|
1965
|
+
...compressed.mutations
|
|
1966
|
+
];
|
|
1967
|
+
const inputTokens = estimateInputTokens(workingIR);
|
|
1968
|
+
const scores = passScoreTargets(workingIR, {
|
|
1969
|
+
estimatedInputTokens: inputTokens,
|
|
1970
|
+
profilesById: resolver,
|
|
1971
|
+
policy: opts.policy
|
|
1972
|
+
});
|
|
1973
|
+
accumulatedMutations.push(...scores.mutations);
|
|
1974
|
+
const target = pickTarget(workingIR, scores.value);
|
|
1975
|
+
if (!target) {
|
|
1976
|
+
throw new Error(
|
|
1977
|
+
`compile(): no allowed model fits the request. Scores: ${JSON.stringify(scores.value, null, 2)}`
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
const profile = resolver(target.modelId);
|
|
1981
|
+
const fallbackChain = scores.value.filter((s) => s.modelId !== target.modelId && s.fits).sort((a, b) => b.rank - a.rank).map((s) => s.modelId);
|
|
1982
|
+
const cliffs = passApplyCliffs(workingIR, profile, inputTokens);
|
|
1983
|
+
workingIR = cliffs.value.ir;
|
|
1984
|
+
accumulatedMutations.push(...cliffs.mutations);
|
|
1985
|
+
const translated = applySectionRewrites({
|
|
1986
|
+
ir: workingIR,
|
|
1987
|
+
profile,
|
|
1988
|
+
archetype: ir.intent.archetype
|
|
1989
|
+
});
|
|
1990
|
+
workingIR = translated.rewrittenIR;
|
|
1991
|
+
const sectionRewritesApplied = translated.rewrites;
|
|
1992
|
+
let wireOverrides;
|
|
1993
|
+
for (const rw of sectionRewritesApplied) {
|
|
1994
|
+
if (!rw.wireOverrides) continue;
|
|
1995
|
+
if (!wireOverrides) wireOverrides = {};
|
|
1996
|
+
if (rw.wireOverrides.parallelToolCalls !== void 0) {
|
|
1997
|
+
wireOverrides.parallelToolCalls = rw.wireOverrides.parallelToolCalls;
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
for (const rw of sectionRewritesApplied) {
|
|
2001
|
+
accumulatedMutations.push({
|
|
2002
|
+
id: `translator:${rw.rule}:${rw.sectionId}`,
|
|
2003
|
+
source: "translator",
|
|
2004
|
+
passName: "translator",
|
|
2005
|
+
description: `Rewrote section "${rw.sectionId}" (kind=${rw.kind}) via rule "${rw.rule}".`
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
const lowered = lower(workingIR, profile, {
|
|
2009
|
+
forceThinkingZero: cliffs.value.loweringHints.forceThinkingZero,
|
|
2010
|
+
forceTerseOutput: cliffs.value.loweringHints.forceTerseOutput,
|
|
2011
|
+
wireOverrides
|
|
2012
|
+
});
|
|
2013
|
+
validateFinalFit(workingIR, profile, inputTokens);
|
|
2014
|
+
const handle = makeHandle();
|
|
2015
|
+
const finalShape = computeShape(workingIR, inputTokens);
|
|
2016
|
+
const _learningKey = learningKey(ir.intent.archetype, profile.id, finalShape);
|
|
2017
|
+
const historyCacheMarkIndex = computeHistoryCacheMarkIndex(workingIR);
|
|
2018
|
+
const systemMessages = buildSystemMessages(workingIR, profile.provider);
|
|
2019
|
+
const systemCacheMarkIndex = lastCacheableSystemIndex(systemMessages);
|
|
2020
|
+
const diagnostics = {
|
|
2021
|
+
sectionsKept: workingIR.sections.length,
|
|
2022
|
+
sectionsDropped: ir.sections.length - workingIR.sections.length,
|
|
2023
|
+
toolsKept: workingIR.tools?.length ?? 0,
|
|
2024
|
+
toolsDropped: (ir.tools?.length ?? 0) - (workingIR.tools?.length ?? 0),
|
|
2025
|
+
historyKept: workingIR.history?.length ?? 0,
|
|
2026
|
+
historyDropped: (ir.history?.length ?? 0) - (workingIR.history?.length ?? 0),
|
|
2027
|
+
cacheableTokens: lowered.diagnostics.cacheableTokens,
|
|
2028
|
+
estimatedCacheSavingsUsd: lowered.diagnostics.estimatedCacheSavingsUsd,
|
|
2029
|
+
historyCacheableTokens: lowered.diagnostics.historyCacheableTokens,
|
|
2030
|
+
historyTokensTotal: compressed.historyTokensTotal,
|
|
2031
|
+
// alpha.20 E3: mirror the consumer's declared mode for Glass-Box +
|
|
2032
|
+
// brain observability. Undefined when not declared (pre-alpha.20).
|
|
2033
|
+
toolOrchestration: ir.constraints?.toolOrchestration,
|
|
2034
|
+
// alpha.33 — see top-of-block comment.
|
|
2035
|
+
historyCacheMarkIndex,
|
|
2036
|
+
systemCacheMarkIndex
|
|
2037
|
+
};
|
|
2038
|
+
if (ir.intent.archetype === "hunt" && ir.constraints?.toolOrchestration === "sequential") {
|
|
2039
|
+
accumulatedMutations.push({
|
|
2040
|
+
id: "sequential-mode-chain-selected",
|
|
2041
|
+
source: "tool_orchestration",
|
|
2042
|
+
passName: "compile",
|
|
2043
|
+
description: "ir.constraints.toolOrchestration='sequential' selected the DeepSeek-tier-0 hunt chain overlay (L-040 parallel-tool cliff doesn't apply at single-step granularity)."
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
const phase2ProfileResolver = opts.profileResolver ? (id) => {
|
|
2047
|
+
try {
|
|
2048
|
+
return opts.profileResolver(id);
|
|
2049
|
+
} catch {
|
|
2050
|
+
return void 0;
|
|
2051
|
+
}
|
|
2052
|
+
} : tryGetProfile;
|
|
2053
|
+
const rawAdvisories = runAdvisor(
|
|
2054
|
+
ir,
|
|
2055
|
+
{
|
|
2056
|
+
target: profile.id,
|
|
2057
|
+
provider: profile.provider,
|
|
2058
|
+
tokensIn: inputTokens,
|
|
2059
|
+
diagnostics
|
|
2060
|
+
},
|
|
2061
|
+
profile,
|
|
2062
|
+
opts.policy,
|
|
2063
|
+
{
|
|
2064
|
+
fallbackChain,
|
|
2065
|
+
profileResolver: phase2ProfileResolver,
|
|
2066
|
+
// alpha.29 — feed translator rewrites to the advisor so the
|
|
2067
|
+
// `archetype-perf-floor-breach` rule can suppress when the translator
|
|
2068
|
+
// already cleared the cliff for the same archetype. Without this,
|
|
2069
|
+
// both the rewrite AND the advisory fire — noisy, and the advisory
|
|
2070
|
+
// would mislead consumers into thinking the cliff is unaddressed.
|
|
2071
|
+
sectionRewritesApplied
|
|
2072
|
+
}
|
|
2073
|
+
);
|
|
2074
|
+
const advisories = rawAdvisories.map((a) => ({
|
|
2075
|
+
...a,
|
|
2076
|
+
kgautoRequestId: handle,
|
|
2077
|
+
ownership: deriveOwnership(a.code, a.ownership)
|
|
2078
|
+
}));
|
|
2079
|
+
return {
|
|
2080
|
+
handle,
|
|
2081
|
+
target: profile.id,
|
|
2082
|
+
provider: profile.provider,
|
|
2083
|
+
request: lowered.request,
|
|
2084
|
+
tokensIn: inputTokens,
|
|
2085
|
+
estimatedCostUsd: target.estimatedCostUsd,
|
|
2086
|
+
mutationsApplied: accumulatedMutations,
|
|
2087
|
+
fallbackChain,
|
|
2088
|
+
advisories,
|
|
2089
|
+
diagnostics,
|
|
2090
|
+
sectionRewritesApplied,
|
|
2091
|
+
wireOverrides,
|
|
2092
|
+
systemMessages
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
function computeHistoryCacheMarkIndex(ir) {
|
|
2096
|
+
const policy = ir.historyCachePolicy;
|
|
2097
|
+
if (!policy || policy.strategy === "none") return void 0;
|
|
2098
|
+
const historyLen = ir.history?.length ?? 0;
|
|
2099
|
+
if (historyLen === 0) return void 0;
|
|
2100
|
+
if (policy.strategy === "all-but-latest") {
|
|
2101
|
+
return historyLen - 1;
|
|
2102
|
+
}
|
|
2103
|
+
if (policy.strategy === "fixed-suffix") {
|
|
2104
|
+
const idx = historyLen - 1 - policy.suffix;
|
|
2105
|
+
if (idx < 0) return void 0;
|
|
2106
|
+
return idx;
|
|
2107
|
+
}
|
|
2108
|
+
return void 0;
|
|
2109
|
+
}
|
|
2110
|
+
function buildSystemMessages(ir, provider) {
|
|
2111
|
+
const sections = ir.sections;
|
|
2112
|
+
if (!sections || sections.length === 0) return [];
|
|
2113
|
+
return sections.map((s) => {
|
|
2114
|
+
const base = { role: "system", content: s.text };
|
|
2115
|
+
if (provider === "anthropic" && s.cacheable) {
|
|
2116
|
+
base.providerOptions = { anthropic: { cacheControl: { type: "ephemeral" } } };
|
|
2117
|
+
}
|
|
2118
|
+
return base;
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
function lastCacheableSystemIndex(systemMessages) {
|
|
2122
|
+
for (let i = systemMessages.length - 1; i >= 0; i--) {
|
|
2123
|
+
const entry = systemMessages[i];
|
|
2124
|
+
if (entry && entry.providerOptions?.anthropic?.cacheControl) {
|
|
2125
|
+
return i;
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return void 0;
|
|
2129
|
+
}
|
|
2130
|
+
function validateIR(ir) {
|
|
2131
|
+
if (!ir.appId) throw new Error("compile(): ir.appId is required");
|
|
2132
|
+
if (!ir.intent || !ir.intent.archetype) {
|
|
2133
|
+
throw new Error("compile(): ir.intent.archetype is required (use a dialect-v1 archetype)");
|
|
2134
|
+
}
|
|
2135
|
+
if (!Array.isArray(ir.models) || ir.models.length === 0) {
|
|
2136
|
+
throw new Error("compile(): ir.models must be a non-empty array");
|
|
2137
|
+
}
|
|
2138
|
+
if (!Array.isArray(ir.sections)) {
|
|
2139
|
+
throw new Error("compile(): ir.sections must be an array");
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
function resolveModelEntries(ir) {
|
|
2143
|
+
const out = [];
|
|
2144
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2145
|
+
for (const entry of ir.models) {
|
|
2146
|
+
let id;
|
|
2147
|
+
if (typeof entry === "string") {
|
|
2148
|
+
id = entry;
|
|
2149
|
+
} else if (entry && typeof entry === "object" && "family" in entry) {
|
|
2150
|
+
id = resolveFamilyEntry(entry.family, {
|
|
2151
|
+
archetype: ir.intent.archetype,
|
|
2152
|
+
appId: ir.appId
|
|
2153
|
+
});
|
|
2154
|
+
} else {
|
|
2155
|
+
throw new Error(
|
|
2156
|
+
`compile(): ir.models entry must be a string or { family: string }; got ${JSON.stringify(entry)}`
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2159
|
+
if (seen.has(id)) continue;
|
|
2160
|
+
seen.add(id);
|
|
2161
|
+
out.push(id);
|
|
2162
|
+
}
|
|
2163
|
+
return { ...ir, models: out };
|
|
2164
|
+
}
|
|
2165
|
+
function pickTarget(ir, scores) {
|
|
2166
|
+
if (ir.constraints?.forceModel) {
|
|
2167
|
+
const forced = scores.find((s) => s.modelId === ir.constraints.forceModel);
|
|
2168
|
+
if (forced && forced.fits) return forced;
|
|
2169
|
+
if (forced) {
|
|
2170
|
+
throw new Error(
|
|
2171
|
+
`compile(): forceModel="${ir.constraints.forceModel}" does not fit: ${forced.rejectReasons.join("; ")}`
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
const fitting = scores.filter((s) => s.fits).sort((a, b) => b.rank - a.rank);
|
|
2176
|
+
return fitting[0];
|
|
2177
|
+
}
|
|
2178
|
+
function validateFinalFit(ir, profile, tokens) {
|
|
2179
|
+
if (tokens > profile.maxContextTokens) {
|
|
2180
|
+
throw new Error(
|
|
2181
|
+
`compile(): final IR is ${tokens} tokens, exceeds ${profile.id} context (${profile.maxContextTokens})`
|
|
2182
|
+
);
|
|
2183
|
+
}
|
|
2184
|
+
if ((ir.tools?.length ?? 0) > profile.maxTools) {
|
|
2185
|
+
throw new Error(
|
|
2186
|
+
`compile(): final IR has ${ir.tools?.length} tools, exceeds ${profile.id} maxTools (${profile.maxTools})`
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
// src/pricing-brain.ts
|
|
2192
|
+
function isPricingRow(x) {
|
|
2193
|
+
if (!x || typeof x !== "object") return false;
|
|
2194
|
+
const r = x;
|
|
2195
|
+
return typeof r.model_id === "string" && typeof r.cost_input_per_1m === "number" && typeof r.cost_output_per_1m === "number" && typeof r.valid_from === "string";
|
|
2196
|
+
}
|
|
2197
|
+
function mapRowsToPricing(rows) {
|
|
2198
|
+
const out = [];
|
|
2199
|
+
for (const row of rows) {
|
|
2200
|
+
if (!isPricingRow(row)) continue;
|
|
2201
|
+
out.push({
|
|
2202
|
+
modelId: row.model_id,
|
|
2203
|
+
costInputPer1m: row.cost_input_per_1m,
|
|
2204
|
+
costOutputPer1m: row.cost_output_per_1m,
|
|
2205
|
+
cacheInputPer1m: row.cache_input_per_1m ?? void 0,
|
|
2206
|
+
cacheCreationPer1m: row.cache_creation_per_1m ?? void 0,
|
|
2207
|
+
validFrom: Date.parse(row.valid_from),
|
|
2208
|
+
validUntil: row.valid_until == null ? void 0 : Date.parse(row.valid_until),
|
|
2209
|
+
source: row.source ?? void 0
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
return out;
|
|
2213
|
+
}
|
|
2214
|
+
function bundledPricing() {
|
|
2215
|
+
const out = [];
|
|
2216
|
+
for (const profile of allProfiles()) {
|
|
2217
|
+
out.push({
|
|
2218
|
+
modelId: profile.id,
|
|
2219
|
+
costInputPer1m: profile.costInputPer1m,
|
|
2220
|
+
costOutputPer1m: profile.costOutputPer1m,
|
|
2221
|
+
cacheInputPer1m: profile.lowering.cache.discount !== void 0 && profile.lowering.cache.discount > 0 ? profile.costInputPer1m * profile.lowering.cache.discount : void 0,
|
|
2222
|
+
validFrom: 0,
|
|
2223
|
+
validUntil: void 0,
|
|
2224
|
+
source: "profile_seed"
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
return out;
|
|
2228
|
+
}
|
|
2229
|
+
var loadPricingFromBrain = createBrainQueryCache({
|
|
2230
|
+
table: "kgauto_pricing",
|
|
2231
|
+
mapRows: mapRowsToPricing,
|
|
2232
|
+
bundledFallback: bundledPricing
|
|
2233
|
+
});
|
|
2234
|
+
function resolvePricingAt(modelId, at = /* @__PURE__ */ new Date()) {
|
|
2235
|
+
const ts = at.getTime();
|
|
2236
|
+
const all = loadPricingFromBrain();
|
|
2237
|
+
let best;
|
|
2238
|
+
for (const row of all) {
|
|
2239
|
+
if (row.modelId !== modelId) continue;
|
|
2240
|
+
if (row.validFrom > ts) continue;
|
|
2241
|
+
if (row.validUntil !== void 0 && row.validUntil <= ts) continue;
|
|
2242
|
+
if (!best || row.validFrom > best.validFrom) best = row;
|
|
2243
|
+
}
|
|
2244
|
+
return best;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
// src/brain.ts
|
|
2248
|
+
var activeConfig;
|
|
2249
|
+
function configureBrain(config) {
|
|
2250
|
+
const endpoint = config.endpoint.replace(/\/outcomes\/?$/, "");
|
|
2251
|
+
activeConfig = { ...config, endpoint };
|
|
2252
|
+
const bq = config.brainQuery ?? {};
|
|
2253
|
+
const enabledTables = /* @__PURE__ */ new Set();
|
|
2254
|
+
if (bq.chains !== false) enabledTables.add("kgauto_chains");
|
|
2255
|
+
if (bq.perf !== false) enabledTables.add("kgauto_archetype_perf");
|
|
2256
|
+
if (bq.pricing !== false) enabledTables.add("kgauto_pricing");
|
|
2257
|
+
if (bq.models !== false) {
|
|
2258
|
+
enabledTables.add("kgauto_models");
|
|
2259
|
+
enabledTables.add("kgauto_aliases");
|
|
2260
|
+
}
|
|
2261
|
+
if (enabledTables.size === 0) {
|
|
2262
|
+
configureBrainQuery(void 0);
|
|
2263
|
+
} else {
|
|
2264
|
+
configureBrainQuery({
|
|
2265
|
+
endpoint,
|
|
2266
|
+
configEndpoint: bq.configEndpoint,
|
|
2267
|
+
ttlMs: bq.cacheTtlMs ?? 3e5,
|
|
2268
|
+
fetchImpl: config.fetchImpl ?? fetch,
|
|
2269
|
+
enabledTables,
|
|
2270
|
+
onError: config.onError
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
if (bq.findingsExclusions !== false) {
|
|
2274
|
+
configureExclusionFindingsBrain({
|
|
2275
|
+
endpoint: bq.findingsEndpoint ?? DEFAULT_FINDINGS_ENDPOINT,
|
|
2276
|
+
ttlMs: bq.cacheTtlMs ?? 3e5,
|
|
2277
|
+
fetchImpl: config.fetchImpl ?? fetch,
|
|
2278
|
+
onError: config.onError
|
|
2279
|
+
});
|
|
2280
|
+
} else {
|
|
2281
|
+
configureExclusionFindingsBrain(void 0);
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
function clearBrain() {
|
|
2285
|
+
activeConfig = void 0;
|
|
2286
|
+
configureBrainQuery(void 0);
|
|
2287
|
+
configureExclusionFindingsBrain(void 0);
|
|
2288
|
+
}
|
|
2289
|
+
var compileRegistry = /* @__PURE__ */ new Map();
|
|
2290
|
+
var REGISTRY_MAX_ENTRIES = 1e4;
|
|
2291
|
+
function registerCompile(appId, archetype, ir, result) {
|
|
2292
|
+
if (compileRegistry.size >= REGISTRY_MAX_ENTRIES) {
|
|
2293
|
+
const cutoff = Math.floor(REGISTRY_MAX_ENTRIES * 0.25);
|
|
2294
|
+
let evicted = 0;
|
|
2295
|
+
for (const k of compileRegistry.keys()) {
|
|
2296
|
+
compileRegistry.delete(k);
|
|
2297
|
+
if (++evicted >= cutoff) break;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
const tokens = result.tokensIn;
|
|
2301
|
+
const shape = computeShape(
|
|
2302
|
+
{
|
|
2303
|
+
appId,
|
|
2304
|
+
intent: { name: archetype, archetype },
|
|
2305
|
+
sections: ir.sections,
|
|
2306
|
+
tools: ir.tools,
|
|
2307
|
+
history: ir.history,
|
|
2308
|
+
models: ir.models,
|
|
2309
|
+
constraints: ir.constraints
|
|
2310
|
+
},
|
|
2311
|
+
tokens
|
|
2312
|
+
);
|
|
2313
|
+
const shapeKey = `${shape.contextBucket}-${shape.toolCountBucket}-${shape.historyDepth}-${shape.outputMode}`;
|
|
2314
|
+
const toolsCount = result.diagnostics.toolsKept;
|
|
2315
|
+
const historyDepth = Array.isArray(ir.history) ? ir.history.length : 0;
|
|
2316
|
+
const systemPromptChars = estimateSystemPromptChars(ir.sections);
|
|
2317
|
+
compileRegistry.set(result.handle, {
|
|
2318
|
+
appId,
|
|
2319
|
+
archetype,
|
|
2320
|
+
model: result.target,
|
|
2321
|
+
provider: result.provider,
|
|
2322
|
+
shapeKey,
|
|
2323
|
+
learningKey: learningKey(archetype, result.target, shape),
|
|
2324
|
+
estimatedTokensIn: tokens,
|
|
2325
|
+
mutationsApplied: result.mutationsApplied.map((m) => m.id),
|
|
2326
|
+
// alpha.30: cache the in-memory advisories so record() can auto-persist
|
|
2327
|
+
// to `compile_outcome_advisories` without consumer-side threading.
|
|
2328
|
+
advisoriesFromCompile: result.advisories ?? [],
|
|
2329
|
+
startedAt: Date.now(),
|
|
2330
|
+
historyCacheableTokens: result.diagnostics.historyCacheableTokens,
|
|
2331
|
+
historyTokensTotal: result.diagnostics.historyTokensTotal,
|
|
2332
|
+
// alpha.20 E3: capture consumer's declared mode for the brain payload.
|
|
2333
|
+
toolOrchestration: result.diagnostics.toolOrchestration,
|
|
2334
|
+
// alpha.28: shape fields for Glass-Box renderer.
|
|
2335
|
+
toolsCount,
|
|
2336
|
+
historyDepth,
|
|
2337
|
+
systemPromptChars,
|
|
2338
|
+
// alpha.29: translator activity — persisted on the brain row so
|
|
2339
|
+
// cross-app aggregates can answer "Sonnet narration rule fired N times,
|
|
2340
|
+
// outcome quality lifted to M."
|
|
2341
|
+
sectionRewritesApplied: result.sectionRewritesApplied
|
|
2342
|
+
});
|
|
2343
|
+
}
|
|
2344
|
+
function estimateSystemPromptChars(sections) {
|
|
2345
|
+
if (!Array.isArray(sections) || sections.length === 0) return void 0;
|
|
2346
|
+
let total = 0;
|
|
2347
|
+
for (const s of sections) {
|
|
2348
|
+
if (s && typeof s === "object") {
|
|
2349
|
+
const content = s.content;
|
|
2350
|
+
if (typeof content === "string") total += content.length;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
return total > 0 ? total : void 0;
|
|
2354
|
+
}
|
|
2355
|
+
async function record(input) {
|
|
2356
|
+
const reg = compileRegistry.get(input.handle);
|
|
2357
|
+
if (reg) compileRegistry.delete(input.handle);
|
|
2358
|
+
if (!activeConfig) {
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
const payload = buildPayload(input, reg);
|
|
2362
|
+
const config = activeConfig;
|
|
2363
|
+
const fetchFn = config.fetchImpl ?? fetch;
|
|
2364
|
+
const send = async () => {
|
|
2365
|
+
let outcomeId;
|
|
2366
|
+
try {
|
|
2367
|
+
const res = await fetchFn(`${config.endpoint}/outcomes`, {
|
|
2368
|
+
method: "POST",
|
|
2369
|
+
headers: {
|
|
2370
|
+
"Content-Type": "application/json",
|
|
2371
|
+
// alpha.20: request the inserted row back so we can JOIN advisories
|
|
2372
|
+
// to it via outcome_id. PostgREST returns the row when
|
|
2373
|
+
// `Prefer: return=representation` is set; proxies that pass the
|
|
2374
|
+
// header through (the recommended `const row = { ...body }` shape
|
|
2375
|
+
// from OutcomePayload's forward-compat rule) will surface
|
|
2376
|
+
// the row id. Proxies that don't (legacy / hand-rolled shapes)
|
|
2377
|
+
// simply produce no parseable id → secondary advisory POST is
|
|
2378
|
+
// skipped silently. Best-effort — primary outcome row is the
|
|
2379
|
+
// load-bearing write.
|
|
2380
|
+
Prefer: "return=representation",
|
|
2381
|
+
...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
|
|
2382
|
+
},
|
|
2383
|
+
body: JSON.stringify(payload)
|
|
2384
|
+
});
|
|
2385
|
+
if (!res.ok) {
|
|
2386
|
+
const text = await res.text().catch(() => "<no body>");
|
|
2387
|
+
throw new Error(`brain ${res.status}: ${text}`);
|
|
2388
|
+
}
|
|
2389
|
+
outcomeId = await tryExtractOutcomeId(res);
|
|
2390
|
+
} catch (err) {
|
|
2391
|
+
(config.onError ?? defaultOnError4)(err);
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
const advisories = input.advisories ?? reg?.advisoriesFromCompile;
|
|
2395
|
+
if (!advisories || advisories.length === 0) return;
|
|
2396
|
+
if (outcomeId === void 0) return;
|
|
2397
|
+
try {
|
|
2398
|
+
const advisoryPayload = advisories.map((a) => buildAdvisoryRow(outcomeId, a));
|
|
2399
|
+
const res = await fetchFn(`${config.endpoint}/compile_outcome_advisories`, {
|
|
2400
|
+
method: "POST",
|
|
2401
|
+
headers: {
|
|
2402
|
+
"Content-Type": "application/json",
|
|
2403
|
+
Prefer: "return=minimal",
|
|
2404
|
+
...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
|
|
2405
|
+
},
|
|
2406
|
+
body: JSON.stringify(advisoryPayload)
|
|
2407
|
+
});
|
|
2408
|
+
if (!res.ok) {
|
|
2409
|
+
const text = await res.text().catch(() => "<no body>");
|
|
2410
|
+
throw new Error(`brain advisories ${res.status}: ${text}`);
|
|
2411
|
+
}
|
|
2412
|
+
} catch (err) {
|
|
2413
|
+
(config.onError ?? defaultOnError4)(err);
|
|
2414
|
+
}
|
|
2415
|
+
};
|
|
2416
|
+
if (config.sync) {
|
|
2417
|
+
await send();
|
|
2418
|
+
} else {
|
|
2419
|
+
void send();
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
function defaultOnError4(err) {
|
|
2423
|
+
console.warn("[kgauto] brain record failed:", err);
|
|
2424
|
+
}
|
|
2425
|
+
function buildPayload(input, reg) {
|
|
2426
|
+
const compileTarget = reg?.model;
|
|
2427
|
+
const actual = input.actualModel ?? compileTarget;
|
|
2428
|
+
const requested = input.actualModel && compileTarget && input.actualModel !== compileTarget ? compileTarget : void 0;
|
|
2429
|
+
const mutationsApplied = input.mutationsApplied ?? reg?.mutationsApplied ?? [];
|
|
2430
|
+
const costModel = actual;
|
|
2431
|
+
const costUsdActual = costModel ? computeCostUsd(costModel, input.tokensIn, input.tokensOut) : void 0;
|
|
2432
|
+
const fellOverFrom = input.fellOverFrom ?? requested;
|
|
2433
|
+
const fallbackReason = fellOverFrom ? input.fallbackReason : void 0;
|
|
798
2434
|
return {
|
|
799
2435
|
handle: input.handle,
|
|
800
2436
|
app_id: reg?.appId,
|
|
@@ -822,34 +2458,418 @@ function buildPayload(input, reg) {
|
|
|
822
2458
|
cache_read_input_tokens: input.cacheReadInputTokens,
|
|
823
2459
|
cache_creation_input_tokens: input.cacheCreationInputTokens,
|
|
824
2460
|
cost_usd_actual: costUsdActual,
|
|
825
|
-
ttft_ms: input.ttftMs
|
|
2461
|
+
ttft_ms: input.ttftMs,
|
|
2462
|
+
history_cacheable_tokens: reg?.historyCacheableTokens,
|
|
2463
|
+
history_tokens_at_compile: reg?.historyTokensTotal,
|
|
2464
|
+
// alpha.20 E3: mirror consumer's declared tool-orchestration mode so
|
|
2465
|
+
// the brain can measure per-mode model perf separately (DeepSeek in
|
|
2466
|
+
// sequential vs parallel mode is two different stories — L-040).
|
|
2467
|
+
// Null when consumer hadn't adopted the constraint yet.
|
|
2468
|
+
tool_orchestration: reg?.toolOrchestration ?? null,
|
|
2469
|
+
// alpha.28 — Glass-Box renderer substrate (migration 018). All optional;
|
|
2470
|
+
// omitted-undefined PostgREST inserts store NULL → renderer renders "—".
|
|
2471
|
+
finish_reason: input.finishReason,
|
|
2472
|
+
total_ms: input.totalMs ?? input.latencyMs,
|
|
2473
|
+
tools_count: input.toolsCount ?? reg?.toolsCount,
|
|
2474
|
+
history_depth: input.historyDepth ?? reg?.historyDepth,
|
|
2475
|
+
system_prompt_chars: input.systemPromptChars ?? reg?.systemPromptChars,
|
|
2476
|
+
fell_over_from: fellOverFrom,
|
|
2477
|
+
fallback_reason: fallbackReason,
|
|
2478
|
+
// alpha.29 — translator activity (migration 019). Send NULL when no
|
|
2479
|
+
// rewrites fired so the brain's "did the translator do anything?"
|
|
2480
|
+
// queries can use `IS NOT NULL` cleanly.
|
|
2481
|
+
section_rewrites_applied: reg?.sectionRewritesApplied && reg.sectionRewritesApplied.length > 0 ? reg.sectionRewritesApplied : null
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
function computeCostUsd(modelId, tokensIn, tokensOut) {
|
|
2485
|
+
if (tokensIn === 0 && tokensOut === 0) return void 0;
|
|
2486
|
+
const brainRow = resolvePricingAt(modelId);
|
|
2487
|
+
if (brainRow && (brainRow.costInputPer1m > 0 || brainRow.costOutputPer1m > 0)) {
|
|
2488
|
+
const inUsd2 = tokensIn / 1e6 * brainRow.costInputPer1m;
|
|
2489
|
+
const outUsd2 = tokensOut / 1e6 * brainRow.costOutputPer1m;
|
|
2490
|
+
return Math.round((inUsd2 + outUsd2) * 1e6) / 1e6;
|
|
2491
|
+
}
|
|
2492
|
+
const profile = tryGetProfile(modelId);
|
|
2493
|
+
if (!profile) return void 0;
|
|
2494
|
+
const inUsd = tokensIn / 1e6 * profile.costInputPer1m;
|
|
2495
|
+
const outUsd = tokensOut / 1e6 * profile.costOutputPer1m;
|
|
2496
|
+
return Math.round((inUsd + outUsd) * 1e6) / 1e6;
|
|
2497
|
+
}
|
|
2498
|
+
async function tryExtractOutcomeId(res) {
|
|
2499
|
+
try {
|
|
2500
|
+
const ct = res.headers?.get?.("content-type") ?? "";
|
|
2501
|
+
if (ct && !ct.includes("application/json")) return void 0;
|
|
2502
|
+
if (typeof res.json !== "function") return void 0;
|
|
2503
|
+
const body = await res.json();
|
|
2504
|
+
if (Array.isArray(body) && body.length > 0) {
|
|
2505
|
+
const first = body[0];
|
|
2506
|
+
const id = first?.id;
|
|
2507
|
+
if (typeof id === "number") return id;
|
|
2508
|
+
} else if (body && typeof body === "object") {
|
|
2509
|
+
const id = body.id;
|
|
2510
|
+
if (typeof id === "number") return id;
|
|
2511
|
+
}
|
|
2512
|
+
return void 0;
|
|
2513
|
+
} catch {
|
|
2514
|
+
return void 0;
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
function buildAdvisoryRow(outcomeId, a) {
|
|
2518
|
+
return {
|
|
2519
|
+
outcome_id: outcomeId,
|
|
2520
|
+
code: a.code,
|
|
2521
|
+
level: a.level,
|
|
2522
|
+
message: a.message,
|
|
2523
|
+
...a.recommendationType ? { recommendation_type: a.recommendationType } : {},
|
|
2524
|
+
...a.suggestion ? { suggestion: a.suggestion } : {},
|
|
2525
|
+
...a.docsUrl ? { docs_url: a.docsUrl } : {}
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
async function recordOutcome(input) {
|
|
2529
|
+
if (!activeConfig) {
|
|
2530
|
+
return { ok: false, reason: "brain_not_configured" };
|
|
2531
|
+
}
|
|
2532
|
+
const config = activeConfig;
|
|
2533
|
+
const fetchFn = config.fetchImpl ?? fetch;
|
|
2534
|
+
const payload = {
|
|
2535
|
+
outcome_id: input.outcomeId,
|
|
2536
|
+
outcome: input.outcome,
|
|
2537
|
+
rating: input.rating ?? null,
|
|
2538
|
+
reason: input.reason ?? null,
|
|
2539
|
+
observed_confidence: input.observedConfidence ?? null
|
|
2540
|
+
};
|
|
2541
|
+
const send = async () => {
|
|
2542
|
+
try {
|
|
2543
|
+
const res = await fetchFn(`${config.endpoint}/compile_outcome_quality`, {
|
|
2544
|
+
method: "POST",
|
|
2545
|
+
headers: {
|
|
2546
|
+
"Content-Type": "application/json",
|
|
2547
|
+
...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
|
|
2548
|
+
},
|
|
2549
|
+
body: JSON.stringify(payload)
|
|
2550
|
+
});
|
|
2551
|
+
if (!res.ok) {
|
|
2552
|
+
const text = await res.text().catch(() => "<no body>");
|
|
2553
|
+
const err = new Error(`brain ${res.status}: ${text}`);
|
|
2554
|
+
(config.onError ?? defaultOnError4)(err);
|
|
2555
|
+
return { ok: false, reason: "persistence_failed" };
|
|
2556
|
+
}
|
|
2557
|
+
return { ok: true };
|
|
2558
|
+
} catch (err) {
|
|
2559
|
+
(config.onError ?? defaultOnError4)(err);
|
|
2560
|
+
return { ok: false, reason: "persistence_failed" };
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
if (config.sync) {
|
|
2564
|
+
return send();
|
|
2565
|
+
}
|
|
2566
|
+
void send();
|
|
2567
|
+
return { ok: true };
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// src/ir.ts
|
|
2571
|
+
var CallError = class extends Error {
|
|
2572
|
+
attempts;
|
|
2573
|
+
lastErrorCode;
|
|
2574
|
+
lastStatus;
|
|
2575
|
+
constructor(message, attempts, lastStatus, lastErrorCode) {
|
|
2576
|
+
super(message);
|
|
2577
|
+
this.name = "CallError";
|
|
2578
|
+
this.attempts = attempts;
|
|
2579
|
+
this.lastStatus = lastStatus;
|
|
2580
|
+
this.lastErrorCode = lastErrorCode;
|
|
2581
|
+
}
|
|
2582
|
+
};
|
|
2583
|
+
|
|
2584
|
+
// src/streaming.ts
|
|
2585
|
+
var ANTHROPIC_URL = "https://api.anthropic.com/v1/messages";
|
|
2586
|
+
async function streamAnthropic(request, apiKey, opts) {
|
|
2587
|
+
const { provider: _provider, ...body } = request;
|
|
2588
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
2589
|
+
let res;
|
|
2590
|
+
try {
|
|
2591
|
+
res = await fetchFn(ANTHROPIC_URL, {
|
|
2592
|
+
method: "POST",
|
|
2593
|
+
headers: {
|
|
2594
|
+
"x-api-key": apiKey,
|
|
2595
|
+
"anthropic-version": "2023-06-01",
|
|
2596
|
+
"content-type": "application/json"
|
|
2597
|
+
},
|
|
2598
|
+
body: JSON.stringify({ ...body, stream: true })
|
|
2599
|
+
});
|
|
2600
|
+
} catch (err) {
|
|
2601
|
+
return retryableError(0, "network_error", String(err), null);
|
|
2602
|
+
}
|
|
2603
|
+
if (!res.ok) {
|
|
2604
|
+
const errBody = await res.json().catch(() => ({}));
|
|
2605
|
+
return classifyHttpError(res.status, errBody);
|
|
2606
|
+
}
|
|
2607
|
+
let text = "";
|
|
2608
|
+
let inputTokens = 0;
|
|
2609
|
+
let outputTokens = 0;
|
|
2610
|
+
let cacheReadTokens;
|
|
2611
|
+
let cacheCreatedTokens;
|
|
2612
|
+
let stopReason;
|
|
2613
|
+
const toolBlocks = /* @__PURE__ */ new Map();
|
|
2614
|
+
try {
|
|
2615
|
+
await parseSSEStream(res, (event) => {
|
|
2616
|
+
if (!event.data) return;
|
|
2617
|
+
let payload;
|
|
2618
|
+
try {
|
|
2619
|
+
payload = JSON.parse(event.data);
|
|
2620
|
+
} catch {
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
const type = payload.type;
|
|
2624
|
+
if (type === "message_start") {
|
|
2625
|
+
const msg = payload.message;
|
|
2626
|
+
const usage = msg?.usage;
|
|
2627
|
+
if (usage) {
|
|
2628
|
+
inputTokens = usage.input_tokens ?? 0;
|
|
2629
|
+
outputTokens = usage.output_tokens ?? 0;
|
|
2630
|
+
if (typeof usage.cache_read_input_tokens === "number")
|
|
2631
|
+
cacheReadTokens = usage.cache_read_input_tokens;
|
|
2632
|
+
if (typeof usage.cache_creation_input_tokens === "number")
|
|
2633
|
+
cacheCreatedTokens = usage.cache_creation_input_tokens;
|
|
2634
|
+
}
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
if (type === "content_block_start") {
|
|
2638
|
+
const p = payload;
|
|
2639
|
+
const idx = p.index ?? 0;
|
|
2640
|
+
const block = p.content_block;
|
|
2641
|
+
if (block?.type === "tool_use" && block.id && block.name) {
|
|
2642
|
+
toolBlocks.set(idx, { id: block.id, name: block.name, argsJson: "" });
|
|
2643
|
+
}
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
2646
|
+
if (type === "content_block_delta") {
|
|
2647
|
+
const p = payload;
|
|
2648
|
+
const delta = p.delta;
|
|
2649
|
+
if (!delta) return;
|
|
2650
|
+
if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
2651
|
+
text += delta.text;
|
|
2652
|
+
opts.onChunk(delta.text);
|
|
2653
|
+
} else if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
2654
|
+
const idx = p.index ?? 0;
|
|
2655
|
+
const tool = toolBlocks.get(idx);
|
|
2656
|
+
if (tool) tool.argsJson += delta.partial_json;
|
|
2657
|
+
}
|
|
2658
|
+
return;
|
|
2659
|
+
}
|
|
2660
|
+
if (type === "message_delta") {
|
|
2661
|
+
const p = payload;
|
|
2662
|
+
if (p.delta?.stop_reason) stopReason = p.delta.stop_reason;
|
|
2663
|
+
if (typeof p.usage?.output_tokens === "number") outputTokens = p.usage.output_tokens;
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2666
|
+
});
|
|
2667
|
+
} catch (err) {
|
|
2668
|
+
return retryableError(0, "stream_interrupted", String(err), null);
|
|
2669
|
+
}
|
|
2670
|
+
const toolCalls = Array.from(toolBlocks.values()).map((b) => ({
|
|
2671
|
+
id: b.id,
|
|
2672
|
+
name: b.name,
|
|
2673
|
+
args: tryParseJson(b.argsJson) ?? {}
|
|
2674
|
+
}));
|
|
2675
|
+
const tokens = {
|
|
2676
|
+
input: inputTokens,
|
|
2677
|
+
output: outputTokens,
|
|
2678
|
+
total: inputTokens + outputTokens,
|
|
2679
|
+
cached: cacheReadTokens,
|
|
2680
|
+
cacheCreated: cacheCreatedTokens
|
|
2681
|
+
};
|
|
2682
|
+
const response = {
|
|
2683
|
+
text,
|
|
2684
|
+
structuredOutput: null,
|
|
2685
|
+
toolCalls,
|
|
2686
|
+
tokens,
|
|
2687
|
+
finishReason: stopReason,
|
|
2688
|
+
raw: { streamed: true, provider: "anthropic" }
|
|
2689
|
+
};
|
|
2690
|
+
return { ok: true, status: res.status, response };
|
|
2691
|
+
}
|
|
2692
|
+
async function streamOpenAILike(url, request, apiKey, providerLabel, opts) {
|
|
2693
|
+
const { provider: _provider, ...body } = request;
|
|
2694
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
2695
|
+
const reqBody = {
|
|
2696
|
+
...body,
|
|
2697
|
+
stream: true,
|
|
2698
|
+
stream_options: {
|
|
2699
|
+
...body.stream_options ?? {},
|
|
2700
|
+
include_usage: true
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
let res;
|
|
2704
|
+
try {
|
|
2705
|
+
res = await fetchFn(url, {
|
|
2706
|
+
method: "POST",
|
|
2707
|
+
headers: {
|
|
2708
|
+
authorization: `Bearer ${apiKey}`,
|
|
2709
|
+
"content-type": "application/json"
|
|
2710
|
+
},
|
|
2711
|
+
body: JSON.stringify(reqBody)
|
|
2712
|
+
});
|
|
2713
|
+
} catch (err) {
|
|
2714
|
+
return retryableError(0, "network_error", String(err), null);
|
|
2715
|
+
}
|
|
2716
|
+
if (!res.ok) {
|
|
2717
|
+
const errBody = await res.json().catch(() => ({}));
|
|
2718
|
+
return classifyHttpError(res.status, errBody);
|
|
2719
|
+
}
|
|
2720
|
+
let text = "";
|
|
2721
|
+
let finishReason;
|
|
2722
|
+
let inputTokens = 0;
|
|
2723
|
+
let outputTokens = 0;
|
|
2724
|
+
let totalTokens;
|
|
2725
|
+
let cachedTokens;
|
|
2726
|
+
const toolBuffers = /* @__PURE__ */ new Map();
|
|
2727
|
+
try {
|
|
2728
|
+
await parseSSEStream(res, (event) => {
|
|
2729
|
+
if (!event.data) return;
|
|
2730
|
+
if (event.data === "[DONE]") return;
|
|
2731
|
+
let payload;
|
|
2732
|
+
try {
|
|
2733
|
+
payload = JSON.parse(event.data);
|
|
2734
|
+
} catch {
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
const choices = payload.choices;
|
|
2738
|
+
if (choices && choices.length > 0) {
|
|
2739
|
+
const choice = choices[0];
|
|
2740
|
+
const deltaContent = choice.delta?.content;
|
|
2741
|
+
if (typeof deltaContent === "string" && deltaContent.length > 0) {
|
|
2742
|
+
text += deltaContent;
|
|
2743
|
+
opts.onChunk(deltaContent);
|
|
2744
|
+
}
|
|
2745
|
+
const toolDeltas = choice.delta?.tool_calls;
|
|
2746
|
+
if (toolDeltas) {
|
|
2747
|
+
for (const tcDelta of toolDeltas) {
|
|
2748
|
+
const idx = tcDelta.index ?? 0;
|
|
2749
|
+
let buf = toolBuffers.get(idx);
|
|
2750
|
+
if (!buf) {
|
|
2751
|
+
buf = { id: tcDelta.id ?? `tc-${idx}`, name: "", argsJson: "" };
|
|
2752
|
+
toolBuffers.set(idx, buf);
|
|
2753
|
+
}
|
|
2754
|
+
if (tcDelta.id) buf.id = tcDelta.id;
|
|
2755
|
+
if (tcDelta.function?.name) buf.name += tcDelta.function.name;
|
|
2756
|
+
if (tcDelta.function?.arguments) buf.argsJson += tcDelta.function.arguments;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
if (choice.finish_reason) finishReason = choice.finish_reason;
|
|
2760
|
+
}
|
|
2761
|
+
const usage = payload.usage;
|
|
2762
|
+
if (usage) {
|
|
2763
|
+
if (typeof usage.prompt_tokens === "number") inputTokens = usage.prompt_tokens;
|
|
2764
|
+
if (typeof usage.completion_tokens === "number") outputTokens = usage.completion_tokens;
|
|
2765
|
+
if (typeof usage.total_tokens === "number") totalTokens = usage.total_tokens;
|
|
2766
|
+
const details = usage.prompt_tokens_details;
|
|
2767
|
+
if (typeof details?.cached_tokens === "number") cachedTokens = details.cached_tokens;
|
|
2768
|
+
}
|
|
2769
|
+
});
|
|
2770
|
+
} catch (err) {
|
|
2771
|
+
return retryableError(0, "stream_interrupted", String(err), null);
|
|
2772
|
+
}
|
|
2773
|
+
const toolCalls = Array.from(toolBuffers.values()).filter((b) => b.name.length > 0).map((b) => ({
|
|
2774
|
+
id: b.id,
|
|
2775
|
+
name: b.name,
|
|
2776
|
+
args: tryParseJson(b.argsJson) ?? {}
|
|
2777
|
+
}));
|
|
2778
|
+
const tokens = {
|
|
2779
|
+
input: inputTokens,
|
|
2780
|
+
output: outputTokens,
|
|
2781
|
+
total: totalTokens ?? inputTokens + outputTokens,
|
|
2782
|
+
cached: cachedTokens
|
|
826
2783
|
};
|
|
2784
|
+
const response = {
|
|
2785
|
+
text,
|
|
2786
|
+
structuredOutput: null,
|
|
2787
|
+
toolCalls,
|
|
2788
|
+
tokens,
|
|
2789
|
+
finishReason,
|
|
2790
|
+
raw: { streamed: true, provider: providerLabel }
|
|
2791
|
+
};
|
|
2792
|
+
return { ok: true, status: res.status, response };
|
|
2793
|
+
}
|
|
2794
|
+
async function parseSSEStream(response, handler) {
|
|
2795
|
+
const body = response.body;
|
|
2796
|
+
if (!body) throw new Error("Response has no body for SSE parse");
|
|
2797
|
+
const reader = body.getReader();
|
|
2798
|
+
const decoder = new TextDecoder("utf-8");
|
|
2799
|
+
let buffer = "";
|
|
2800
|
+
for (; ; ) {
|
|
2801
|
+
const { value, done } = await reader.read();
|
|
2802
|
+
if (done) break;
|
|
2803
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2804
|
+
let sep;
|
|
2805
|
+
while (sep = buffer.indexOf("\n\n"), sep !== -1) {
|
|
2806
|
+
const block = buffer.slice(0, sep);
|
|
2807
|
+
buffer = buffer.slice(sep + 2);
|
|
2808
|
+
const event = parseSSEBlock(block);
|
|
2809
|
+
if (event) handler(event);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if (buffer.length > 0) {
|
|
2813
|
+
const event = parseSSEBlock(buffer);
|
|
2814
|
+
if (event) handler(event);
|
|
2815
|
+
}
|
|
827
2816
|
}
|
|
828
|
-
function
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
const
|
|
833
|
-
|
|
834
|
-
|
|
2817
|
+
function parseSSEBlock(block) {
|
|
2818
|
+
const lines = block.split(/\r?\n/);
|
|
2819
|
+
let eventName;
|
|
2820
|
+
const dataLines = [];
|
|
2821
|
+
for (const line of lines) {
|
|
2822
|
+
if (line.startsWith(":")) continue;
|
|
2823
|
+
if (line.startsWith("event:")) {
|
|
2824
|
+
eventName = line.slice(6).trim();
|
|
2825
|
+
} else if (line.startsWith("data:")) {
|
|
2826
|
+
dataLines.push(line.slice(5).trim());
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
if (dataLines.length === 0) return void 0;
|
|
2830
|
+
return { event: eventName, data: dataLines.join("\n") };
|
|
835
2831
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
super(message);
|
|
844
|
-
this.name = "CallError";
|
|
845
|
-
this.attempts = attempts;
|
|
846
|
-
this.lastStatus = lastStatus;
|
|
847
|
-
this.lastErrorCode = lastErrorCode;
|
|
2832
|
+
function tryParseJson(s) {
|
|
2833
|
+
if (typeof s !== "string" || s.length === 0) return void 0;
|
|
2834
|
+
try {
|
|
2835
|
+
const parsed = JSON.parse(s);
|
|
2836
|
+
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
2837
|
+
} catch {
|
|
2838
|
+
return void 0;
|
|
848
2839
|
}
|
|
849
|
-
}
|
|
2840
|
+
}
|
|
2841
|
+
function classifyHttpError(status, body) {
|
|
2842
|
+
const message = extractErrorMessage(body) ?? `HTTP ${status}`;
|
|
2843
|
+
if (status === 429)
|
|
2844
|
+
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
2845
|
+
if (status === 408)
|
|
2846
|
+
return { ok: false, status, errorType: "retryable", errorCode: "timeout", message, raw: body };
|
|
2847
|
+
if (status >= 500)
|
|
2848
|
+
return { ok: false, status, errorType: "retryable", errorCode: "server_error", message, raw: body };
|
|
2849
|
+
if (status === 404)
|
|
2850
|
+
return { ok: false, status, errorType: "retryable", errorCode: "model_not_found", message, raw: body };
|
|
2851
|
+
if (status === 401 || status === 403)
|
|
2852
|
+
return { ok: false, status, errorType: "terminal", errorCode: "auth", message, raw: body };
|
|
2853
|
+
if (status === 400)
|
|
2854
|
+
return { ok: false, status, errorType: "terminal", errorCode: "invalid_request", message, raw: body };
|
|
2855
|
+
return { ok: false, status, errorType: "terminal", errorCode: "unknown", message, raw: body };
|
|
2856
|
+
}
|
|
2857
|
+
function extractErrorMessage(body) {
|
|
2858
|
+
if (!body || typeof body !== "object") return void 0;
|
|
2859
|
+
const b = body;
|
|
2860
|
+
if (b.error && typeof b.error === "object") {
|
|
2861
|
+
const e = b.error;
|
|
2862
|
+
if (typeof e.message === "string") return e.message;
|
|
2863
|
+
}
|
|
2864
|
+
if (typeof b.message === "string") return b.message;
|
|
2865
|
+
return void 0;
|
|
2866
|
+
}
|
|
2867
|
+
function retryableError(status, code, message, raw) {
|
|
2868
|
+
return { ok: false, status, errorType: "retryable", errorCode: code, message, raw };
|
|
2869
|
+
}
|
|
850
2870
|
|
|
851
2871
|
// src/execute.ts
|
|
852
|
-
var
|
|
2872
|
+
var ANTHROPIC_URL2 = "https://api.anthropic.com/v1/messages";
|
|
853
2873
|
var OPENAI_URL = "https://api.openai.com/v1/chat/completions";
|
|
854
2874
|
var DEEPSEEK_URL = "https://api.deepseek.com/chat/completions";
|
|
855
2875
|
async function execute(request, opts = {}) {
|
|
@@ -870,16 +2890,22 @@ async function execute(request, opts = {}) {
|
|
|
870
2890
|
}
|
|
871
2891
|
}
|
|
872
2892
|
async function executeAnthropic(request, opts) {
|
|
873
|
-
const apiKey = opts.apiKeys
|
|
2893
|
+
const apiKey = resolveProviderKey("anthropic", { apiKeys: opts.apiKeys });
|
|
874
2894
|
if (!apiKey) {
|
|
875
2895
|
return terminalError(401, "auth", "ANTHROPIC_API_KEY missing");
|
|
876
2896
|
}
|
|
2897
|
+
if (opts.onChunk) {
|
|
2898
|
+
return streamAnthropic(request, apiKey, {
|
|
2899
|
+
onChunk: opts.onChunk,
|
|
2900
|
+
fetchImpl: opts.fetchImpl
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
877
2903
|
const { provider: _provider, ...body } = request;
|
|
878
2904
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
879
2905
|
let res;
|
|
880
2906
|
let json;
|
|
881
2907
|
try {
|
|
882
|
-
res = await fetchFn(
|
|
2908
|
+
res = await fetchFn(ANTHROPIC_URL2, {
|
|
883
2909
|
method: "POST",
|
|
884
2910
|
headers: {
|
|
885
2911
|
"x-api-key": apiKey,
|
|
@@ -890,9 +2916,9 @@ async function executeAnthropic(request, opts) {
|
|
|
890
2916
|
});
|
|
891
2917
|
json = await res.json().catch(() => ({}));
|
|
892
2918
|
} catch (err) {
|
|
893
|
-
return
|
|
2919
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
894
2920
|
}
|
|
895
|
-
if (!res.ok) return
|
|
2921
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
896
2922
|
return { ok: true, status: res.status, response: normalizeAnthropic(json) };
|
|
897
2923
|
}
|
|
898
2924
|
function normalizeAnthropic(raw) {
|
|
@@ -909,7 +2935,7 @@ function normalizeAnthropic(raw) {
|
|
|
909
2935
|
return { text, structuredOutput: null, toolCalls, tokens, finishReason: r.stop_reason, raw };
|
|
910
2936
|
}
|
|
911
2937
|
async function executeGoogle(request, opts) {
|
|
912
|
-
const apiKey =
|
|
2938
|
+
const apiKey = resolveProviderKey("google", { apiKeys: opts.apiKeys });
|
|
913
2939
|
if (!apiKey) {
|
|
914
2940
|
return terminalError(401, "auth", "GOOGLE_API_KEY/GEMINI_API_KEY missing");
|
|
915
2941
|
}
|
|
@@ -926,9 +2952,9 @@ async function executeGoogle(request, opts) {
|
|
|
926
2952
|
});
|
|
927
2953
|
json = await res.json().catch(() => ({}));
|
|
928
2954
|
} catch (err) {
|
|
929
|
-
return
|
|
2955
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
930
2956
|
}
|
|
931
|
-
if (!res.ok) return
|
|
2957
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
932
2958
|
return { ok: true, status: res.status, response: normalizeGoogle(json) };
|
|
933
2959
|
}
|
|
934
2960
|
function normalizeGoogle(raw) {
|
|
@@ -951,10 +2977,16 @@ function normalizeGoogle(raw) {
|
|
|
951
2977
|
return { text, structuredOutput: null, toolCalls, tokens, finishReason: candidate?.finishReason, raw };
|
|
952
2978
|
}
|
|
953
2979
|
async function executeOpenAI(request, opts) {
|
|
954
|
-
const apiKey = opts.apiKeys
|
|
2980
|
+
const apiKey = resolveProviderKey("openai", { apiKeys: opts.apiKeys });
|
|
955
2981
|
if (!apiKey) {
|
|
956
2982
|
return terminalError(401, "auth", "OPENAI_API_KEY missing");
|
|
957
2983
|
}
|
|
2984
|
+
if (opts.onChunk) {
|
|
2985
|
+
return streamOpenAILike(OPENAI_URL, request, apiKey, "openai", {
|
|
2986
|
+
onChunk: opts.onChunk,
|
|
2987
|
+
fetchImpl: opts.fetchImpl
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
958
2990
|
const { provider: _provider, ...body } = request;
|
|
959
2991
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
960
2992
|
let res;
|
|
@@ -967,16 +2999,22 @@ async function executeOpenAI(request, opts) {
|
|
|
967
2999
|
});
|
|
968
3000
|
json = await res.json().catch(() => ({}));
|
|
969
3001
|
} catch (err) {
|
|
970
|
-
return
|
|
3002
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
971
3003
|
}
|
|
972
|
-
if (!res.ok) return
|
|
3004
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
973
3005
|
return { ok: true, status: res.status, response: normalizeOpenAILike(json) };
|
|
974
3006
|
}
|
|
975
3007
|
async function executeDeepSeek(request, opts) {
|
|
976
|
-
const apiKey = opts.apiKeys
|
|
3008
|
+
const apiKey = resolveProviderKey("deepseek", { apiKeys: opts.apiKeys });
|
|
977
3009
|
if (!apiKey) {
|
|
978
3010
|
return terminalError(401, "auth", "DEEPSEEK_API_KEY missing");
|
|
979
3011
|
}
|
|
3012
|
+
if (opts.onChunk) {
|
|
3013
|
+
return streamOpenAILike(DEEPSEEK_URL, request, apiKey, "deepseek", {
|
|
3014
|
+
onChunk: opts.onChunk,
|
|
3015
|
+
fetchImpl: opts.fetchImpl
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
980
3018
|
const { provider: _provider, ...body } = request;
|
|
981
3019
|
const fetchFn = opts.fetchImpl ?? fetch;
|
|
982
3020
|
let res;
|
|
@@ -989,9 +3027,9 @@ async function executeDeepSeek(request, opts) {
|
|
|
989
3027
|
});
|
|
990
3028
|
json = await res.json().catch(() => ({}));
|
|
991
3029
|
} catch (err) {
|
|
992
|
-
return
|
|
3030
|
+
return retryableError2(0, "network_error", String(err), null);
|
|
993
3031
|
}
|
|
994
|
-
if (!res.ok) return
|
|
3032
|
+
if (!res.ok) return classifyHttpError2(res.status, json);
|
|
995
3033
|
return { ok: true, status: res.status, response: normalizeOpenAILike(json) };
|
|
996
3034
|
}
|
|
997
3035
|
function normalizeOpenAILike(raw) {
|
|
@@ -1001,7 +3039,7 @@ function normalizeOpenAILike(raw) {
|
|
|
1001
3039
|
const toolCalls = (choice?.message?.tool_calls ?? []).filter((tc) => tc.function?.name).map((tc, i) => ({
|
|
1002
3040
|
id: tc.id ?? `tc-${i}`,
|
|
1003
3041
|
name: tc.function.name,
|
|
1004
|
-
args:
|
|
3042
|
+
args: tryParseJson2(tc.function?.arguments) ?? {}
|
|
1005
3043
|
}));
|
|
1006
3044
|
const u = r.usage ?? {};
|
|
1007
3045
|
const tokens = {
|
|
@@ -1018,8 +3056,8 @@ function applyOverrides(request, overrides) {
|
|
|
1018
3056
|
if (!layer) return request;
|
|
1019
3057
|
return { ...request, ...layer };
|
|
1020
3058
|
}
|
|
1021
|
-
function
|
|
1022
|
-
const message =
|
|
3059
|
+
function classifyHttpError2(status, body) {
|
|
3060
|
+
const message = extractErrorMessage2(body) ?? `HTTP ${status}`;
|
|
1023
3061
|
if (status === 429) {
|
|
1024
3062
|
return { ok: false, status, errorType: "retryable", errorCode: "rate_limit", message, raw: body };
|
|
1025
3063
|
}
|
|
@@ -1040,7 +3078,7 @@ function classifyHttpError(status, body) {
|
|
|
1040
3078
|
}
|
|
1041
3079
|
return { ok: false, status, errorType: "terminal", errorCode: "unknown", message, raw: body };
|
|
1042
3080
|
}
|
|
1043
|
-
function
|
|
3081
|
+
function extractErrorMessage2(body) {
|
|
1044
3082
|
if (!body || typeof body !== "object") return void 0;
|
|
1045
3083
|
const b = body;
|
|
1046
3084
|
if (b.error && typeof b.error === "object") {
|
|
@@ -1053,10 +3091,10 @@ function extractErrorMessage(body) {
|
|
|
1053
3091
|
function terminalError(status, code, message) {
|
|
1054
3092
|
return { ok: false, status, errorType: "terminal", errorCode: code, message, raw: null };
|
|
1055
3093
|
}
|
|
1056
|
-
function
|
|
3094
|
+
function retryableError2(status, code, message, raw) {
|
|
1057
3095
|
return { ok: false, status, errorType: "retryable", errorCode: code, message, raw };
|
|
1058
3096
|
}
|
|
1059
|
-
function
|
|
3097
|
+
function tryParseJson2(s) {
|
|
1060
3098
|
if (typeof s !== "string" || s.length === 0) return void 0;
|
|
1061
3099
|
try {
|
|
1062
3100
|
const parsed = JSON.parse(s);
|
|
@@ -1068,15 +3106,148 @@ function tryParseJson(s) {
|
|
|
1068
3106
|
|
|
1069
3107
|
// src/call.ts
|
|
1070
3108
|
async function call(ir, opts = {}) {
|
|
3109
|
+
const traceId = generateTraceId();
|
|
3110
|
+
safeEmit(
|
|
3111
|
+
() => emitCompileStart(traceId, ir.appId, {
|
|
3112
|
+
appId: ir.appId,
|
|
3113
|
+
archetype: ir.intent.archetype,
|
|
3114
|
+
// alpha.41: ir.models is ChainModelEntry[] (string | { family }).
|
|
3115
|
+
// Glass-Box renderer carries string ids only; surface family entries
|
|
3116
|
+
// as their family tag prefixed so the panel still shows them in the
|
|
3117
|
+
// pre-resolution log.
|
|
3118
|
+
models: ir.models.map(
|
|
3119
|
+
(m) => typeof m === "string" ? m : `family:${m.family}`
|
|
3120
|
+
)
|
|
3121
|
+
})
|
|
3122
|
+
);
|
|
1071
3123
|
const initial = compileAndRegister(ir, opts);
|
|
3124
|
+
safeEmit(
|
|
3125
|
+
() => emitCompileDone(traceId, ir.appId, {
|
|
3126
|
+
target: initial.target,
|
|
3127
|
+
provider: initial.provider,
|
|
3128
|
+
fallbackChain: initial.fallbackChain,
|
|
3129
|
+
tokensIn: initial.tokensIn,
|
|
3130
|
+
estimatedCostUsd: initial.estimatedCostUsd,
|
|
3131
|
+
mutationsApplied: initial.mutationsApplied,
|
|
3132
|
+
advisories: initial.advisories
|
|
3133
|
+
})
|
|
3134
|
+
);
|
|
3135
|
+
for (const adv of initial.advisories) {
|
|
3136
|
+
safeEmit(
|
|
3137
|
+
() => emitAdvisoryFired(traceId, ir.appId, { code: adv.code, message: adv.message })
|
|
3138
|
+
);
|
|
3139
|
+
}
|
|
1072
3140
|
const start = Date.now();
|
|
1073
3141
|
const attempts = [];
|
|
1074
|
-
const
|
|
3142
|
+
const rawTargets = [initial.target, ...initial.fallbackChain];
|
|
3143
|
+
let unreachableFiltered;
|
|
3144
|
+
let targetsToTry;
|
|
3145
|
+
if (opts.noAutoFilter) {
|
|
3146
|
+
targetsToTry = rawTargets;
|
|
3147
|
+
} else {
|
|
3148
|
+
const dropped = [];
|
|
3149
|
+
targetsToTry = [];
|
|
3150
|
+
for (const t of rawTargets) {
|
|
3151
|
+
if (isModelReachable(t, { apiKeys: opts.apiKeys })) {
|
|
3152
|
+
targetsToTry.push(t);
|
|
3153
|
+
} else {
|
|
3154
|
+
dropped.push(t);
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
unreachableFiltered = dropped;
|
|
3158
|
+
if (targetsToTry.length === 0) {
|
|
3159
|
+
const latencyMs2 = Date.now() - start;
|
|
3160
|
+
await record({
|
|
3161
|
+
handle: initial.handle,
|
|
3162
|
+
tokensIn: 0,
|
|
3163
|
+
tokensOut: 0,
|
|
3164
|
+
latencyMs: latencyMs2,
|
|
3165
|
+
success: false,
|
|
3166
|
+
errorType: "no_reachable_models",
|
|
3167
|
+
promptPreview: extractPromptPreview(ir)
|
|
3168
|
+
});
|
|
3169
|
+
const noReachableAttempts = dropped.map((m) => ({
|
|
3170
|
+
model: m,
|
|
3171
|
+
status: "terminal",
|
|
3172
|
+
errorCode: "unreachable_provider",
|
|
3173
|
+
message: `No API key for ${m}'s provider \u2014 set one of PROVIDER_ENV_KEYS or pass apiKeys`
|
|
3174
|
+
}));
|
|
3175
|
+
throw new CallError(
|
|
3176
|
+
`call(): no reachable models in chain. Filtered: [${dropped.join(", ")}]. Add a key for one provider, or pass apiKeys.`,
|
|
3177
|
+
noReachableAttempts,
|
|
3178
|
+
void 0,
|
|
3179
|
+
"no_reachable_models"
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3182
|
+
const archetypeName = ir.intent?.archetype;
|
|
3183
|
+
if (archetypeName) {
|
|
3184
|
+
const ensured = ensureCrossProviderTail({
|
|
3185
|
+
chain: targetsToTry,
|
|
3186
|
+
archetype: archetypeName,
|
|
3187
|
+
apiKeys: opts.apiKeys
|
|
3188
|
+
});
|
|
3189
|
+
if (ensured.appended) {
|
|
3190
|
+
targetsToTry = ensured.chain;
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
let policyBlockedFiltered;
|
|
3195
|
+
if (opts.policy?.blockedModels && opts.policy.blockedModels.length > 0) {
|
|
3196
|
+
const blocked = new Set(opts.policy.blockedModels);
|
|
3197
|
+
const filtered = [];
|
|
3198
|
+
const dropped = [];
|
|
3199
|
+
for (const t of targetsToTry) {
|
|
3200
|
+
if (blocked.has(t)) {
|
|
3201
|
+
dropped.push(t);
|
|
3202
|
+
} else {
|
|
3203
|
+
filtered.push(t);
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
if (dropped.length > 0) {
|
|
3207
|
+
policyBlockedFiltered = dropped;
|
|
3208
|
+
targetsToTry = filtered;
|
|
3209
|
+
}
|
|
3210
|
+
if (targetsToTry.length === 0) {
|
|
3211
|
+
const latencyMs2 = Date.now() - start;
|
|
3212
|
+
await record({
|
|
3213
|
+
handle: initial.handle,
|
|
3214
|
+
tokensIn: 0,
|
|
3215
|
+
tokensOut: 0,
|
|
3216
|
+
latencyMs: latencyMs2,
|
|
3217
|
+
success: false,
|
|
3218
|
+
errorType: "all_blocked_by_policy",
|
|
3219
|
+
promptPreview: extractPromptPreview(ir)
|
|
3220
|
+
});
|
|
3221
|
+
const blockedAttempts = dropped.map((m) => ({
|
|
3222
|
+
model: m,
|
|
3223
|
+
status: "terminal",
|
|
3224
|
+
errorCode: "blocked_by_policy",
|
|
3225
|
+
message: `Skipped \u2014 model ${m} is in CompilePolicy.blockedModels`
|
|
3226
|
+
}));
|
|
3227
|
+
throw new CallError(
|
|
3228
|
+
`call(): all chain targets blocked by CompilePolicy.blockedModels: [${dropped.join(", ")}]`,
|
|
3229
|
+
blockedAttempts,
|
|
3230
|
+
void 0,
|
|
3231
|
+
"all_blocked_by_policy"
|
|
3232
|
+
);
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
1075
3235
|
let activeCompile = initial;
|
|
1076
3236
|
let lastErr;
|
|
3237
|
+
const failedProviders = /* @__PURE__ */ new Set();
|
|
1077
3238
|
for (let i = 0; i < targetsToTry.length; i++) {
|
|
1078
3239
|
const targetModel = targetsToTry[i];
|
|
1079
|
-
|
|
3240
|
+
const targetProfile = tryGetProfile(targetModel);
|
|
3241
|
+
if (targetProfile && failedProviders.has(targetProfile.provider) && !opts.noFallback) {
|
|
3242
|
+
attempts.push({
|
|
3243
|
+
model: targetModel,
|
|
3244
|
+
status: "terminal",
|
|
3245
|
+
errorCode: "auth_inferred",
|
|
3246
|
+
message: `Skipped \u2014 provider ${targetProfile.provider} returned 401/403 earlier in this call; same key inferred to fail`
|
|
3247
|
+
});
|
|
3248
|
+
continue;
|
|
3249
|
+
}
|
|
3250
|
+
if (targetModel !== initial.target) {
|
|
1080
3251
|
try {
|
|
1081
3252
|
activeCompile = compileAndRegister(
|
|
1082
3253
|
{
|
|
@@ -1096,49 +3267,98 @@ async function call(ir, opts = {}) {
|
|
|
1096
3267
|
continue;
|
|
1097
3268
|
}
|
|
1098
3269
|
}
|
|
3270
|
+
safeEmit(
|
|
3271
|
+
() => emitExecuteAttempt(traceId, ir.appId, { model: targetModel, attemptIndex: i })
|
|
3272
|
+
);
|
|
3273
|
+
const targetSupportsStreaming = targetProfile?.streaming === true;
|
|
3274
|
+
const streamingOnChunk = opts.onChunk && !opts.noStream && targetSupportsStreaming ? opts.onChunk : void 0;
|
|
1099
3275
|
const exec = await execute(activeCompile.request, {
|
|
1100
3276
|
apiKeys: opts.apiKeys,
|
|
1101
3277
|
fetchImpl: opts.fetchImpl,
|
|
1102
|
-
providerOverrides: opts.providerOverrides
|
|
3278
|
+
providerOverrides: opts.providerOverrides,
|
|
3279
|
+
onChunk: streamingOnChunk
|
|
1103
3280
|
});
|
|
1104
|
-
|
|
3281
|
+
const validated = exec.ok ? validateStructuredContract(exec, ir) : exec;
|
|
3282
|
+
if (validated.ok) {
|
|
1105
3283
|
attempts.push({ model: targetModel, status: "success" });
|
|
1106
3284
|
const latencyMs2 = Date.now() - start;
|
|
1107
|
-
|
|
3285
|
+
safeEmit(
|
|
3286
|
+
() => emitExecuteSuccess(traceId, ir.appId, {
|
|
3287
|
+
model: targetModel,
|
|
3288
|
+
tokensIn: validated.response.tokens.input,
|
|
3289
|
+
tokensOut: validated.response.tokens.output,
|
|
3290
|
+
latencyMs: latencyMs2
|
|
3291
|
+
})
|
|
3292
|
+
);
|
|
3293
|
+
const fellOver = targetModel !== initial.target;
|
|
3294
|
+
const fallbackReason = fellOver ? normalizeFallbackReason(attempts) : void 0;
|
|
1108
3295
|
await record({
|
|
1109
3296
|
handle: initial.handle,
|
|
1110
|
-
tokensIn:
|
|
1111
|
-
tokensOut:
|
|
3297
|
+
tokensIn: validated.response.tokens.input,
|
|
3298
|
+
tokensOut: validated.response.tokens.output,
|
|
1112
3299
|
latencyMs: latencyMs2,
|
|
1113
3300
|
success: true,
|
|
1114
|
-
emptyResponse:
|
|
1115
|
-
toolsCalled:
|
|
3301
|
+
emptyResponse: validated.response.tokens.output === 0,
|
|
3302
|
+
toolsCalled: validated.response.toolCalls.map((tc) => tc.name),
|
|
1116
3303
|
actualModel: targetModel !== initial.target ? targetModel : void 0,
|
|
1117
3304
|
mutationsApplied: targetModel !== initial.target ? activeCompile.mutationsApplied.map((m) => m.id) : void 0,
|
|
1118
3305
|
promptPreview: extractPromptPreview(ir),
|
|
1119
|
-
responsePreview:
|
|
1120
|
-
cacheReadInputTokens:
|
|
1121
|
-
cacheCreationInputTokens:
|
|
3306
|
+
responsePreview: validated.response.text.slice(0, 200),
|
|
3307
|
+
cacheReadInputTokens: validated.response.tokens.cached,
|
|
3308
|
+
cacheCreationInputTokens: validated.response.tokens.cacheCreated,
|
|
3309
|
+
// alpha.28 — Glass-Box renderer substrate (migration 018). call()
|
|
3310
|
+
// owns the lifecycle so it has direct visibility into finishReason
|
|
3311
|
+
// (from the normalized provider response), totalMs (mirrors latencyMs
|
|
3312
|
+
// for non-streaming; future streaming variant may diverge), and the
|
|
3313
|
+
// fell-over-from / fallback-reason pair (already computed above for
|
|
3314
|
+
// the CallResult return shape).
|
|
3315
|
+
finishReason: validated.response.finishReason,
|
|
3316
|
+
totalMs: latencyMs2,
|
|
3317
|
+
fellOverFrom: fellOver ? initial.target : void 0,
|
|
3318
|
+
fallbackReason
|
|
1122
3319
|
});
|
|
3320
|
+
if (fellOver) {
|
|
3321
|
+
const firstFailed = attempts.find((a) => a.status !== "success");
|
|
3322
|
+
if (firstFailed) {
|
|
3323
|
+
safeEmit(
|
|
3324
|
+
() => emitFallbackWalked(traceId, ir.appId, {
|
|
3325
|
+
from: initial.target,
|
|
3326
|
+
to: targetModel,
|
|
3327
|
+
reason: fallbackReason ?? "unknown",
|
|
3328
|
+
attempt: firstFailed
|
|
3329
|
+
})
|
|
3330
|
+
);
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
1123
3333
|
return {
|
|
1124
3334
|
handle: initial.handle,
|
|
1125
3335
|
actualModel: targetModel,
|
|
1126
3336
|
requestedModel: initial.target,
|
|
1127
3337
|
provider: activeCompile.provider,
|
|
1128
|
-
response:
|
|
3338
|
+
response: validated.response,
|
|
1129
3339
|
latencyMs: latencyMs2,
|
|
1130
3340
|
mutationsApplied: activeCompile.mutationsApplied,
|
|
1131
|
-
attempts
|
|
3341
|
+
attempts,
|
|
3342
|
+
servedBy: targetModel,
|
|
3343
|
+
fellOverFrom: fellOver ? initial.target : void 0,
|
|
3344
|
+
fallbackReason,
|
|
3345
|
+
unreachableFiltered,
|
|
3346
|
+
policyBlockedFiltered,
|
|
3347
|
+
traceId
|
|
1132
3348
|
};
|
|
1133
3349
|
}
|
|
1134
3350
|
attempts.push({
|
|
1135
3351
|
model: targetModel,
|
|
1136
|
-
status:
|
|
1137
|
-
errorCode:
|
|
1138
|
-
message:
|
|
3352
|
+
status: validated.errorType,
|
|
3353
|
+
errorCode: validated.errorCode,
|
|
3354
|
+
message: validated.message
|
|
1139
3355
|
});
|
|
1140
|
-
lastErr =
|
|
1141
|
-
if (
|
|
3356
|
+
lastErr = validated;
|
|
3357
|
+
if (validated.errorType === "terminal" || opts.noFallback) {
|
|
3358
|
+
if (validated.errorCode === "auth" && !opts.noFallback && activeCompile.provider) {
|
|
3359
|
+
failedProviders.add(activeCompile.provider);
|
|
3360
|
+
continue;
|
|
3361
|
+
}
|
|
1142
3362
|
break;
|
|
1143
3363
|
}
|
|
1144
3364
|
}
|
|
@@ -1152,8 +3372,10 @@ async function call(ir, opts = {}) {
|
|
|
1152
3372
|
errorType: lastErr?.errorCode,
|
|
1153
3373
|
promptPreview: extractPromptPreview(ir)
|
|
1154
3374
|
});
|
|
3375
|
+
const filteredNote = unreachableFiltered && unreachableFiltered.length > 0 ? ` (also auto-filtered: [${unreachableFiltered.join(", ")}] \u2014 no API key)` : "";
|
|
3376
|
+
const blockedNote = policyBlockedFiltered && policyBlockedFiltered.length > 0 ? ` (also policy-blocked: [${policyBlockedFiltered.join(", ")}])` : "";
|
|
1155
3377
|
throw new CallError(
|
|
1156
|
-
`call(): all attempts failed${lastErr ? ` \u2014 ${lastErr.errorCode}: ${lastErr.message}` : ""}`,
|
|
3378
|
+
`call(): all attempts failed${lastErr ? ` \u2014 ${lastErr.errorCode}: ${lastErr.message}` : ""}${filteredNote}${blockedNote}`,
|
|
1157
3379
|
attempts,
|
|
1158
3380
|
lastErr?.status,
|
|
1159
3381
|
lastErr?.errorCode
|
|
@@ -1175,20 +3397,95 @@ function extractPromptPreview(ir) {
|
|
|
1175
3397
|
if (lastHist) return lastHist.slice(0, 200);
|
|
1176
3398
|
return void 0;
|
|
1177
3399
|
}
|
|
1178
|
-
function
|
|
1179
|
-
if (!ir.constraints?.structuredOutput)
|
|
1180
|
-
|
|
3400
|
+
function validateStructuredContract(exec, ir) {
|
|
3401
|
+
if (!ir.constraints?.structuredOutput) {
|
|
3402
|
+
return { ok: true, response: exec.response };
|
|
3403
|
+
}
|
|
3404
|
+
const finish = (exec.response.finishReason ?? "").toLowerCase();
|
|
3405
|
+
if (finish === "max_tokens" || finish === "length") {
|
|
3406
|
+
return {
|
|
3407
|
+
ok: false,
|
|
3408
|
+
status: exec.status,
|
|
3409
|
+
errorType: "retryable",
|
|
3410
|
+
errorCode: "max_tokens_on_structured_output",
|
|
3411
|
+
message: `Provider returned finishReason="${exec.response.finishReason}" on a structured-output call \u2014 output truncated mid-token, JSON cannot be valid`,
|
|
3412
|
+
raw: exec.response.raw
|
|
3413
|
+
};
|
|
3414
|
+
}
|
|
3415
|
+
if (!exec.response.text) {
|
|
3416
|
+
return { ok: true, response: exec.response };
|
|
3417
|
+
}
|
|
1181
3418
|
try {
|
|
1182
|
-
const parsed = JSON.parse(response.text);
|
|
1183
|
-
return { ...response, structuredOutput: parsed };
|
|
3419
|
+
const parsed = JSON.parse(exec.response.text);
|
|
3420
|
+
return { ok: true, response: { ...exec.response, structuredOutput: parsed } };
|
|
1184
3421
|
} catch (err) {
|
|
1185
3422
|
return {
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
3423
|
+
ok: false,
|
|
3424
|
+
status: exec.status,
|
|
3425
|
+
errorType: "retryable",
|
|
3426
|
+
errorCode: "structured_output_parse_failed",
|
|
3427
|
+
message: err instanceof Error ? err.message : String(err),
|
|
3428
|
+
raw: exec.response.raw
|
|
1189
3429
|
};
|
|
1190
3430
|
}
|
|
1191
3431
|
}
|
|
3432
|
+
function normalizeFallbackReason(attempts) {
|
|
3433
|
+
const first = attempts.find((a) => a.status !== "success");
|
|
3434
|
+
if (!first) return void 0;
|
|
3435
|
+
const code = first.errorCode ?? "";
|
|
3436
|
+
if (code === "rate_limit_429" || code === "rate_limit") return "rate_limit";
|
|
3437
|
+
if (code === "max_tokens_on_structured_output" || code === "structured_output_parse_failed") {
|
|
3438
|
+
return "cliff";
|
|
3439
|
+
}
|
|
3440
|
+
if (code === "cost_cap_exceeded") return "cost_cap";
|
|
3441
|
+
if (code === "auth" || code === "auth_inferred") return "provider_auth_failed";
|
|
3442
|
+
return "provider_error";
|
|
3443
|
+
}
|
|
3444
|
+
function generateTraceId() {
|
|
3445
|
+
try {
|
|
3446
|
+
const g = globalThis;
|
|
3447
|
+
if (g.crypto && typeof g.crypto.randomUUID === "function") {
|
|
3448
|
+
return g.crypto.randomUUID();
|
|
3449
|
+
}
|
|
3450
|
+
} catch {
|
|
3451
|
+
}
|
|
3452
|
+
const hex = (n) => Math.floor(Math.random() * Math.pow(16, n)).toString(16).padStart(n, "0");
|
|
3453
|
+
return `${hex(8)}-${hex(4)}-${hex(4)}-${hex(4)}-${hex(12)}`;
|
|
3454
|
+
}
|
|
3455
|
+
function safeEmit(fn) {
|
|
3456
|
+
try {
|
|
3457
|
+
fn();
|
|
3458
|
+
} catch {
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
// src/streamtext-helpers.ts
|
|
3463
|
+
function attachCacheControlToStreamTextInput(result, convertedMessages) {
|
|
3464
|
+
const messages = convertedMessages.map((m) => ({ ...m }));
|
|
3465
|
+
const markIdx = result.diagnostics.historyCacheMarkIndex;
|
|
3466
|
+
if (result.provider === "anthropic" && typeof markIdx === "number" && markIdx >= 0 && markIdx < messages.length) {
|
|
3467
|
+
const target = messages[markIdx];
|
|
3468
|
+
if (target) {
|
|
3469
|
+
messages[markIdx] = {
|
|
3470
|
+
role: target.role,
|
|
3471
|
+
content: target.content,
|
|
3472
|
+
providerOptions: {
|
|
3473
|
+
...target.providerOptions ?? {},
|
|
3474
|
+
anthropic: {
|
|
3475
|
+
...target.providerOptions?.anthropic ?? {},
|
|
3476
|
+
cacheControl: { type: "ephemeral" }
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
const systemMessages = result.systemMessages;
|
|
3483
|
+
const hasStructuredSystemCacheControl = result.provider === "anthropic" && systemMessages.some(
|
|
3484
|
+
(m) => m.providerOptions?.anthropic?.cacheControl !== void 0
|
|
3485
|
+
);
|
|
3486
|
+
const system = hasStructuredSystemCacheControl ? systemMessages : systemMessages.map((m) => m.content).join("\n\n");
|
|
3487
|
+
return { system, messages };
|
|
3488
|
+
}
|
|
1192
3489
|
|
|
1193
3490
|
// src/oracle.ts
|
|
1194
3491
|
var DEFAULT_DIMENSIONS = ["correctness", "completeness", "conciseness", "format"];
|
|
@@ -1278,6 +3575,338 @@ function clamp(n) {
|
|
|
1278
3575
|
return Math.max(0, Math.min(1, n));
|
|
1279
3576
|
}
|
|
1280
3577
|
|
|
3578
|
+
// src/advisories-api.ts
|
|
3579
|
+
var SEVERITY_SET = /* @__PURE__ */ new Set(["info", "warn", "critical"]);
|
|
3580
|
+
var STATUS_SET = /* @__PURE__ */ new Set(["open", "snoozed", "resolved"]);
|
|
3581
|
+
var RESOLUTION_SOURCE_SET = /* @__PURE__ */ new Set([
|
|
3582
|
+
"auto",
|
|
3583
|
+
"consumer-marked",
|
|
3584
|
+
"declined"
|
|
3585
|
+
]);
|
|
3586
|
+
function asString2(v) {
|
|
3587
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
3588
|
+
}
|
|
3589
|
+
function asSeverity(v) {
|
|
3590
|
+
if (typeof v === "string" && SEVERITY_SET.has(v)) {
|
|
3591
|
+
return v;
|
|
3592
|
+
}
|
|
3593
|
+
return "info";
|
|
3594
|
+
}
|
|
3595
|
+
function asStatus(v) {
|
|
3596
|
+
if (typeof v === "string" && STATUS_SET.has(v)) {
|
|
3597
|
+
return v;
|
|
3598
|
+
}
|
|
3599
|
+
return "open";
|
|
3600
|
+
}
|
|
3601
|
+
function asResolutionSource(v) {
|
|
3602
|
+
if (typeof v === "string" && RESOLUTION_SOURCE_SET.has(v)) {
|
|
3603
|
+
return v;
|
|
3604
|
+
}
|
|
3605
|
+
return void 0;
|
|
3606
|
+
}
|
|
3607
|
+
function rowToAdvisory(row) {
|
|
3608
|
+
const archetype = asString2(row.applies_to_archetype);
|
|
3609
|
+
const model = asString2(row.applies_to_model);
|
|
3610
|
+
const docsLink = asString2(row.docs_url);
|
|
3611
|
+
const suggestion = asString2(row.suggestion);
|
|
3612
|
+
let suggestedFix = null;
|
|
3613
|
+
if (docsLink || suggestion) {
|
|
3614
|
+
suggestedFix = { type: "manual" };
|
|
3615
|
+
if (docsLink) suggestedFix.docsLink = docsLink;
|
|
3616
|
+
if (suggestion) suggestedFix.before = suggestion;
|
|
3617
|
+
}
|
|
3618
|
+
const out = {
|
|
3619
|
+
id: typeof row.id === "string" ? row.id : "",
|
|
3620
|
+
rule: typeof row.rule === "string" ? row.rule : "",
|
|
3621
|
+
severity: asSeverity(row.severity),
|
|
3622
|
+
openedAt: typeof row.opened_at === "string" ? row.opened_at : "",
|
|
3623
|
+
lastObservedAt: typeof row.last_observed_at === "string" ? row.last_observed_at : "",
|
|
3624
|
+
observationCount: typeof row.observation_count === "number" ? row.observation_count : 0,
|
|
3625
|
+
appliesTo: {
|
|
3626
|
+
...archetype ? { archetype } : {},
|
|
3627
|
+
...model ? { model } : {}
|
|
3628
|
+
},
|
|
3629
|
+
message: typeof row.message === "string" ? row.message : "",
|
|
3630
|
+
suggestedFix,
|
|
3631
|
+
autoApplicable: false,
|
|
3632
|
+
// reserved — alpha.30+
|
|
3633
|
+
status: asStatus(row.status)
|
|
3634
|
+
};
|
|
3635
|
+
const resolvedAt = asString2(row.resolved_at);
|
|
3636
|
+
if (resolvedAt) out.resolvedAt = resolvedAt;
|
|
3637
|
+
const resolutionSource = asResolutionSource(row.resolution_source);
|
|
3638
|
+
if (resolutionSource) out.resolutionSource = resolutionSource;
|
|
3639
|
+
const resolutionNote = asString2(row.resolution_note);
|
|
3640
|
+
if (resolutionNote) out.resolutionNote = resolutionNote;
|
|
3641
|
+
return out;
|
|
3642
|
+
}
|
|
3643
|
+
function resolveFetch(injected) {
|
|
3644
|
+
return injected ?? ((...args) => globalThis.fetch(...args));
|
|
3645
|
+
}
|
|
3646
|
+
function normalizeEndpoint2(endpoint) {
|
|
3647
|
+
return endpoint.replace(/\/+$/, "");
|
|
3648
|
+
}
|
|
3649
|
+
async function getActionableAdvisories(opts) {
|
|
3650
|
+
const {
|
|
3651
|
+
appId,
|
|
3652
|
+
severity,
|
|
3653
|
+
status,
|
|
3654
|
+
brainEndpoint,
|
|
3655
|
+
brainJwt,
|
|
3656
|
+
brainAnonKey,
|
|
3657
|
+
fetch: injectedFetch
|
|
3658
|
+
} = opts;
|
|
3659
|
+
if (!appId) {
|
|
3660
|
+
throw new Error("getActionableAdvisories: appId is required");
|
|
3661
|
+
}
|
|
3662
|
+
const doFetch = resolveFetch(injectedFetch);
|
|
3663
|
+
const base = normalizeEndpoint2(brainEndpoint);
|
|
3664
|
+
const qs = new URLSearchParams();
|
|
3665
|
+
qs.set("app_id", `eq.${appId}`);
|
|
3666
|
+
if (severity) qs.set("severity", `eq.${severity}`);
|
|
3667
|
+
const effectiveStatus = status ?? "open";
|
|
3668
|
+
if (effectiveStatus !== "all") {
|
|
3669
|
+
qs.set("status", `eq.${effectiveStatus}`);
|
|
3670
|
+
}
|
|
3671
|
+
qs.set("order", "last_observed_at.desc");
|
|
3672
|
+
const url = `${base}/rest/v1/actionable_advisories_v?${qs.toString()}`;
|
|
3673
|
+
let res;
|
|
3674
|
+
try {
|
|
3675
|
+
res = await doFetch(url, {
|
|
3676
|
+
method: "GET",
|
|
3677
|
+
headers: {
|
|
3678
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
3679
|
+
apikey: brainAnonKey,
|
|
3680
|
+
Accept: "application/json"
|
|
3681
|
+
}
|
|
3682
|
+
});
|
|
3683
|
+
} catch (err) {
|
|
3684
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3685
|
+
throw new Error(`getActionableAdvisories: network error: ${msg}`);
|
|
3686
|
+
}
|
|
3687
|
+
if (res.status === 401 || res.status === 403) {
|
|
3688
|
+
throw new Error("getActionableAdvisories: brain auth misconfig");
|
|
3689
|
+
}
|
|
3690
|
+
if (res.status >= 500) {
|
|
3691
|
+
throw new Error(`getActionableAdvisories: brain unavailable (${res.status})`);
|
|
3692
|
+
}
|
|
3693
|
+
if (!res.ok) {
|
|
3694
|
+
throw new Error(`getActionableAdvisories: bad request (${res.status})`);
|
|
3695
|
+
}
|
|
3696
|
+
let rows;
|
|
3697
|
+
try {
|
|
3698
|
+
rows = await res.json();
|
|
3699
|
+
} catch {
|
|
3700
|
+
throw new Error("getActionableAdvisories: malformed brain response");
|
|
3701
|
+
}
|
|
3702
|
+
if (!Array.isArray(rows)) {
|
|
3703
|
+
throw new Error("getActionableAdvisories: expected array from brain");
|
|
3704
|
+
}
|
|
3705
|
+
const out = [];
|
|
3706
|
+
for (const raw of rows) {
|
|
3707
|
+
if (raw && typeof raw === "object") {
|
|
3708
|
+
out.push(rowToAdvisory(raw));
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
return out;
|
|
3712
|
+
}
|
|
3713
|
+
async function markAdvisoryResolved(opts) {
|
|
3714
|
+
const {
|
|
3715
|
+
id,
|
|
3716
|
+
resolutionNote,
|
|
3717
|
+
brainEndpoint,
|
|
3718
|
+
brainJwt,
|
|
3719
|
+
brainAnonKey,
|
|
3720
|
+
fetch: injectedFetch
|
|
3721
|
+
} = opts;
|
|
3722
|
+
if (!id) {
|
|
3723
|
+
return { ok: false, reason: "id_required" };
|
|
3724
|
+
}
|
|
3725
|
+
const doFetch = resolveFetch(injectedFetch);
|
|
3726
|
+
const base = normalizeEndpoint2(brainEndpoint);
|
|
3727
|
+
const lookupUrl = `${base}/rest/v1/actionable_advisories_v?id=eq.${encodeURIComponent(id)}&select=app_id,rule`;
|
|
3728
|
+
let lookupRes;
|
|
3729
|
+
try {
|
|
3730
|
+
lookupRes = await doFetch(lookupUrl, {
|
|
3731
|
+
method: "GET",
|
|
3732
|
+
headers: {
|
|
3733
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
3734
|
+
apikey: brainAnonKey,
|
|
3735
|
+
Accept: "application/json"
|
|
3736
|
+
}
|
|
3737
|
+
});
|
|
3738
|
+
} catch (err) {
|
|
3739
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3740
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
3741
|
+
}
|
|
3742
|
+
if (lookupRes.status === 401 || lookupRes.status === 403) {
|
|
3743
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
3744
|
+
}
|
|
3745
|
+
if (lookupRes.status >= 500) {
|
|
3746
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
3747
|
+
}
|
|
3748
|
+
if (!lookupRes.ok) {
|
|
3749
|
+
return { ok: false, reason: `brain_lookup_failed:${lookupRes.status}` };
|
|
3750
|
+
}
|
|
3751
|
+
let lookupRows;
|
|
3752
|
+
try {
|
|
3753
|
+
lookupRows = await lookupRes.json();
|
|
3754
|
+
} catch {
|
|
3755
|
+
return { ok: false, reason: "brain_lookup_malformed" };
|
|
3756
|
+
}
|
|
3757
|
+
if (!Array.isArray(lookupRows) || lookupRows.length === 0) {
|
|
3758
|
+
return { ok: false, reason: "advisory_not_found" };
|
|
3759
|
+
}
|
|
3760
|
+
const tuple = lookupRows[0];
|
|
3761
|
+
const appId = typeof tuple.app_id === "string" ? tuple.app_id : "";
|
|
3762
|
+
const code = typeof tuple.rule === "string" ? tuple.rule : "";
|
|
3763
|
+
if (!appId || !code) {
|
|
3764
|
+
return { ok: false, reason: "advisory_tuple_invalid" };
|
|
3765
|
+
}
|
|
3766
|
+
const outcomesUrl = `${base}/rest/v1/compile_outcomes?app_id=eq.${encodeURIComponent(appId)}&select=id`;
|
|
3767
|
+
let outcomesRes;
|
|
3768
|
+
try {
|
|
3769
|
+
outcomesRes = await doFetch(outcomesUrl, {
|
|
3770
|
+
method: "GET",
|
|
3771
|
+
headers: {
|
|
3772
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
3773
|
+
apikey: brainAnonKey,
|
|
3774
|
+
Accept: "application/json"
|
|
3775
|
+
}
|
|
3776
|
+
});
|
|
3777
|
+
} catch (err) {
|
|
3778
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3779
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
3780
|
+
}
|
|
3781
|
+
if (outcomesRes.status === 401 || outcomesRes.status === 403) {
|
|
3782
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
3783
|
+
}
|
|
3784
|
+
if (outcomesRes.status >= 500) {
|
|
3785
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
3786
|
+
}
|
|
3787
|
+
if (!outcomesRes.ok) {
|
|
3788
|
+
return { ok: false, reason: `brain_lookup_failed:${outcomesRes.status}` };
|
|
3789
|
+
}
|
|
3790
|
+
let outcomeRows;
|
|
3791
|
+
try {
|
|
3792
|
+
outcomeRows = await outcomesRes.json();
|
|
3793
|
+
} catch {
|
|
3794
|
+
return { ok: false, reason: "brain_lookup_malformed" };
|
|
3795
|
+
}
|
|
3796
|
+
if (!Array.isArray(outcomeRows)) {
|
|
3797
|
+
return { ok: false, reason: "brain_lookup_malformed" };
|
|
3798
|
+
}
|
|
3799
|
+
const outcomeIds = [];
|
|
3800
|
+
for (const row of outcomeRows) {
|
|
3801
|
+
if (row && typeof row === "object") {
|
|
3802
|
+
const idVal = row.id;
|
|
3803
|
+
if (typeof idVal === "number" && Number.isFinite(idVal)) {
|
|
3804
|
+
outcomeIds.push(idVal);
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
if (outcomeIds.length === 0) {
|
|
3809
|
+
return { ok: true };
|
|
3810
|
+
}
|
|
3811
|
+
const inList = outcomeIds.join(",");
|
|
3812
|
+
const patchUrl = `${base}/rest/v1/compile_outcome_advisories?outcome_id=in.(${inList})&code=eq.${encodeURIComponent(code)}&resolved_at=is.null`;
|
|
3813
|
+
const patchBody = {
|
|
3814
|
+
resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3815
|
+
resolution_source: "consumer-marked"
|
|
3816
|
+
};
|
|
3817
|
+
if (resolutionNote !== void 0) {
|
|
3818
|
+
patchBody.resolution_note = resolutionNote;
|
|
3819
|
+
}
|
|
3820
|
+
let patchRes;
|
|
3821
|
+
try {
|
|
3822
|
+
patchRes = await doFetch(patchUrl, {
|
|
3823
|
+
method: "PATCH",
|
|
3824
|
+
headers: {
|
|
3825
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
3826
|
+
apikey: brainAnonKey,
|
|
3827
|
+
"Content-Type": "application/json",
|
|
3828
|
+
Accept: "application/json",
|
|
3829
|
+
// PostgREST default is no return; we don't need the row back.
|
|
3830
|
+
Prefer: "return=minimal"
|
|
3831
|
+
},
|
|
3832
|
+
body: JSON.stringify(patchBody)
|
|
3833
|
+
});
|
|
3834
|
+
} catch (err) {
|
|
3835
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3836
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
3837
|
+
}
|
|
3838
|
+
if (patchRes.status === 401 || patchRes.status === 403) {
|
|
3839
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
3840
|
+
}
|
|
3841
|
+
if (patchRes.status >= 500) {
|
|
3842
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
3843
|
+
}
|
|
3844
|
+
if (!patchRes.ok) {
|
|
3845
|
+
return { ok: false, reason: `patch_failed:${patchRes.status}` };
|
|
3846
|
+
}
|
|
3847
|
+
return { ok: true };
|
|
3848
|
+
}
|
|
3849
|
+
async function markExclusionFindingHandled(opts) {
|
|
3850
|
+
const {
|
|
3851
|
+
appId,
|
|
3852
|
+
archetype,
|
|
3853
|
+
excludedModel,
|
|
3854
|
+
resolution,
|
|
3855
|
+
resolutionNote,
|
|
3856
|
+
brainEndpoint,
|
|
3857
|
+
brainJwt,
|
|
3858
|
+
brainAnonKey,
|
|
3859
|
+
fetch: injectedFetch
|
|
3860
|
+
} = opts;
|
|
3861
|
+
if (!appId) return { ok: false, reason: "app_id_required" };
|
|
3862
|
+
if (!archetype) return { ok: false, reason: "archetype_required" };
|
|
3863
|
+
if (!excludedModel) {
|
|
3864
|
+
return { ok: false, reason: "excluded_model_required" };
|
|
3865
|
+
}
|
|
3866
|
+
if (resolution !== "consumer-marked" && resolution !== "declined" && resolution !== "probed-unblock" && resolution !== "probed-stay-excluded") {
|
|
3867
|
+
return { ok: false, reason: "resolution_invalid" };
|
|
3868
|
+
}
|
|
3869
|
+
const doFetch = resolveFetch(injectedFetch);
|
|
3870
|
+
const base = normalizeEndpoint2(brainEndpoint);
|
|
3871
|
+
const url = `${base}/rest/v1/exclusion_findings?app_id=eq.${encodeURIComponent(appId)}&intent_archetype=eq.${encodeURIComponent(archetype)}&excluded_model=eq.${encodeURIComponent(excludedModel)}&resolved_at=is.null`;
|
|
3872
|
+
const patchBody = {
|
|
3873
|
+
resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3874
|
+
resolution_source: resolution
|
|
3875
|
+
};
|
|
3876
|
+
if (resolutionNote !== void 0) {
|
|
3877
|
+
patchBody.resolution_note = resolutionNote;
|
|
3878
|
+
}
|
|
3879
|
+
let res;
|
|
3880
|
+
try {
|
|
3881
|
+
res = await doFetch(url, {
|
|
3882
|
+
method: "PATCH",
|
|
3883
|
+
headers: {
|
|
3884
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
3885
|
+
apikey: brainAnonKey,
|
|
3886
|
+
"Content-Type": "application/json",
|
|
3887
|
+
Accept: "application/json",
|
|
3888
|
+
// PATCH may match zero rows when already resolved — return=minimal
|
|
3889
|
+
// keeps the response body empty so the success path is uniform.
|
|
3890
|
+
Prefer: "return=minimal"
|
|
3891
|
+
},
|
|
3892
|
+
body: JSON.stringify(patchBody)
|
|
3893
|
+
});
|
|
3894
|
+
} catch (err) {
|
|
3895
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3896
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
3897
|
+
}
|
|
3898
|
+
if (res.status === 401 || res.status === 403) {
|
|
3899
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
3900
|
+
}
|
|
3901
|
+
if (res.status >= 500) {
|
|
3902
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
3903
|
+
}
|
|
3904
|
+
if (!res.ok) {
|
|
3905
|
+
return { ok: false, reason: `patch_failed:${res.status}` };
|
|
3906
|
+
}
|
|
3907
|
+
return { ok: true };
|
|
3908
|
+
}
|
|
3909
|
+
|
|
1281
3910
|
// src/index.ts
|
|
1282
3911
|
function compile2(ir, opts) {
|
|
1283
3912
|
const result = compile(ir, opts);
|
|
@@ -1285,12 +3914,23 @@ function compile2(ir, opts) {
|
|
|
1285
3914
|
return result;
|
|
1286
3915
|
}
|
|
1287
3916
|
export {
|
|
3917
|
+
ABSOLUTE_FLOOR,
|
|
1288
3918
|
ALIASES,
|
|
1289
3919
|
ALL_ARCHETYPES,
|
|
3920
|
+
ARCHETYPE_FLOOR_DEFAULT,
|
|
1290
3921
|
CallError,
|
|
3922
|
+
DEFAULT_FINDINGS_ENDPOINT,
|
|
1291
3923
|
DIALECT_VERSION,
|
|
3924
|
+
FamilyResolutionError,
|
|
1292
3925
|
INTENT_ARCHETYPES,
|
|
3926
|
+
MEASURED_GROUNDING_MIN_N,
|
|
3927
|
+
PRODUCER_OWNED_RULE_CODES,
|
|
3928
|
+
PROVIDER_ENV_KEYS,
|
|
3929
|
+
RULE_SEQUENTIAL_TOOL_CLIFF,
|
|
3930
|
+
TRANSLATOR_FLOOR,
|
|
1293
3931
|
allProfiles,
|
|
3932
|
+
applySectionRewrites,
|
|
3933
|
+
attachCacheControlToStreamTextInput,
|
|
1294
3934
|
bucketContext,
|
|
1295
3935
|
bucketHistory,
|
|
1296
3936
|
bucketToolCount,
|
|
@@ -1300,14 +3940,50 @@ export {
|
|
|
1300
3940
|
compile2 as compile,
|
|
1301
3941
|
configureBrain,
|
|
1302
3942
|
countTokens,
|
|
3943
|
+
deriveFamilyFromModelId,
|
|
3944
|
+
deriveOwnership,
|
|
1303
3945
|
execute,
|
|
3946
|
+
getActionableAdvisories,
|
|
3947
|
+
getAllStarterChains,
|
|
3948
|
+
getAllStarterChainsWithGrounding,
|
|
3949
|
+
getArchetypePerfScore,
|
|
3950
|
+
getDefaultFallbackChain,
|
|
3951
|
+
getDefaultFallbackChainWithGrounding,
|
|
3952
|
+
getModelCompatibility,
|
|
3953
|
+
getPerAxisMetrics,
|
|
1304
3954
|
getProfile,
|
|
3955
|
+
getReachabilityDiagnostic,
|
|
3956
|
+
getRecommendedPrimary,
|
|
3957
|
+
getSequentialStarterChain,
|
|
3958
|
+
getSequentialStarterChainWithGrounding,
|
|
3959
|
+
getStaleExclusionFindings,
|
|
3960
|
+
getStarterChain,
|
|
3961
|
+
getStarterChainWithGrounding,
|
|
1305
3962
|
hashShape,
|
|
1306
3963
|
isArchetype,
|
|
3964
|
+
isBrainQueryActiveFor,
|
|
3965
|
+
isExclusionFindingsBrainActive,
|
|
3966
|
+
isModelReachable,
|
|
3967
|
+
isProviderReachable,
|
|
1307
3968
|
learningKey,
|
|
3969
|
+
loadAliasesFromBrain,
|
|
3970
|
+
loadArchetypePerfFromBrain,
|
|
3971
|
+
loadArchetypePerfNFromBrain,
|
|
3972
|
+
loadChainsFromBrain,
|
|
3973
|
+
loadModelsFromBrain,
|
|
3974
|
+
loadPricingFromBrain,
|
|
3975
|
+
markAdvisoryResolved,
|
|
3976
|
+
markExclusionFindingHandled,
|
|
3977
|
+
markPromoteReadyHandled,
|
|
3978
|
+
profileToRow,
|
|
1308
3979
|
profilesByProvider,
|
|
3980
|
+
readBrainReadEnv,
|
|
1309
3981
|
record,
|
|
3982
|
+
recordOutcome,
|
|
1310
3983
|
resetTokenizer,
|
|
3984
|
+
resolvePricingAt,
|
|
3985
|
+
resolveProviderKey,
|
|
3986
|
+
runAdvisor,
|
|
1311
3987
|
setTokenizer,
|
|
1312
3988
|
tryGetProfile
|
|
1313
3989
|
};
|