@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/dist/index.js CHANGED
@@ -990,7 +990,7 @@ var MODEL_ALIASES = {
990
990
  gpt5: "openai/gpt-5.4",
991
991
  "gpt-5.4": "openai/gpt-5.4",
992
992
  "gpt-5.4-pro": "openai/gpt-5.4-pro",
993
- codex: "openai/gpt-5.2-codex",
993
+ codex: "openai/gpt-5.3-codex",
994
994
  mini: "openai/gpt-4o-mini",
995
995
  o1: "openai/o1",
996
996
  o3: "openai/o3",
@@ -1146,15 +1146,29 @@ var BLOCKRUN_MODELS = [
1146
1146
  reasoning: true,
1147
1147
  toolCalling: true
1148
1148
  },
1149
- // OpenAI Codex Family
1149
+ // OpenAI GPT-5.3 Family
1150
1150
  {
1151
- id: "openai/gpt-5.2-codex",
1152
- name: "GPT-5.2 Codex",
1153
- version: "5.2",
1151
+ id: "openai/gpt-5.3",
1152
+ name: "GPT-5.3",
1153
+ version: "5.3",
1154
1154
  inputPrice: 1.75,
1155
1155
  outputPrice: 14,
1156
1156
  contextWindow: 128e3,
1157
- maxOutput: 32e3,
1157
+ maxOutput: 16e3,
1158
+ reasoning: true,
1159
+ vision: true,
1160
+ agentic: true,
1161
+ toolCalling: true
1162
+ },
1163
+ // OpenAI Codex Family
1164
+ {
1165
+ id: "openai/gpt-5.3-codex",
1166
+ name: "GPT-5.3 Codex",
1167
+ version: "5.3",
1168
+ inputPrice: 1.75,
1169
+ outputPrice: 14,
1170
+ contextWindow: 4e5,
1171
+ maxOutput: 128e3,
1158
1172
  agentic: true,
1159
1173
  toolCalling: true
1160
1174
  },
@@ -2022,6 +2036,99 @@ function getFallbackChainFiltered(tier, tierConfigs, estimatedTotalTokens, getCo
2022
2036
  return filtered;
2023
2037
  }
2024
2038
 
2039
+ // src/router/strategy.ts
2040
+ var RulesStrategy = class {
2041
+ name = "rules";
2042
+ route(prompt, systemPrompt, maxOutputTokens, options) {
2043
+ const { config, modelPricing } = options;
2044
+ const fullText = `${systemPrompt ?? ""} ${prompt}`;
2045
+ const estimatedTokens = Math.ceil(fullText.length / 4);
2046
+ const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);
2047
+ const { routingProfile } = options;
2048
+ let tierConfigs;
2049
+ let profileSuffix;
2050
+ let profile;
2051
+ if (routingProfile === "eco" && config.ecoTiers) {
2052
+ tierConfigs = config.ecoTiers;
2053
+ profileSuffix = " | eco";
2054
+ profile = "eco";
2055
+ } else if (routingProfile === "premium" && config.premiumTiers) {
2056
+ tierConfigs = config.premiumTiers;
2057
+ profileSuffix = " | premium";
2058
+ profile = "premium";
2059
+ } else {
2060
+ const agenticScore = ruleResult.agenticScore ?? 0;
2061
+ const isAutoAgentic = agenticScore >= 0.5;
2062
+ const isExplicitAgentic = config.overrides.agenticMode ?? false;
2063
+ const hasToolsInRequest = options.hasTools ?? false;
2064
+ const useAgenticTiers = (hasToolsInRequest || isAutoAgentic || isExplicitAgentic) && config.agenticTiers != null;
2065
+ tierConfigs = useAgenticTiers ? config.agenticTiers : config.tiers;
2066
+ profileSuffix = useAgenticTiers ? ` | agentic${hasToolsInRequest ? " (tools)" : ""}` : "";
2067
+ profile = useAgenticTiers ? "agentic" : "auto";
2068
+ }
2069
+ const agenticScoreValue = ruleResult.agenticScore;
2070
+ if (estimatedTokens > config.overrides.maxTokensForceComplex) {
2071
+ const decision2 = selectModel(
2072
+ "COMPLEX",
2073
+ 0.95,
2074
+ "rules",
2075
+ `Input exceeds ${config.overrides.maxTokensForceComplex} tokens${profileSuffix}`,
2076
+ tierConfigs,
2077
+ modelPricing,
2078
+ estimatedTokens,
2079
+ maxOutputTokens,
2080
+ routingProfile,
2081
+ agenticScoreValue
2082
+ );
2083
+ return { ...decision2, tierConfigs, profile };
2084
+ }
2085
+ const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;
2086
+ let tier;
2087
+ let confidence;
2088
+ const method = "rules";
2089
+ let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(", ")}`;
2090
+ if (ruleResult.tier !== null) {
2091
+ tier = ruleResult.tier;
2092
+ confidence = ruleResult.confidence;
2093
+ } else {
2094
+ tier = config.overrides.ambiguousDefaultTier;
2095
+ confidence = 0.5;
2096
+ reasoning += ` | ambiguous -> default: ${tier}`;
2097
+ }
2098
+ if (hasStructuredOutput) {
2099
+ const tierRank = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };
2100
+ const minTier = config.overrides.structuredOutputMinTier;
2101
+ if (tierRank[tier] < tierRank[minTier]) {
2102
+ reasoning += ` | upgraded to ${minTier} (structured output)`;
2103
+ tier = minTier;
2104
+ }
2105
+ }
2106
+ reasoning += profileSuffix;
2107
+ const decision = selectModel(
2108
+ tier,
2109
+ confidence,
2110
+ method,
2111
+ reasoning,
2112
+ tierConfigs,
2113
+ modelPricing,
2114
+ estimatedTokens,
2115
+ maxOutputTokens,
2116
+ routingProfile,
2117
+ agenticScoreValue
2118
+ );
2119
+ return { ...decision, tierConfigs, profile };
2120
+ }
2121
+ };
2122
+ var registry = /* @__PURE__ */ new Map();
2123
+ registry.set("rules", new RulesStrategy());
2124
+ function getStrategy(name) {
2125
+ const strategy = registry.get(name);
2126
+ if (!strategy) {
2127
+ throw new Error(`Unknown routing strategy: ${name}`);
2128
+ }
2129
+ return strategy;
2130
+ }
2131
+
2025
2132
  // src/router/config.ts
2026
2133
  var DEFAULT_ROUTING_CONFIG = {
2027
2134
  version: "2.0",
@@ -3114,7 +3221,11 @@ var DEFAULT_ROUTING_CONFIG = {
3114
3221
  SIMPLE: {
3115
3222
  primary: "nvidia/gpt-oss-120b",
3116
3223
  // FREE! $0.00/$0.00
3117
- fallback: ["google/gemini-2.5-flash-lite", "google/gemini-2.5-flash", "deepseek/deepseek-chat"]
3224
+ fallback: [
3225
+ "google/gemini-2.5-flash-lite",
3226
+ "google/gemini-2.5-flash",
3227
+ "deepseek/deepseek-chat"
3228
+ ]
3118
3229
  },
3119
3230
  MEDIUM: {
3120
3231
  primary: "google/gemini-2.5-flash-lite",
@@ -3147,8 +3258,8 @@ var DEFAULT_ROUTING_CONFIG = {
3147
3258
  ]
3148
3259
  },
3149
3260
  MEDIUM: {
3150
- primary: "openai/gpt-5.2-codex",
3151
- // $2.50/$10 - strong coding for medium tasks
3261
+ primary: "openai/gpt-5.3-codex",
3262
+ // $1.75/$14 - 400K context, 128K output, replaces 5.2
3152
3263
  fallback: [
3153
3264
  "moonshot/kimi-k2.5",
3154
3265
  "google/gemini-2.5-flash",
@@ -3164,7 +3275,7 @@ var DEFAULT_ROUTING_CONFIG = {
3164
3275
  fallback: [
3165
3276
  "openai/gpt-5.4",
3166
3277
  // Newest flagship
3167
- "openai/gpt-5.2-codex",
3278
+ "openai/gpt-5.3-codex",
3168
3279
  "anthropic/claude-opus-4.6",
3169
3280
  "anthropic/claude-sonnet-4.6",
3170
3281
  "google/gemini-3.1-pro",
@@ -3239,77 +3350,8 @@ var DEFAULT_ROUTING_CONFIG = {
3239
3350
 
3240
3351
  // src/router/index.ts
3241
3352
  function route(prompt, systemPrompt, maxOutputTokens, options) {
3242
- const { config, modelPricing } = options;
3243
- const fullText = `${systemPrompt ?? ""} ${prompt}`;
3244
- const estimatedTokens = Math.ceil(fullText.length / 4);
3245
- const ruleResult = classifyByRules(prompt, systemPrompt, estimatedTokens, config.scoring);
3246
- const { routingProfile } = options;
3247
- let tierConfigs;
3248
- let profileSuffix;
3249
- if (routingProfile === "eco" && config.ecoTiers) {
3250
- tierConfigs = config.ecoTiers;
3251
- profileSuffix = " | eco";
3252
- } else if (routingProfile === "premium" && config.premiumTiers) {
3253
- tierConfigs = config.premiumTiers;
3254
- profileSuffix = " | premium";
3255
- } else {
3256
- const agenticScore = ruleResult.agenticScore ?? 0;
3257
- const isAutoAgentic = agenticScore >= 0.5;
3258
- const isExplicitAgentic = config.overrides.agenticMode ?? false;
3259
- const hasToolsInRequest = options.hasTools ?? false;
3260
- const useAgenticTiers = (hasToolsInRequest || isAutoAgentic || isExplicitAgentic) && config.agenticTiers != null;
3261
- tierConfigs = useAgenticTiers ? config.agenticTiers : config.tiers;
3262
- profileSuffix = useAgenticTiers ? ` | agentic${hasToolsInRequest ? " (tools)" : ""}` : "";
3263
- }
3264
- const agenticScoreValue = ruleResult.agenticScore;
3265
- if (estimatedTokens > config.overrides.maxTokensForceComplex) {
3266
- return selectModel(
3267
- "COMPLEX",
3268
- 0.95,
3269
- "rules",
3270
- `Input exceeds ${config.overrides.maxTokensForceComplex} tokens${profileSuffix}`,
3271
- tierConfigs,
3272
- modelPricing,
3273
- estimatedTokens,
3274
- maxOutputTokens,
3275
- routingProfile,
3276
- agenticScoreValue
3277
- );
3278
- }
3279
- const hasStructuredOutput = systemPrompt ? /json|structured|schema/i.test(systemPrompt) : false;
3280
- let tier;
3281
- let confidence;
3282
- const method = "rules";
3283
- let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(", ")}`;
3284
- if (ruleResult.tier !== null) {
3285
- tier = ruleResult.tier;
3286
- confidence = ruleResult.confidence;
3287
- } else {
3288
- tier = config.overrides.ambiguousDefaultTier;
3289
- confidence = 0.5;
3290
- reasoning += ` | ambiguous -> default: ${tier}`;
3291
- }
3292
- if (hasStructuredOutput) {
3293
- const tierRank = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };
3294
- const minTier = config.overrides.structuredOutputMinTier;
3295
- if (tierRank[tier] < tierRank[minTier]) {
3296
- reasoning += ` | upgraded to ${minTier} (structured output)`;
3297
- tier = minTier;
3298
- }
3299
- }
3300
- reasoning += profileSuffix;
3301
- return selectModel(
3302
- tier,
3303
- confidence,
3304
- method,
3305
- reasoning,
3306
- tierConfigs,
3307
- modelPricing,
3308
- estimatedTokens,
3309
- maxOutputTokens,
3310
- routingProfile,
3311
- agenticScoreValue
3312
- );
3353
+ const strategy = getStrategy("rules");
3354
+ return strategy.route(prompt, systemPrompt, maxOutputTokens, options);
3313
3355
  }
3314
3356
 
3315
3357
  // src/logger.ts
@@ -5454,6 +5496,12 @@ var ROUTING_PROFILES = /* @__PURE__ */ new Set([
5454
5496
  "premium"
5455
5497
  ]);
5456
5498
  var FREE_MODEL = "nvidia/gpt-oss-120b";
5499
+ var FREE_TIER_CONFIGS = {
5500
+ SIMPLE: { primary: FREE_MODEL, fallback: [] },
5501
+ MEDIUM: { primary: FREE_MODEL, fallback: [] },
5502
+ COMPLEX: { primary: FREE_MODEL, fallback: [] },
5503
+ REASONING: { primary: FREE_MODEL, fallback: [] }
5504
+ };
5457
5505
  var freeRequestCount = 0;
5458
5506
  var MAX_MESSAGES = 200;
5459
5507
  var CONTEXT_LIMIT_KB = 5120;
@@ -7328,7 +7376,17 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
7328
7376
 
7329
7377
  `;
7330
7378
  }
7331
- routingDecision = { model: freeModel, tier: "SIMPLE", confidence: 1, method: "rules", reasoning: "free profile" };
7379
+ routingDecision = {
7380
+ model: freeModel,
7381
+ tier: "SIMPLE",
7382
+ confidence: 1,
7383
+ method: "rules",
7384
+ reasoning: "free profile",
7385
+ costEstimate: 0,
7386
+ baselineCost: 0,
7387
+ savings: 1,
7388
+ tierConfigs: FREE_TIER_CONFIGS
7389
+ };
7332
7390
  } else {
7333
7391
  effectiveSessionId = getSessionId(req.headers) ?? deriveSessionId(parsedMessages);
7334
7392
  const existingSession = effectiveSessionId ? sessionStore.getSession(effectiveSessionId) : void 0;
@@ -7419,18 +7477,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
7419
7477
  const contentHash = hashRequestContent(prompt, toolCallNames);
7420
7478
  const shouldEscalate = sessionStore.recordRequestHash(effectiveSessionId, contentHash);
7421
7479
  if (shouldEscalate) {
7422
- const activeTierConfigs = (() => {
7423
- if (routingDecision.reasoning?.includes("agentic") && routerOpts.config.agenticTiers) {
7424
- return routerOpts.config.agenticTiers;
7425
- }
7426
- if (routingProfile === "eco" && routerOpts.config.ecoTiers) {
7427
- return routerOpts.config.ecoTiers;
7428
- }
7429
- if (routingProfile === "premium" && routerOpts.config.premiumTiers) {
7430
- return routerOpts.config.premiumTiers;
7431
- }
7432
- return routerOpts.config.tiers;
7433
- })();
7480
+ const activeTierConfigs = routingDecision.tierConfigs ?? routerOpts.config.tiers;
7434
7481
  const escalation = sessionStore.escalateSession(
7435
7482
  effectiveSessionId,
7436
7483
  activeTierConfigs
@@ -7646,18 +7693,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
7646
7693
  if (routingDecision) {
7647
7694
  const estimatedInputTokens = Math.ceil(body.length / 4);
7648
7695
  const estimatedTotalTokens = estimatedInputTokens + maxTokens;
7649
- const tierConfigs = (() => {
7650
- if (routingDecision.reasoning?.includes("agentic") && routerOpts.config.agenticTiers) {
7651
- return routerOpts.config.agenticTiers;
7652
- }
7653
- if (routingProfile === "eco" && routerOpts.config.ecoTiers) {
7654
- return routerOpts.config.ecoTiers;
7655
- }
7656
- if (routingProfile === "premium" && routerOpts.config.premiumTiers) {
7657
- return routerOpts.config.premiumTiers;
7658
- }
7659
- return routerOpts.config.tiers;
7660
- })();
7696
+ const tierConfigs = routingDecision.tierConfigs ?? routerOpts.config.tiers;
7661
7697
  const fullChain = getFallbackChain(routingDecision.tier, tierConfigs);
7662
7698
  const contextFiltered = getFallbackChainFiltered(
7663
7699
  routingDecision.tier,
@@ -7737,6 +7773,14 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
7737
7773
  status: result.errorStatus || 500
7738
7774
  };
7739
7775
  if (result.isProviderError && !isLastAttempt) {
7776
+ const isExplicitModelError = !routingDecision;
7777
+ const isUnknownExplicitModel = isExplicitModelError && /unknown.*model|invalid.*model/i.test(result.errorBody || "");
7778
+ if (isUnknownExplicitModel) {
7779
+ console.log(
7780
+ `[ClawRouter] Explicit model error from ${tryModel}, not falling back: ${result.errorBody?.slice(0, 100)}`
7781
+ );
7782
+ break;
7783
+ }
7740
7784
  if (result.errorStatus === 429) {
7741
7785
  markRateLimited(tryModel);
7742
7786
  try {
@@ -8666,7 +8710,7 @@ function injectModelsConfig(logger) {
8666
8710
  "anthropic/claude-sonnet-4.6",
8667
8711
  "anthropic/claude-opus-4.6",
8668
8712
  "anthropic/claude-haiku-4.5",
8669
- "openai/gpt-5.2",
8713
+ "openai/gpt-5.3",
8670
8714
  "openai/gpt-4o",
8671
8715
  "openai/o3",
8672
8716
  "google/gemini-3.1-pro",