@blockrun/clawrouter 0.9.27 → 0.9.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +100 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +100 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -511,6 +511,7 @@ function calibrateConfidence(distance, steepness) {
|
|
|
511
511
|
}
|
|
512
512
|
|
|
513
513
|
// src/router/selector.ts
|
|
514
|
+
var BASELINE_MODEL_ID = "anthropic/claude-opus-4-5";
|
|
514
515
|
function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPricing, estimatedInputTokens, maxOutputTokens, routingProfile) {
|
|
515
516
|
const tierConfig = tierConfigs[tier];
|
|
516
517
|
const model = tierConfig.primary;
|
|
@@ -520,7 +521,7 @@ function selectModel(tier, confidence, method, reasoning, tierConfigs, modelPric
|
|
|
520
521
|
const inputCost = estimatedInputTokens / 1e6 * inputPrice;
|
|
521
522
|
const outputCost = maxOutputTokens / 1e6 * outputPrice;
|
|
522
523
|
const costEstimate = inputCost + outputCost;
|
|
523
|
-
const opusPricing = modelPricing.get(
|
|
524
|
+
const opusPricing = modelPricing.get(BASELINE_MODEL_ID);
|
|
524
525
|
const opusInputPrice = opusPricing?.inputPrice ?? 0;
|
|
525
526
|
const opusOutputPrice = opusPricing?.outputPrice ?? 0;
|
|
526
527
|
const baselineInput = estimatedInputTokens / 1e6 * opusInputPrice;
|
|
@@ -549,7 +550,7 @@ function calculateModelCost(model, modelPricing, estimatedInputTokens, maxOutput
|
|
|
549
550
|
const inputCost = estimatedInputTokens / 1e6 * inputPrice;
|
|
550
551
|
const outputCost = maxOutputTokens / 1e6 * outputPrice;
|
|
551
552
|
const costEstimate = inputCost + outputCost;
|
|
552
|
-
const opusPricing = modelPricing.get(
|
|
553
|
+
const opusPricing = modelPricing.get(BASELINE_MODEL_ID);
|
|
553
554
|
const opusInputPrice = opusPricing?.inputPrice ?? 0;
|
|
554
555
|
const opusOutputPrice = opusPricing?.outputPrice ?? 0;
|
|
555
556
|
const baselineInput = estimatedInputTokens / 1e6 * opusInputPrice;
|
|
@@ -1482,6 +1483,10 @@ var MODEL_ALIASES = {
|
|
|
1482
1483
|
"anthropic/claude-opus-4.6": "anthropic/claude-opus-4-6",
|
|
1483
1484
|
"anthropic/claude-opus-4.5": "anthropic/claude-opus-4-5",
|
|
1484
1485
|
"anthropic/claude-haiku-4.5": "anthropic/claude-haiku-4-5",
|
|
1486
|
+
// Base model names without version -> route to latest
|
|
1487
|
+
"anthropic/claude-sonnet-4": "anthropic/claude-sonnet-4-6",
|
|
1488
|
+
"anthropic/claude-opus-4": "anthropic/claude-opus-4-6",
|
|
1489
|
+
"anthropic/claude-haiku-4": "anthropic/claude-haiku-4-5",
|
|
1485
1490
|
// OpenAI
|
|
1486
1491
|
gpt: "openai/gpt-4o",
|
|
1487
1492
|
gpt4: "openai/gpt-4o",
|
|
@@ -3789,6 +3794,83 @@ var PROVIDER_ERROR_PATTERNS = [
|
|
|
3789
3794
|
/request.*size.*exceeds/i,
|
|
3790
3795
|
/payload too large/i
|
|
3791
3796
|
];
|
|
3797
|
+
var DEGRADED_RESPONSE_PATTERNS = [
|
|
3798
|
+
/the ai service is temporarily overloaded/i,
|
|
3799
|
+
/service is temporarily overloaded/i,
|
|
3800
|
+
/please try again in a moment/i
|
|
3801
|
+
];
|
|
3802
|
+
var DEGRADED_LOOP_PATTERNS = [
|
|
3803
|
+
/the boxed is the response\./i,
|
|
3804
|
+
/the response is the text\./i,
|
|
3805
|
+
/the final answer is the boxed\./i
|
|
3806
|
+
];
|
|
3807
|
+
function extractAssistantContent(payload) {
|
|
3808
|
+
if (!payload || typeof payload !== "object") return void 0;
|
|
3809
|
+
const record = payload;
|
|
3810
|
+
const choices = record.choices;
|
|
3811
|
+
if (!Array.isArray(choices) || choices.length === 0) return void 0;
|
|
3812
|
+
const firstChoice = choices[0];
|
|
3813
|
+
if (!firstChoice || typeof firstChoice !== "object") return void 0;
|
|
3814
|
+
const choice = firstChoice;
|
|
3815
|
+
const message = choice.message;
|
|
3816
|
+
if (!message || typeof message !== "object") return void 0;
|
|
3817
|
+
const content = message.content;
|
|
3818
|
+
return typeof content === "string" ? content : void 0;
|
|
3819
|
+
}
|
|
3820
|
+
function hasKnownLoopSignature(text) {
|
|
3821
|
+
const matchCount = DEGRADED_LOOP_PATTERNS.reduce(
|
|
3822
|
+
(count, pattern) => pattern.test(text) ? count + 1 : count,
|
|
3823
|
+
0
|
|
3824
|
+
);
|
|
3825
|
+
if (matchCount >= 2) return true;
|
|
3826
|
+
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
3827
|
+
if (lines.length < 8) return false;
|
|
3828
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3829
|
+
for (const line of lines) {
|
|
3830
|
+
counts.set(line, (counts.get(line) ?? 0) + 1);
|
|
3831
|
+
}
|
|
3832
|
+
const maxRepeat = Math.max(...counts.values());
|
|
3833
|
+
const uniqueRatio = counts.size / lines.length;
|
|
3834
|
+
return maxRepeat >= 3 && uniqueRatio <= 0.45;
|
|
3835
|
+
}
|
|
3836
|
+
function detectDegradedSuccessResponse(body) {
|
|
3837
|
+
const trimmed = body.trim();
|
|
3838
|
+
if (!trimmed) return void 0;
|
|
3839
|
+
if (DEGRADED_RESPONSE_PATTERNS.some((pattern) => pattern.test(trimmed))) {
|
|
3840
|
+
return "degraded response: overloaded placeholder";
|
|
3841
|
+
}
|
|
3842
|
+
if (hasKnownLoopSignature(trimmed)) {
|
|
3843
|
+
return "degraded response: repetitive loop output";
|
|
3844
|
+
}
|
|
3845
|
+
try {
|
|
3846
|
+
const parsed = JSON.parse(trimmed);
|
|
3847
|
+
const errorField = parsed.error;
|
|
3848
|
+
let errorText = "";
|
|
3849
|
+
if (typeof errorField === "string") {
|
|
3850
|
+
errorText = errorField;
|
|
3851
|
+
} else if (errorField && typeof errorField === "object") {
|
|
3852
|
+
const errObj = errorField;
|
|
3853
|
+
errorText = [
|
|
3854
|
+
typeof errObj.message === "string" ? errObj.message : "",
|
|
3855
|
+
typeof errObj.type === "string" ? errObj.type : "",
|
|
3856
|
+
typeof errObj.code === "string" ? errObj.code : ""
|
|
3857
|
+
].filter(Boolean).join(" ");
|
|
3858
|
+
}
|
|
3859
|
+
if (errorText && PROVIDER_ERROR_PATTERNS.some((pattern) => pattern.test(errorText))) {
|
|
3860
|
+
return `degraded response: ${errorText.slice(0, 120)}`;
|
|
3861
|
+
}
|
|
3862
|
+
const assistantContent = extractAssistantContent(parsed);
|
|
3863
|
+
if (!assistantContent) return void 0;
|
|
3864
|
+
if (DEGRADED_RESPONSE_PATTERNS.some((pattern) => pattern.test(assistantContent))) {
|
|
3865
|
+
return "degraded response: overloaded assistant content";
|
|
3866
|
+
}
|
|
3867
|
+
if (hasKnownLoopSignature(assistantContent)) {
|
|
3868
|
+
return "degraded response: repetitive assistant loop";
|
|
3869
|
+
}
|
|
3870
|
+
} catch {
|
|
3871
|
+
}
|
|
3872
|
+
return void 0;
|
|
3873
|
+
}
|
|
3792
3874
|
var FALLBACK_STATUS_CODES = [
|
|
3793
3875
|
400,
|
|
3794
3876
|
// Bad request - sometimes used for billing errors
|
|
@@ -4328,6 +4410,22 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
4328
4410
|
isProviderError: isProviderErr
|
|
4329
4411
|
};
|
|
4330
4412
|
}
|
|
4413
|
+
const contentType = response.headers.get("content-type") || "";
|
|
4414
|
+
if (contentType.includes("json") || contentType.includes("text")) {
|
|
4415
|
+
try {
|
|
4416
|
+
const responseBody = await response.clone().text();
|
|
4417
|
+
const degradedReason = detectDegradedSuccessResponse(responseBody);
|
|
4418
|
+
if (degradedReason) {
|
|
4419
|
+
return {
|
|
4420
|
+
success: false,
|
|
4421
|
+
errorBody: degradedReason,
|
|
4422
|
+
errorStatus: 503,
|
|
4423
|
+
isProviderError: true
|
|
4424
|
+
};
|
|
4425
|
+
}
|
|
4426
|
+
} catch {
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4331
4429
|
return { success: true, response };
|
|
4332
4430
|
} catch (err) {
|
|
4333
4431
|
const errorMsg = err instanceof Error ? err.message : String(err);
|