@blockrun/clawrouter 0.12.64 → 0.12.66

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
@@ -42464,7 +42464,19 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
42464
42464
  return async (input, init) => {
42465
42465
  const request = new Request(input, init);
42466
42466
  const urlPath = new URL(request.url).pathname;
42467
- const cached = !options?.skipPreAuth ? cache2.get(urlPath) : void 0;
42467
+ let requestModel = "";
42468
+ if (init?.body) {
42469
+ try {
42470
+ const bodyStr = init.body instanceof Uint8Array ? new TextDecoder().decode(init.body) : typeof init.body === "string" ? init.body : "";
42471
+ if (bodyStr) {
42472
+ const parsed = JSON.parse(bodyStr);
42473
+ requestModel = parsed.model ?? "";
42474
+ }
42475
+ } catch {
42476
+ }
42477
+ }
42478
+ const cacheKey2 = `${urlPath}:${requestModel}`;
42479
+ const cached = !options?.skipPreAuth ? cache2.get(cacheKey2) : void 0;
42468
42480
  if (cached && Date.now() - cached.cachedAt < ttlMs) {
42469
42481
  try {
42470
42482
  const payload2 = await client.createPaymentPayload(cached.paymentRequired);
@@ -42477,9 +42489,9 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
42477
42489
  if (response2.status !== 402) {
42478
42490
  return response2;
42479
42491
  }
42480
- cache2.delete(urlPath);
42492
+ cache2.delete(cacheKey2);
42481
42493
  } catch {
42482
- cache2.delete(urlPath);
42494
+ cache2.delete(cacheKey2);
42483
42495
  }
42484
42496
  }
42485
42497
  const clonedRequest = request.clone();
@@ -42502,7 +42514,7 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
42502
42514
  } catch {
42503
42515
  }
42504
42516
  paymentRequired = httpClient.getPaymentRequiredResponse(getHeader, body);
42505
- cache2.set(urlPath, { paymentRequired, cachedAt: Date.now() });
42517
+ cache2.set(cacheKey2, { paymentRequired, cachedAt: Date.now() });
42506
42518
  } catch (error) {
42507
42519
  throw new Error(
42508
42520
  `Failed to parse payment requirements: ${error instanceof Error ? error.message : "Unknown error"}`,
@@ -46862,12 +46874,7 @@ async function checkForUpdates() {
46862
46874
  import { readFileSync, writeFileSync, mkdirSync } from "fs";
46863
46875
  import { join as join7, dirname as dirname2 } from "path";
46864
46876
  import { homedir as homedir4 } from "os";
46865
- var DEFAULT_FILE_PATH = join7(
46866
- homedir4(),
46867
- ".openclaw",
46868
- "blockrun",
46869
- "exclude-models.json"
46870
- );
46877
+ var DEFAULT_FILE_PATH = join7(homedir4(), ".openclaw", "blockrun", "exclude-models.json");
46871
46878
  function loadExcludeList(filePath = DEFAULT_FILE_PATH) {
46872
46879
  try {
46873
46880
  const raw = readFileSync(filePath, "utf-8");
@@ -47272,8 +47279,7 @@ function categorizeError(status, body) {
47272
47279
  if (status === 401) return "auth_failure";
47273
47280
  if (status === 402) return "payment_error";
47274
47281
  if (status === 403) {
47275
- if (/plan.*limit|quota.*exceeded|subscription|allowance/i.test(body))
47276
- return "quota_exceeded";
47282
+ if (/plan.*limit|quota.*exceeded|subscription|allowance/i.test(body)) return "quota_exceeded";
47277
47283
  return "auth_failure";
47278
47284
  }
47279
47285
  if (status === 429) return "rate_limited";
@@ -49560,6 +49566,7 @@ data: [DONE]
49560
49566
  let upstream;
49561
49567
  let lastError;
49562
49568
  let actualModelUsed = modelId;
49569
+ const failedAttempts = [];
49563
49570
  for (let i = 0; i < modelsToTry.length; i++) {
49564
49571
  const tryModel = modelsToTry[i];
49565
49572
  const isLastAttempt = i === modelsToTry.length - 1;
@@ -49608,6 +49615,31 @@ data: [DONE]
49608
49615
  body: result.errorBody || "Unknown error",
49609
49616
  status: result.errorStatus || 500
49610
49617
  };
49618
+ failedAttempts.push({
49619
+ model: tryModel,
49620
+ reason: result.errorCategory || `HTTP ${result.errorStatus || 500}`,
49621
+ status: result.errorStatus || 500
49622
+ });
49623
+ const isPaymentErr = /payment.*verification.*failed|payment.*settlement.*failed|insufficient.*funds|transaction_simulation_failed/i.test(
49624
+ result.errorBody || ""
49625
+ );
49626
+ if (isPaymentErr && tryModel !== FREE_MODEL && !isLastAttempt) {
49627
+ failedAttempts.push({
49628
+ ...failedAttempts[failedAttempts.length - 1],
49629
+ reason: "payment_error"
49630
+ });
49631
+ const freeIdx = modelsToTry.indexOf(FREE_MODEL);
49632
+ if (freeIdx > i + 1) {
49633
+ console.log(`[ClawRouter] Payment error \u2014 skipping to free model: ${FREE_MODEL}`);
49634
+ i = freeIdx - 1;
49635
+ continue;
49636
+ }
49637
+ if (freeIdx === -1) {
49638
+ modelsToTry.push(FREE_MODEL);
49639
+ console.log(`[ClawRouter] Payment error \u2014 appending free model: ${FREE_MODEL}`);
49640
+ continue;
49641
+ }
49642
+ }
49611
49643
  if (result.isProviderError && !isLastAttempt) {
49612
49644
  const isExplicitModelError = !routingDecision;
49613
49645
  const isUnknownExplicitModel = isExplicitModelError && /unknown.*model|invalid.*model/i.test(result.errorBody || "");
@@ -49685,17 +49717,6 @@ data: [DONE]
49685
49717
  `[ClawRouter] \u{1F511} ${errorCat === "auth_failure" ? "Auth failure" : "Quota exceeded"} for ${tryModel} \u2014 check provider config`
49686
49718
  );
49687
49719
  }
49688
- const isPaymentErr = /payment.*verification.*failed|payment.*settlement.*failed|insufficient.*funds|transaction_simulation_failed/i.test(
49689
- result.errorBody || ""
49690
- );
49691
- if (isPaymentErr && tryModel !== FREE_MODEL) {
49692
- const freeIdx = modelsToTry.indexOf(FREE_MODEL);
49693
- if (freeIdx > i + 1) {
49694
- console.log(`[ClawRouter] Payment error \u2014 skipping to free model: ${FREE_MODEL}`);
49695
- i = freeIdx - 1;
49696
- continue;
49697
- }
49698
- }
49699
49720
  console.log(
49700
49721
  `[ClawRouter] Provider error from ${tryModel}, trying fallback: ${result.errorBody?.slice(0, 100)}`
49701
49722
  );
@@ -49745,7 +49766,10 @@ data: [DONE]
49745
49766
  }
49746
49767
  }
49747
49768
  if (!upstream) {
49748
- const rawErrBody = lastError?.body || "All models in fallback chain failed";
49769
+ const attemptSummary = failedAttempts.length > 0 ? failedAttempts.map((a) => `${a.model} (${a.reason})`).join(", ") : "unknown";
49770
+ const structuredMessage = failedAttempts.length > 0 ? `All ${failedAttempts.length} models failed. Tried: ${attemptSummary}` : "All models in fallback chain failed";
49771
+ console.log(`[ClawRouter] ${structuredMessage}`);
49772
+ const rawErrBody = lastError?.body || structuredMessage;
49749
49773
  const errStatus = lastError?.status || 502;
49750
49774
  const transformedErr = transformPaymentError(rawErrBody);
49751
49775
  if (headersSentEarly) {
@@ -49804,7 +49828,7 @@ data: [DONE]
49804
49828
  id: rsp.id ?? `chatcmpl-${Date.now()}`,
49805
49829
  object: "chat.completion.chunk",
49806
49830
  created: rsp.created ?? Math.floor(Date.now() / 1e3),
49807
- model: rsp.model ?? "unknown",
49831
+ model: actualModelUsed || rsp.model || "unknown",
49808
49832
  system_fingerprint: null
49809
49833
  };
49810
49834
  if (rsp.choices && Array.isArray(rsp.choices)) {
@@ -49919,6 +49943,13 @@ data: [DONE]
49919
49943
  responseChunks.push(Buffer.from(sseData));
49920
49944
  }
49921
49945
  }
49946
+ if (routingDecision) {
49947
+ const costComment = `: cost=$${routingDecision.costEstimate.toFixed(4)} savings=${(routingDecision.savings * 100).toFixed(0)}% model=${actualModelUsed} tier=${routingDecision.tier}
49948
+
49949
+ `;
49950
+ safeWrite(res, costComment);
49951
+ responseChunks.push(Buffer.from(costComment));
49952
+ }
49922
49953
  safeWrite(res, "data: [DONE]\n\n");
49923
49954
  responseChunks.push(Buffer.from("data: [DONE]\n\n"));
49924
49955
  res.end();
@@ -49947,6 +49978,10 @@ data: [DONE]
49947
49978
  responseHeaders["x-clawrouter-agentic-score"] = routingDecision.agenticScore.toFixed(2);
49948
49979
  }
49949
49980
  }
49981
+ if (routingDecision) {
49982
+ responseHeaders["x-clawrouter-cost"] = routingDecision.costEstimate.toFixed(6);
49983
+ responseHeaders["x-clawrouter-savings"] = `${(routingDecision.savings * 100).toFixed(0)}%`;
49984
+ }
49950
49985
  const bodyParts = [];
49951
49986
  if (upstream.body) {
49952
49987
  const chunks = await readBodyWithTimeout(upstream.body);
@@ -49977,6 +50012,16 @@ data: [DONE]
49977
50012
  }
49978
50013
  budgetDowngradeNotice = void 0;
49979
50014
  }
50015
+ if (actualModelUsed && responseBody.length > 0) {
50016
+ try {
50017
+ const parsed = JSON.parse(responseBody.toString());
50018
+ if (parsed.model !== void 0) {
50019
+ parsed.model = actualModelUsed;
50020
+ responseBody = Buffer.from(JSON.stringify(parsed));
50021
+ }
50022
+ } catch {
50023
+ }
50024
+ }
49980
50025
  if (budgetDowngradeHeaderMode) {
49981
50026
  responseHeaders["x-clawrouter-budget-downgrade"] = "1";
49982
50027
  responseHeaders["x-clawrouter-budget-mode"] = budgetDowngradeHeaderMode;
@@ -50802,7 +50847,9 @@ async function startProxyInBackground(api) {
50802
50847
  activeProxyHandle = proxy;
50803
50848
  const startupExclusions = loadExcludeList();
50804
50849
  if (startupExclusions.size > 0) {
50805
- api.logger.info(`Model exclusions active (${startupExclusions.size}): ${[...startupExclusions].join(", ")}`);
50850
+ api.logger.info(
50851
+ `Model exclusions active (${startupExclusions.size}): ${[...startupExclusions].join(", ")}`
50852
+ );
50806
50853
  }
50807
50854
  api.logger.info(`ClawRouter ready \u2014 smart routing enabled`);
50808
50855
  api.logger.info(`Pricing: Simple ~$0.001 | Code ~$0.01 | Complex ~$0.05 | Free: $0`);
@@ -50891,7 +50938,10 @@ Use /exclude remove <model> to unblock.`
50891
50938
  }
50892
50939
  if (subcommand === "add") {
50893
50940
  if (!modelArg) {
50894
- return { text: "Usage: /exclude add <model>\nExample: /exclude add nvidia/gpt-oss-120b", isError: true };
50941
+ return {
50942
+ text: "Usage: /exclude add <model>\nExample: /exclude add nvidia/gpt-oss-120b",
50943
+ isError: true
50944
+ };
50895
50945
  }
50896
50946
  const resolved = addExclusion(modelArg);
50897
50947
  const list = loadExcludeList();