@blockrun/clawrouter 0.8.10 → 0.8.12
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 +57 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +71 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1791,6 +1791,12 @@ async function getStats(days = 7) {
|
|
|
1791
1791
|
}
|
|
1792
1792
|
const totalSavings = totalBaselineCost - totalCost;
|
|
1793
1793
|
const savingsPercentage = totalBaselineCost > 0 ? totalSavings / totalBaselineCost * 100 : 0;
|
|
1794
|
+
let entriesWithBaseline = 0;
|
|
1795
|
+
for (const day of dailyBreakdown) {
|
|
1796
|
+
if (day.totalBaselineCost !== day.totalCost) {
|
|
1797
|
+
entriesWithBaseline += day.totalRequests;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1794
1800
|
return {
|
|
1795
1801
|
period: days === 1 ? "today" : `last ${days} days`,
|
|
1796
1802
|
totalRequests,
|
|
@@ -1802,8 +1808,10 @@ async function getStats(days = 7) {
|
|
|
1802
1808
|
avgCostPerRequest: totalRequests > 0 ? totalCost / totalRequests : 0,
|
|
1803
1809
|
byTier: byTierWithPercentage,
|
|
1804
1810
|
byModel: byModelWithPercentage,
|
|
1805
|
-
dailyBreakdown: dailyBreakdown.reverse()
|
|
1811
|
+
dailyBreakdown: dailyBreakdown.reverse(),
|
|
1806
1812
|
// Oldest first for charts
|
|
1813
|
+
entriesWithBaseline
|
|
1814
|
+
// How many entries have valid baseline tracking
|
|
1807
1815
|
};
|
|
1808
1816
|
}
|
|
1809
1817
|
|
|
@@ -2215,6 +2223,42 @@ var HEALTH_CHECK_TIMEOUT_MS = 2e3;
|
|
|
2215
2223
|
var RATE_LIMIT_COOLDOWN_MS = 6e4;
|
|
2216
2224
|
var PORT_RETRY_ATTEMPTS = 5;
|
|
2217
2225
|
var PORT_RETRY_DELAY_MS = 1e3;
|
|
2226
|
+
function transformPaymentError(errorBody) {
|
|
2227
|
+
try {
|
|
2228
|
+
const parsed = JSON.parse(errorBody);
|
|
2229
|
+
if (parsed.error === "Payment verification failed" && parsed.details) {
|
|
2230
|
+
const match = parsed.details.match(/Verification failed:\s*(\{.*\})/s);
|
|
2231
|
+
if (match) {
|
|
2232
|
+
const innerJson = JSON.parse(match[1]);
|
|
2233
|
+
if (innerJson.invalidReason === "insufficient_funds" && innerJson.invalidMessage) {
|
|
2234
|
+
const balanceMatch = innerJson.invalidMessage.match(
|
|
2235
|
+
/insufficient balance:\s*(\d+)\s*<\s*(\d+)/i
|
|
2236
|
+
);
|
|
2237
|
+
if (balanceMatch) {
|
|
2238
|
+
const currentMicros = parseInt(balanceMatch[1], 10);
|
|
2239
|
+
const requiredMicros = parseInt(balanceMatch[2], 10);
|
|
2240
|
+
const currentUSD = (currentMicros / 1e6).toFixed(6);
|
|
2241
|
+
const requiredUSD = (requiredMicros / 1e6).toFixed(6);
|
|
2242
|
+
const wallet = innerJson.payer || "unknown";
|
|
2243
|
+
const shortWallet = wallet.length > 12 ? `${wallet.slice(0, 6)}...${wallet.slice(-4)}` : wallet;
|
|
2244
|
+
return JSON.stringify({
|
|
2245
|
+
error: {
|
|
2246
|
+
message: `Insufficient USDC balance. Current: $${currentUSD}, Required: ~$${requiredUSD}`,
|
|
2247
|
+
type: "insufficient_funds",
|
|
2248
|
+
wallet,
|
|
2249
|
+
current_balance_usd: currentUSD,
|
|
2250
|
+
required_usd: requiredUSD,
|
|
2251
|
+
help: `Fund wallet ${shortWallet} with USDC on Base, or use free model: /model free`
|
|
2252
|
+
}
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
} catch {
|
|
2259
|
+
}
|
|
2260
|
+
return errorBody;
|
|
2261
|
+
}
|
|
2218
2262
|
var rateLimitedModels = /* @__PURE__ */ new Map();
|
|
2219
2263
|
function isRateLimited(modelId) {
|
|
2220
2264
|
const hitTime = rateLimitedModels.get(modelId);
|
|
@@ -3083,10 +3127,18 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
3083
3127
|
options.onRouted?.(routingDecision);
|
|
3084
3128
|
}
|
|
3085
3129
|
if (!upstream) {
|
|
3086
|
-
const
|
|
3130
|
+
const rawErrBody = lastError?.body || "All models in fallback chain failed";
|
|
3087
3131
|
const errStatus = lastError?.status || 502;
|
|
3132
|
+
const transformedErr = transformPaymentError(rawErrBody);
|
|
3088
3133
|
if (headersSentEarly) {
|
|
3089
|
-
|
|
3134
|
+
let errPayload;
|
|
3135
|
+
try {
|
|
3136
|
+
const parsed = JSON.parse(transformedErr);
|
|
3137
|
+
errPayload = JSON.stringify(parsed);
|
|
3138
|
+
} catch {
|
|
3139
|
+
errPayload = JSON.stringify({ error: { message: rawErrBody, type: "provider_error", status: errStatus } });
|
|
3140
|
+
}
|
|
3141
|
+
const errEvent = `data: ${errPayload}
|
|
3090
3142
|
|
|
3091
3143
|
`;
|
|
3092
3144
|
safeWrite(res, errEvent);
|
|
@@ -3101,17 +3153,11 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
3101
3153
|
});
|
|
3102
3154
|
} else {
|
|
3103
3155
|
res.writeHead(errStatus, { "Content-Type": "application/json" });
|
|
3104
|
-
res.end(
|
|
3105
|
-
JSON.stringify({
|
|
3106
|
-
error: { message: errBody, type: "provider_error" }
|
|
3107
|
-
})
|
|
3108
|
-
);
|
|
3156
|
+
res.end(transformedErr);
|
|
3109
3157
|
deduplicator.complete(dedupKey, {
|
|
3110
3158
|
status: errStatus,
|
|
3111
3159
|
headers: { "content-type": "application/json" },
|
|
3112
|
-
body: Buffer.from(
|
|
3113
|
-
JSON.stringify({ error: { message: errBody, type: "provider_error" } })
|
|
3114
|
-
),
|
|
3160
|
+
body: Buffer.from(transformedErr),
|
|
3115
3161
|
completedAt: Date.now()
|
|
3116
3162
|
});
|
|
3117
3163
|
}
|