@blockrun/clawrouter 0.12.39 → 0.12.41
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 +129 -115
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +153 -109
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1387 -0
- package/dist/index.js +151 -107
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -525,6 +525,99 @@ function getFallbackChainFiltered(tier, tierConfigs, estimatedTotalTokens, getCo
|
|
|
525
525
|
return filtered;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
+
// src/router/strategy.ts
|
|
529
|
+
var RulesStrategy = class {
|
|
530
|
+
name = "rules";
|
|
531
|
+
route(prompt, systemPrompt, maxOutputTokens, options) {
|
|
532
|
+
const { config, modelPricing } = options;
|
|
533
|
+
const fullText = `${systemPrompt ?? ""} ${prompt}`;
|
|
534
|
+
const estimatedTokens = Math.ceil(fullText.length / 4);
|
|
535
|
+
const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);
|
|
536
|
+
const { routingProfile } = options;
|
|
537
|
+
let tierConfigs;
|
|
538
|
+
let profileSuffix;
|
|
539
|
+
let profile;
|
|
540
|
+
if (routingProfile === "eco" && config.ecoTiers) {
|
|
541
|
+
tierConfigs = config.ecoTiers;
|
|
542
|
+
profileSuffix = " | eco";
|
|
543
|
+
profile = "eco";
|
|
544
|
+
} else if (routingProfile === "premium" && config.premiumTiers) {
|
|
545
|
+
tierConfigs = config.premiumTiers;
|
|
546
|
+
profileSuffix = " | premium";
|
|
547
|
+
profile = "premium";
|
|
548
|
+
} else {
|
|
549
|
+
const agenticScore = ruleResult.agenticScore ?? 0;
|
|
550
|
+
const isAutoAgentic = agenticScore >= 0.5;
|
|
551
|
+
const isExplicitAgentic = config.overrides.agenticMode ?? false;
|
|
552
|
+
const hasToolsInRequest = options.hasTools ?? false;
|
|
553
|
+
const useAgenticTiers = (hasToolsInRequest || isAutoAgentic || isExplicitAgentic) && config.agenticTiers != null;
|
|
554
|
+
tierConfigs = useAgenticTiers ? config.agenticTiers : config.tiers;
|
|
555
|
+
profileSuffix = useAgenticTiers ? ` | agentic${hasToolsInRequest ? " (tools)" : ""}` : "";
|
|
556
|
+
profile = useAgenticTiers ? "agentic" : "auto";
|
|
557
|
+
}
|
|
558
|
+
const agenticScoreValue = ruleResult.agenticScore;
|
|
559
|
+
if (estimatedTokens > config.overrides.maxTokensForceComplex) {
|
|
560
|
+
const decision2 = selectModel(
|
|
561
|
+
"COMPLEX",
|
|
562
|
+
0.95,
|
|
563
|
+
"rules",
|
|
564
|
+
`Input exceeds ${config.overrides.maxTokensForceComplex} tokens${profileSuffix}`,
|
|
565
|
+
tierConfigs,
|
|
566
|
+
modelPricing,
|
|
567
|
+
estimatedTokens,
|
|
568
|
+
maxOutputTokens,
|
|
569
|
+
routingProfile,
|
|
570
|
+
agenticScoreValue
|
|
571
|
+
);
|
|
572
|
+
return { ...decision2, tierConfigs, profile };
|
|
573
|
+
}
|
|
574
|
+
const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;
|
|
575
|
+
let tier;
|
|
576
|
+
let confidence;
|
|
577
|
+
const method = "rules";
|
|
578
|
+
let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(", ")}`;
|
|
579
|
+
if (ruleResult.tier !== null) {
|
|
580
|
+
tier = ruleResult.tier;
|
|
581
|
+
confidence = ruleResult.confidence;
|
|
582
|
+
} else {
|
|
583
|
+
tier = config.overrides.ambiguousDefaultTier;
|
|
584
|
+
confidence = 0.5;
|
|
585
|
+
reasoning += ` | ambiguous -> default: ${tier}`;
|
|
586
|
+
}
|
|
587
|
+
if (hasStructuredOutput) {
|
|
588
|
+
const tierRank = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };
|
|
589
|
+
const minTier = config.overrides.structuredOutputMinTier;
|
|
590
|
+
if (tierRank[tier] < tierRank[minTier]) {
|
|
591
|
+
reasoning += ` | upgraded to ${minTier} (structured output)`;
|
|
592
|
+
tier = minTier;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
reasoning += profileSuffix;
|
|
596
|
+
const decision = selectModel(
|
|
597
|
+
tier,
|
|
598
|
+
confidence,
|
|
599
|
+
method,
|
|
600
|
+
reasoning,
|
|
601
|
+
tierConfigs,
|
|
602
|
+
modelPricing,
|
|
603
|
+
estimatedTokens,
|
|
604
|
+
maxOutputTokens,
|
|
605
|
+
routingProfile,
|
|
606
|
+
agenticScoreValue
|
|
607
|
+
);
|
|
608
|
+
return { ...decision, tierConfigs, profile };
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
var registry = /* @__PURE__ */ new Map();
|
|
612
|
+
registry.set("rules", new RulesStrategy());
|
|
613
|
+
function getStrategy(name) {
|
|
614
|
+
const strategy = registry.get(name);
|
|
615
|
+
if (!strategy) {
|
|
616
|
+
throw new Error(`Unknown routing strategy: ${name}`);
|
|
617
|
+
}
|
|
618
|
+
return strategy;
|
|
619
|
+
}
|
|
620
|
+
|
|
528
621
|
// src/router/config.ts
|
|
529
622
|
var DEFAULT_ROUTING_CONFIG = {
|
|
530
623
|
version: "2.0",
|
|
@@ -1617,7 +1710,11 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1617
1710
|
SIMPLE: {
|
|
1618
1711
|
primary: "nvidia/gpt-oss-120b",
|
|
1619
1712
|
// FREE! $0.00/$0.00
|
|
1620
|
-
fallback: [
|
|
1713
|
+
fallback: [
|
|
1714
|
+
"google/gemini-2.5-flash-lite",
|
|
1715
|
+
"google/gemini-2.5-flash",
|
|
1716
|
+
"deepseek/deepseek-chat"
|
|
1717
|
+
]
|
|
1621
1718
|
},
|
|
1622
1719
|
MEDIUM: {
|
|
1623
1720
|
primary: "google/gemini-2.5-flash-lite",
|
|
@@ -1650,8 +1747,8 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1650
1747
|
]
|
|
1651
1748
|
},
|
|
1652
1749
|
MEDIUM: {
|
|
1653
|
-
primary: "openai/gpt-5.
|
|
1654
|
-
// $
|
|
1750
|
+
primary: "openai/gpt-5.3-codex",
|
|
1751
|
+
// $1.75/$14 - 400K context, 128K output, replaces 5.2
|
|
1655
1752
|
fallback: [
|
|
1656
1753
|
"moonshot/kimi-k2.5",
|
|
1657
1754
|
"google/gemini-2.5-flash",
|
|
@@ -1667,7 +1764,7 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1667
1764
|
fallback: [
|
|
1668
1765
|
"openai/gpt-5.4",
|
|
1669
1766
|
// Newest flagship
|
|
1670
|
-
"openai/gpt-5.
|
|
1767
|
+
"openai/gpt-5.3-codex",
|
|
1671
1768
|
"anthropic/claude-opus-4.6",
|
|
1672
1769
|
"anthropic/claude-sonnet-4.6",
|
|
1673
1770
|
"google/gemini-3.1-pro",
|
|
@@ -1742,77 +1839,8 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1742
1839
|
|
|
1743
1840
|
// src/router/index.ts
|
|
1744
1841
|
function route(prompt, systemPrompt, maxOutputTokens, options) {
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1747
|
-
const estimatedTokens = Math.ceil(fullText.length / 4);
|
|
1748
|
-
const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);
|
|
1749
|
-
const { routingProfile } = options;
|
|
1750
|
-
let tierConfigs;
|
|
1751
|
-
let profileSuffix;
|
|
1752
|
-
if (routingProfile === "eco" && config.ecoTiers) {
|
|
1753
|
-
tierConfigs = config.ecoTiers;
|
|
1754
|
-
profileSuffix = " | eco";
|
|
1755
|
-
} else if (routingProfile === "premium" && config.premiumTiers) {
|
|
1756
|
-
tierConfigs = config.premiumTiers;
|
|
1757
|
-
profileSuffix = " | premium";
|
|
1758
|
-
} else {
|
|
1759
|
-
const agenticScore = ruleResult.agenticScore ?? 0;
|
|
1760
|
-
const isAutoAgentic = agenticScore >= 0.5;
|
|
1761
|
-
const isExplicitAgentic = config.overrides.agenticMode ?? false;
|
|
1762
|
-
const hasToolsInRequest = options.hasTools ?? false;
|
|
1763
|
-
const useAgenticTiers = (hasToolsInRequest || isAutoAgentic || isExplicitAgentic) && config.agenticTiers != null;
|
|
1764
|
-
tierConfigs = useAgenticTiers ? config.agenticTiers : config.tiers;
|
|
1765
|
-
profileSuffix = useAgenticTiers ? ` | agentic${hasToolsInRequest ? " (tools)" : ""}` : "";
|
|
1766
|
-
}
|
|
1767
|
-
const agenticScoreValue = ruleResult.agenticScore;
|
|
1768
|
-
if (estimatedTokens > config.overrides.maxTokensForceComplex) {
|
|
1769
|
-
return selectModel(
|
|
1770
|
-
"COMPLEX",
|
|
1771
|
-
0.95,
|
|
1772
|
-
"rules",
|
|
1773
|
-
`Input exceeds ${config.overrides.maxTokensForceComplex} tokens${profileSuffix}`,
|
|
1774
|
-
tierConfigs,
|
|
1775
|
-
modelPricing,
|
|
1776
|
-
estimatedTokens,
|
|
1777
|
-
maxOutputTokens,
|
|
1778
|
-
routingProfile,
|
|
1779
|
-
agenticScoreValue
|
|
1780
|
-
);
|
|
1781
|
-
}
|
|
1782
|
-
const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;
|
|
1783
|
-
let tier;
|
|
1784
|
-
let confidence;
|
|
1785
|
-
const method = "rules";
|
|
1786
|
-
let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(", ")}`;
|
|
1787
|
-
if (ruleResult.tier !== null) {
|
|
1788
|
-
tier = ruleResult.tier;
|
|
1789
|
-
confidence = ruleResult.confidence;
|
|
1790
|
-
} else {
|
|
1791
|
-
tier = config.overrides.ambiguousDefaultTier;
|
|
1792
|
-
confidence = 0.5;
|
|
1793
|
-
reasoning += ` | ambiguous -> default: ${tier}`;
|
|
1794
|
-
}
|
|
1795
|
-
if (hasStructuredOutput) {
|
|
1796
|
-
const tierRank = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };
|
|
1797
|
-
const minTier = config.overrides.structuredOutputMinTier;
|
|
1798
|
-
if (tierRank[tier] < tierRank[minTier]) {
|
|
1799
|
-
reasoning += ` | upgraded to ${minTier} (structured output)`;
|
|
1800
|
-
tier = minTier;
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
reasoning += profileSuffix;
|
|
1804
|
-
return selectModel(
|
|
1805
|
-
tier,
|
|
1806
|
-
confidence,
|
|
1807
|
-
method,
|
|
1808
|
-
reasoning,
|
|
1809
|
-
tierConfigs,
|
|
1810
|
-
modelPricing,
|
|
1811
|
-
estimatedTokens,
|
|
1812
|
-
maxOutputTokens,
|
|
1813
|
-
routingProfile,
|
|
1814
|
-
agenticScoreValue
|
|
1815
|
-
);
|
|
1842
|
+
const strategy = getStrategy("rules");
|
|
1843
|
+
return strategy.route(prompt, systemPrompt, maxOutputTokens, options);
|
|
1816
1844
|
}
|
|
1817
1845
|
|
|
1818
1846
|
// src/models.ts
|
|
@@ -1847,7 +1875,7 @@ var MODEL_ALIASES = {
|
|
|
1847
1875
|
gpt5: "openai/gpt-5.4",
|
|
1848
1876
|
"gpt-5.4": "openai/gpt-5.4",
|
|
1849
1877
|
"gpt-5.4-pro": "openai/gpt-5.4-pro",
|
|
1850
|
-
codex: "openai/gpt-5.
|
|
1878
|
+
codex: "openai/gpt-5.3-codex",
|
|
1851
1879
|
mini: "openai/gpt-4o-mini",
|
|
1852
1880
|
o1: "openai/o1",
|
|
1853
1881
|
o3: "openai/o3",
|
|
@@ -2003,15 +2031,29 @@ var BLOCKRUN_MODELS = [
|
|
|
2003
2031
|
reasoning: true,
|
|
2004
2032
|
toolCalling: true
|
|
2005
2033
|
},
|
|
2006
|
-
// OpenAI
|
|
2034
|
+
// OpenAI GPT-5.3 Family
|
|
2007
2035
|
{
|
|
2008
|
-
id: "openai/gpt-5.
|
|
2009
|
-
name: "GPT-5.
|
|
2010
|
-
version: "5.
|
|
2036
|
+
id: "openai/gpt-5.3",
|
|
2037
|
+
name: "GPT-5.3",
|
|
2038
|
+
version: "5.3",
|
|
2011
2039
|
inputPrice: 1.75,
|
|
2012
2040
|
outputPrice: 14,
|
|
2013
2041
|
contextWindow: 128e3,
|
|
2014
|
-
maxOutput:
|
|
2042
|
+
maxOutput: 16e3,
|
|
2043
|
+
reasoning: true,
|
|
2044
|
+
vision: true,
|
|
2045
|
+
agentic: true,
|
|
2046
|
+
toolCalling: true
|
|
2047
|
+
},
|
|
2048
|
+
// OpenAI Codex Family
|
|
2049
|
+
{
|
|
2050
|
+
id: "openai/gpt-5.3-codex",
|
|
2051
|
+
name: "GPT-5.3 Codex",
|
|
2052
|
+
version: "5.3",
|
|
2053
|
+
inputPrice: 1.75,
|
|
2054
|
+
outputPrice: 14,
|
|
2055
|
+
contextWindow: 4e5,
|
|
2056
|
+
maxOutput: 128e3,
|
|
2015
2057
|
agentic: true,
|
|
2016
2058
|
toolCalling: true
|
|
2017
2059
|
},
|
|
@@ -5026,6 +5068,12 @@ var ROUTING_PROFILES = /* @__PURE__ */ new Set([
|
|
|
5026
5068
|
"premium"
|
|
5027
5069
|
]);
|
|
5028
5070
|
var FREE_MODEL = "nvidia/gpt-oss-120b";
|
|
5071
|
+
var FREE_TIER_CONFIGS = {
|
|
5072
|
+
SIMPLE: { primary: FREE_MODEL, fallback: [] },
|
|
5073
|
+
MEDIUM: { primary: FREE_MODEL, fallback: [] },
|
|
5074
|
+
COMPLEX: { primary: FREE_MODEL, fallback: [] },
|
|
5075
|
+
REASONING: { primary: FREE_MODEL, fallback: [] }
|
|
5076
|
+
};
|
|
5029
5077
|
var freeRequestCount = 0;
|
|
5030
5078
|
var MAX_MESSAGES = 200;
|
|
5031
5079
|
var CONTEXT_LIMIT_KB = 5120;
|
|
@@ -6900,7 +6948,17 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6900
6948
|
|
|
6901
6949
|
`;
|
|
6902
6950
|
}
|
|
6903
|
-
routingDecision = {
|
|
6951
|
+
routingDecision = {
|
|
6952
|
+
model: freeModel,
|
|
6953
|
+
tier: "SIMPLE",
|
|
6954
|
+
confidence: 1,
|
|
6955
|
+
method: "rules",
|
|
6956
|
+
reasoning: "free profile",
|
|
6957
|
+
costEstimate: 0,
|
|
6958
|
+
baselineCost: 0,
|
|
6959
|
+
savings: 1,
|
|
6960
|
+
tierConfigs: FREE_TIER_CONFIGS
|
|
6961
|
+
};
|
|
6904
6962
|
} else {
|
|
6905
6963
|
effectiveSessionId = getSessionId(req.headers) ?? deriveSessionId(parsedMessages);
|
|
6906
6964
|
const existingSession = effectiveSessionId ? sessionStore.getSession(effectiveSessionId) : void 0;
|
|
@@ -6991,18 +7049,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
6991
7049
|
const contentHash = hashRequestContent(prompt, toolCallNames);
|
|
6992
7050
|
const shouldEscalate = sessionStore.recordRequestHash(effectiveSessionId, contentHash);
|
|
6993
7051
|
if (shouldEscalate) {
|
|
6994
|
-
const activeTierConfigs =
|
|
6995
|
-
if (routingDecision.reasoning?.includes("agentic") && routerOpts.config.agenticTiers) {
|
|
6996
|
-
return routerOpts.config.agenticTiers;
|
|
6997
|
-
}
|
|
6998
|
-
if (routingProfile === "eco" && routerOpts.config.ecoTiers) {
|
|
6999
|
-
return routerOpts.config.ecoTiers;
|
|
7000
|
-
}
|
|
7001
|
-
if (routingProfile === "premium" && routerOpts.config.premiumTiers) {
|
|
7002
|
-
return routerOpts.config.premiumTiers;
|
|
7003
|
-
}
|
|
7004
|
-
return routerOpts.config.tiers;
|
|
7005
|
-
})();
|
|
7052
|
+
const activeTierConfigs = routingDecision.tierConfigs ?? routerOpts.config.tiers;
|
|
7006
7053
|
const escalation = sessionStore.escalateSession(
|
|
7007
7054
|
effectiveSessionId,
|
|
7008
7055
|
activeTierConfigs
|
|
@@ -7218,18 +7265,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7218
7265
|
if (routingDecision) {
|
|
7219
7266
|
const estimatedInputTokens = Math.ceil(body.length / 4);
|
|
7220
7267
|
const estimatedTotalTokens = estimatedInputTokens + maxTokens;
|
|
7221
|
-
const tierConfigs =
|
|
7222
|
-
if (routingDecision.reasoning?.includes("agentic") && routerOpts.config.agenticTiers) {
|
|
7223
|
-
return routerOpts.config.agenticTiers;
|
|
7224
|
-
}
|
|
7225
|
-
if (routingProfile === "eco" && routerOpts.config.ecoTiers) {
|
|
7226
|
-
return routerOpts.config.ecoTiers;
|
|
7227
|
-
}
|
|
7228
|
-
if (routingProfile === "premium" && routerOpts.config.premiumTiers) {
|
|
7229
|
-
return routerOpts.config.premiumTiers;
|
|
7230
|
-
}
|
|
7231
|
-
return routerOpts.config.tiers;
|
|
7232
|
-
})();
|
|
7268
|
+
const tierConfigs = routingDecision.tierConfigs ?? routerOpts.config.tiers;
|
|
7233
7269
|
const fullChain = getFallbackChain(routingDecision.tier, tierConfigs);
|
|
7234
7270
|
const contextFiltered = getFallbackChainFiltered(
|
|
7235
7271
|
routingDecision.tier,
|
|
@@ -7309,6 +7345,14 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
7309
7345
|
status: result.errorStatus || 500
|
|
7310
7346
|
};
|
|
7311
7347
|
if (result.isProviderError && !isLastAttempt) {
|
|
7348
|
+
const isExplicitModelError = !routingDecision;
|
|
7349
|
+
const isUnknownExplicitModel = isExplicitModelError && /unknown.*model|invalid.*model/i.test(result.errorBody || "");
|
|
7350
|
+
if (isUnknownExplicitModel) {
|
|
7351
|
+
console.log(
|
|
7352
|
+
`[ClawRouter] Explicit model error from ${tryModel}, not falling back: ${result.errorBody?.slice(0, 100)}`
|
|
7353
|
+
);
|
|
7354
|
+
break;
|
|
7355
|
+
}
|
|
7312
7356
|
if (result.errorStatus === 429) {
|
|
7313
7357
|
markRateLimited(tryModel);
|
|
7314
7358
|
try {
|
|
@@ -8084,8 +8128,8 @@ Commands:
|
|
|
8084
8128
|
partners List available partner APIs with pricing
|
|
8085
8129
|
partners test Test partner API endpoints (expect 402 = alive)
|
|
8086
8130
|
wallet recover Restore wallet.key from mnemonic (if generated by ClawRouter)
|
|
8087
|
-
chain solana Switch
|
|
8088
|
-
chain base Switch
|
|
8131
|
+
chain solana Switch to Solana (persists). Aliases: /wallet solana, wallet solana
|
|
8132
|
+
chain base Switch to Base EVM (persists). Aliases: /wallet base, wallet base
|
|
8089
8133
|
|
|
8090
8134
|
Examples:
|
|
8091
8135
|
# Start standalone proxy
|
|
@@ -8155,7 +8199,7 @@ function parseArgs(args) {
|
|
|
8155
8199
|
} else if (arg === "wallet" && args[i + 1] === "recover") {
|
|
8156
8200
|
result.walletRecover = true;
|
|
8157
8201
|
i++;
|
|
8158
|
-
} else if (arg === "chain" && (args[i + 1] === "solana" || args[i + 1] === "base")) {
|
|
8202
|
+
} else if ((arg === "chain" || arg === "/wallet" || arg === "wallet") && (args[i + 1] === "solana" || args[i + 1] === "base")) {
|
|
8159
8203
|
result.chain = args[i + 1];
|
|
8160
8204
|
i++;
|
|
8161
8205
|
} else if (arg === "--port" && args[i + 1]) {
|