@blockrun/clawrouter 0.2.3 → 0.3.0
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.d.ts +104 -3
- package/dist/index.js +297 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -249,8 +249,14 @@ declare function route(prompt: string, systemPrompt: string | undefined, maxOutp
|
|
|
249
249
|
* → gets 402 → @x402/fetch signs payment → retries
|
|
250
250
|
* → streams response back to pi-ai
|
|
251
251
|
*
|
|
252
|
-
*
|
|
253
|
-
* -
|
|
252
|
+
* Optimizations (v0.3.0):
|
|
253
|
+
* - SSE heartbeat: for streaming requests, sends headers + heartbeat immediately
|
|
254
|
+
* before the x402 flow, preventing OpenClaw's 10-15s timeout from firing.
|
|
255
|
+
* - Response dedup: hashes request bodies and caches responses for 30s,
|
|
256
|
+
* preventing double-charging when OpenClaw retries after timeout.
|
|
257
|
+
* - Payment cache: after first 402, pre-signs subsequent requests to skip
|
|
258
|
+
* the 402 round trip (~200ms savings per request).
|
|
259
|
+
* - Smart routing: when model is "blockrun/auto", classify query and pick cheapest model.
|
|
254
260
|
* - Usage logging: log every request as JSON line to ~/.openclaw/blockrun/logs/
|
|
255
261
|
*/
|
|
256
262
|
|
|
@@ -345,6 +351,101 @@ type UsageEntry = {
|
|
|
345
351
|
*/
|
|
346
352
|
declare function logUsage(entry: UsageEntry): Promise<void>;
|
|
347
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Request Deduplication
|
|
356
|
+
*
|
|
357
|
+
* Prevents double-charging when OpenClaw retries a request after timeout.
|
|
358
|
+
* Tracks in-flight requests and caches completed responses for a short TTL.
|
|
359
|
+
*/
|
|
360
|
+
type CachedResponse = {
|
|
361
|
+
status: number;
|
|
362
|
+
headers: Record<string, string>;
|
|
363
|
+
body: Buffer;
|
|
364
|
+
completedAt: number;
|
|
365
|
+
};
|
|
366
|
+
declare class RequestDeduplicator {
|
|
367
|
+
private inflight;
|
|
368
|
+
private completed;
|
|
369
|
+
private ttlMs;
|
|
370
|
+
constructor(ttlMs?: number);
|
|
371
|
+
/** Hash request body to create a dedup key. */
|
|
372
|
+
static hash(body: Buffer): string;
|
|
373
|
+
/** Check if a response is cached for this key. */
|
|
374
|
+
getCached(key: string): CachedResponse | undefined;
|
|
375
|
+
/** Check if a request with this key is currently in-flight. Returns a promise to wait on. */
|
|
376
|
+
getInflight(key: string): Promise<CachedResponse> | undefined;
|
|
377
|
+
/** Mark a request as in-flight. */
|
|
378
|
+
markInflight(key: string): void;
|
|
379
|
+
/** Complete an in-flight request — cache result and notify waiters. */
|
|
380
|
+
complete(key: string, result: CachedResponse): void;
|
|
381
|
+
/** Remove an in-flight entry on error (don't cache failures). */
|
|
382
|
+
removeInflight(key: string): void;
|
|
383
|
+
/** Prune expired completed entries. */
|
|
384
|
+
private prune;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Payment Parameter Cache
|
|
389
|
+
*
|
|
390
|
+
* Caches the 402 payment parameters (payTo, asset, network, etc.) after the first
|
|
391
|
+
* request to each endpoint. On subsequent requests, pre-signs the payment and
|
|
392
|
+
* attaches it to the first request, skipping the 402 round trip (~200ms savings).
|
|
393
|
+
*/
|
|
394
|
+
type CachedPaymentParams = {
|
|
395
|
+
payTo: string;
|
|
396
|
+
asset: string;
|
|
397
|
+
scheme: string;
|
|
398
|
+
network: string;
|
|
399
|
+
extra?: {
|
|
400
|
+
name?: string;
|
|
401
|
+
version?: string;
|
|
402
|
+
};
|
|
403
|
+
maxTimeoutSeconds?: number;
|
|
404
|
+
cachedAt: number;
|
|
405
|
+
};
|
|
406
|
+
declare class PaymentCache {
|
|
407
|
+
private cache;
|
|
408
|
+
private ttlMs;
|
|
409
|
+
constructor(ttlMs?: number);
|
|
410
|
+
/** Get cached payment params for an endpoint path. */
|
|
411
|
+
get(endpointPath: string): CachedPaymentParams | undefined;
|
|
412
|
+
/** Cache payment params from a 402 response. */
|
|
413
|
+
set(endpointPath: string, params: Omit<CachedPaymentParams, "cachedAt">): void;
|
|
414
|
+
/** Invalidate cache for an endpoint (e.g., if payTo changed). */
|
|
415
|
+
invalidate(endpointPath: string): void;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* x402 Payment Implementation
|
|
420
|
+
*
|
|
421
|
+
* Based on BlockRun's proven implementation.
|
|
422
|
+
* Handles 402 Payment Required responses with EIP-712 signed USDC transfers.
|
|
423
|
+
*
|
|
424
|
+
* Optimizations (v0.3.0):
|
|
425
|
+
* - Payment cache: after first 402, caches {payTo, asset, network} per endpoint.
|
|
426
|
+
* On subsequent requests, pre-signs payment and sends with first request,
|
|
427
|
+
* skipping the 402 round trip (~200ms savings).
|
|
428
|
+
* - Falls back to normal 402 flow if pre-signed payment is rejected.
|
|
429
|
+
*/
|
|
430
|
+
|
|
431
|
+
/** Pre-auth parameters for skipping the 402 round trip. */
|
|
432
|
+
type PreAuthParams = {
|
|
433
|
+
estimatedAmount: string;
|
|
434
|
+
};
|
|
435
|
+
/** Result from createPaymentFetch — includes the fetch wrapper and payment cache. */
|
|
436
|
+
type PaymentFetchResult = {
|
|
437
|
+
fetch: (input: RequestInfo | URL, init?: RequestInit, preAuth?: PreAuthParams) => Promise<Response>;
|
|
438
|
+
cache: PaymentCache;
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
* Create a fetch wrapper that handles x402 payment automatically.
|
|
442
|
+
*
|
|
443
|
+
* Supports pre-auth: if cached payment params + estimated amount are available,
|
|
444
|
+
* pre-signs and attaches payment to the first request, skipping the 402 round trip.
|
|
445
|
+
* Falls back to normal 402 flow if pre-signed payment is rejected.
|
|
446
|
+
*/
|
|
447
|
+
declare function createPaymentFetch(privateKey: `0x${string}`): PaymentFetchResult;
|
|
448
|
+
|
|
348
449
|
/**
|
|
349
450
|
* @blockrun/clawrouter
|
|
350
451
|
*
|
|
@@ -366,4 +467,4 @@ declare function logUsage(entry: UsageEntry): Promise<void>;
|
|
|
366
467
|
|
|
367
468
|
declare const plugin: OpenClawPluginDefinition;
|
|
368
469
|
|
|
369
|
-
export { BLOCKRUN_MODELS, DEFAULT_ROUTING_CONFIG, OPENCLAW_MODELS, type RoutingConfig, type RoutingDecision, type Tier, type UsageEntry, blockrunProvider, buildProviderModels, plugin as default, logUsage, route, startProxy };
|
|
470
|
+
export { BLOCKRUN_MODELS, type CachedPaymentParams, type CachedResponse, DEFAULT_ROUTING_CONFIG, OPENCLAW_MODELS, PaymentCache, type PaymentFetchResult, type PreAuthParams, RequestDeduplicator, type RoutingConfig, type RoutingDecision, type Tier, type UsageEntry, blockrunProvider, buildProviderModels, createPaymentFetch, plugin as default, logUsage, route, startProxy };
|
package/dist/index.js
CHANGED
|
@@ -191,6 +191,36 @@ import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
|
191
191
|
|
|
192
192
|
// src/x402.ts
|
|
193
193
|
import { signTypedData, privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
194
|
+
|
|
195
|
+
// src/payment-cache.ts
|
|
196
|
+
var DEFAULT_TTL_MS = 36e5;
|
|
197
|
+
var PaymentCache = class {
|
|
198
|
+
cache = /* @__PURE__ */ new Map();
|
|
199
|
+
ttlMs;
|
|
200
|
+
constructor(ttlMs = DEFAULT_TTL_MS) {
|
|
201
|
+
this.ttlMs = ttlMs;
|
|
202
|
+
}
|
|
203
|
+
/** Get cached payment params for an endpoint path. */
|
|
204
|
+
get(endpointPath) {
|
|
205
|
+
const entry = this.cache.get(endpointPath);
|
|
206
|
+
if (!entry) return void 0;
|
|
207
|
+
if (Date.now() - entry.cachedAt > this.ttlMs) {
|
|
208
|
+
this.cache.delete(endpointPath);
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
return entry;
|
|
212
|
+
}
|
|
213
|
+
/** Cache payment params from a 402 response. */
|
|
214
|
+
set(endpointPath, params) {
|
|
215
|
+
this.cache.set(endpointPath, { ...params, cachedAt: Date.now() });
|
|
216
|
+
}
|
|
217
|
+
/** Invalidate cache for an endpoint (e.g., if payTo changed). */
|
|
218
|
+
invalidate(endpointPath) {
|
|
219
|
+
this.cache.delete(endpointPath);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// src/x402.ts
|
|
194
224
|
var BASE_CHAIN_ID = 8453;
|
|
195
225
|
var USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
196
226
|
var USDC_DOMAIN = {
|
|
@@ -271,8 +301,31 @@ async function createPaymentPayload(privateKey, fromAddress, recipient, amount,
|
|
|
271
301
|
function createPaymentFetch(privateKey) {
|
|
272
302
|
const account = privateKeyToAccount2(privateKey);
|
|
273
303
|
const walletAddress = account.address;
|
|
274
|
-
|
|
304
|
+
const paymentCache = new PaymentCache();
|
|
305
|
+
const payFetch = async (input, init, preAuth) => {
|
|
275
306
|
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
307
|
+
const endpointPath = new URL(url).pathname;
|
|
308
|
+
const cached = paymentCache.get(endpointPath);
|
|
309
|
+
if (cached && preAuth?.estimatedAmount) {
|
|
310
|
+
const paymentPayload = await createPaymentPayload(
|
|
311
|
+
privateKey,
|
|
312
|
+
walletAddress,
|
|
313
|
+
cached.payTo,
|
|
314
|
+
preAuth.estimatedAmount,
|
|
315
|
+
url
|
|
316
|
+
);
|
|
317
|
+
const preAuthHeaders = new Headers(init?.headers);
|
|
318
|
+
preAuthHeaders.set("payment-signature", paymentPayload);
|
|
319
|
+
const response2 = await fetch(input, { ...init, headers: preAuthHeaders });
|
|
320
|
+
if (response2.status !== 402) {
|
|
321
|
+
return response2;
|
|
322
|
+
}
|
|
323
|
+
const paymentHeader2 = response2.headers.get("x-payment-required");
|
|
324
|
+
if (paymentHeader2) {
|
|
325
|
+
return handle402(input, init, url, endpointPath, paymentHeader2);
|
|
326
|
+
}
|
|
327
|
+
return response2;
|
|
328
|
+
}
|
|
276
329
|
const response = await fetch(input, init);
|
|
277
330
|
if (response.status !== 402) {
|
|
278
331
|
return response;
|
|
@@ -281,6 +334,9 @@ function createPaymentFetch(privateKey) {
|
|
|
281
334
|
if (!paymentHeader) {
|
|
282
335
|
throw new Error("402 response missing x-payment-required header");
|
|
283
336
|
}
|
|
337
|
+
return handle402(input, init, url, endpointPath, paymentHeader);
|
|
338
|
+
};
|
|
339
|
+
async function handle402(input, init, url, endpointPath, paymentHeader) {
|
|
284
340
|
const paymentRequired = parsePaymentRequired(paymentHeader);
|
|
285
341
|
const option = paymentRequired.accepts?.[0];
|
|
286
342
|
if (!option) {
|
|
@@ -290,6 +346,14 @@ function createPaymentFetch(privateKey) {
|
|
|
290
346
|
if (!amount) {
|
|
291
347
|
throw new Error("No amount in payment requirements");
|
|
292
348
|
}
|
|
349
|
+
paymentCache.set(endpointPath, {
|
|
350
|
+
payTo: option.payTo,
|
|
351
|
+
asset: option.asset,
|
|
352
|
+
scheme: option.scheme,
|
|
353
|
+
network: option.network,
|
|
354
|
+
extra: option.extra,
|
|
355
|
+
maxTimeoutSeconds: option.maxTimeoutSeconds
|
|
356
|
+
});
|
|
293
357
|
const paymentPayload = await createPaymentPayload(
|
|
294
358
|
privateKey,
|
|
295
359
|
walletAddress,
|
|
@@ -303,7 +367,8 @@ function createPaymentFetch(privateKey) {
|
|
|
303
367
|
...init,
|
|
304
368
|
headers: retryHeaders
|
|
305
369
|
});
|
|
306
|
-
}
|
|
370
|
+
}
|
|
371
|
+
return { fetch: payFetch, cache: paymentCache };
|
|
307
372
|
}
|
|
308
373
|
|
|
309
374
|
// src/router/rules.ts
|
|
@@ -872,10 +937,87 @@ async function logUsage(entry) {
|
|
|
872
937
|
}
|
|
873
938
|
}
|
|
874
939
|
|
|
940
|
+
// src/dedup.ts
|
|
941
|
+
import { createHash } from "crypto";
|
|
942
|
+
var DEFAULT_TTL_MS2 = 3e4;
|
|
943
|
+
var MAX_BODY_SIZE = 1048576;
|
|
944
|
+
var RequestDeduplicator = class {
|
|
945
|
+
inflight = /* @__PURE__ */ new Map();
|
|
946
|
+
completed = /* @__PURE__ */ new Map();
|
|
947
|
+
ttlMs;
|
|
948
|
+
constructor(ttlMs = DEFAULT_TTL_MS2) {
|
|
949
|
+
this.ttlMs = ttlMs;
|
|
950
|
+
}
|
|
951
|
+
/** Hash request body to create a dedup key. */
|
|
952
|
+
static hash(body) {
|
|
953
|
+
return createHash("sha256").update(body).digest("hex").slice(0, 16);
|
|
954
|
+
}
|
|
955
|
+
/** Check if a response is cached for this key. */
|
|
956
|
+
getCached(key) {
|
|
957
|
+
const entry = this.completed.get(key);
|
|
958
|
+
if (!entry) return void 0;
|
|
959
|
+
if (Date.now() - entry.completedAt > this.ttlMs) {
|
|
960
|
+
this.completed.delete(key);
|
|
961
|
+
return void 0;
|
|
962
|
+
}
|
|
963
|
+
return entry;
|
|
964
|
+
}
|
|
965
|
+
/** Check if a request with this key is currently in-flight. Returns a promise to wait on. */
|
|
966
|
+
getInflight(key) {
|
|
967
|
+
const entry = this.inflight.get(key);
|
|
968
|
+
if (!entry) return void 0;
|
|
969
|
+
const promise = new Promise((resolve) => {
|
|
970
|
+
entry.waiters.push(new Promise((r) => {
|
|
971
|
+
const orig = entry.resolve;
|
|
972
|
+
entry.resolve = (result) => {
|
|
973
|
+
orig(result);
|
|
974
|
+
resolve(result);
|
|
975
|
+
r(result);
|
|
976
|
+
};
|
|
977
|
+
}));
|
|
978
|
+
});
|
|
979
|
+
return promise;
|
|
980
|
+
}
|
|
981
|
+
/** Mark a request as in-flight. */
|
|
982
|
+
markInflight(key) {
|
|
983
|
+
this.inflight.set(key, {
|
|
984
|
+
resolve: () => {
|
|
985
|
+
},
|
|
986
|
+
waiters: []
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
/** Complete an in-flight request — cache result and notify waiters. */
|
|
990
|
+
complete(key, result) {
|
|
991
|
+
if (result.body.length <= MAX_BODY_SIZE) {
|
|
992
|
+
this.completed.set(key, result);
|
|
993
|
+
}
|
|
994
|
+
const entry = this.inflight.get(key);
|
|
995
|
+
if (entry) {
|
|
996
|
+
entry.resolve(result);
|
|
997
|
+
this.inflight.delete(key);
|
|
998
|
+
}
|
|
999
|
+
this.prune();
|
|
1000
|
+
}
|
|
1001
|
+
/** Remove an in-flight entry on error (don't cache failures). */
|
|
1002
|
+
removeInflight(key) {
|
|
1003
|
+
this.inflight.delete(key);
|
|
1004
|
+
}
|
|
1005
|
+
/** Prune expired completed entries. */
|
|
1006
|
+
prune() {
|
|
1007
|
+
const now = Date.now();
|
|
1008
|
+
for (const [key, entry] of this.completed) {
|
|
1009
|
+
if (now - entry.completedAt > this.ttlMs) {
|
|
1010
|
+
this.completed.delete(key);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
|
|
875
1016
|
// src/proxy.ts
|
|
876
1017
|
var BLOCKRUN_API = "https://blockrun.ai/api";
|
|
877
1018
|
var AUTO_MODEL = "blockrun/auto";
|
|
878
|
-
var USER_AGENT = "clawrouter/0.
|
|
1019
|
+
var USER_AGENT = "clawrouter/0.3.0";
|
|
1020
|
+
var HEARTBEAT_INTERVAL_MS = 2e3;
|
|
879
1021
|
function buildModelPricing() {
|
|
880
1022
|
const map = /* @__PURE__ */ new Map();
|
|
881
1023
|
for (const m of BLOCKRUN_MODELS) {
|
|
@@ -895,18 +1037,29 @@ function mergeRoutingConfig(overrides) {
|
|
|
895
1037
|
overrides: { ...DEFAULT_ROUTING_CONFIG.overrides, ...overrides.overrides }
|
|
896
1038
|
};
|
|
897
1039
|
}
|
|
1040
|
+
function estimateAmount(modelId, bodyLength, maxTokens) {
|
|
1041
|
+
const model = BLOCKRUN_MODELS.find((m) => m.id === modelId);
|
|
1042
|
+
if (!model) return void 0;
|
|
1043
|
+
const estimatedInputTokens = Math.ceil(bodyLength / 4);
|
|
1044
|
+
const estimatedOutputTokens = maxTokens || model.maxOutput || 4096;
|
|
1045
|
+
const costUsd = estimatedInputTokens / 1e6 * model.inputPrice + estimatedOutputTokens / 1e6 * model.outputPrice;
|
|
1046
|
+
const amountMicros = Math.ceil(costUsd * 1.2 * 1e6);
|
|
1047
|
+
return amountMicros.toString();
|
|
1048
|
+
}
|
|
898
1049
|
async function startProxy(options) {
|
|
899
1050
|
const apiBase = options.apiBase ?? BLOCKRUN_API;
|
|
900
1051
|
const account = privateKeyToAccount3(options.walletKey);
|
|
901
|
-
const payFetch = createPaymentFetch(options.walletKey);
|
|
1052
|
+
const { fetch: payFetch, cache: paymentCache } = createPaymentFetch(options.walletKey);
|
|
902
1053
|
const routingConfig = mergeRoutingConfig(options.routingConfig);
|
|
903
1054
|
const modelPricing = buildModelPricing();
|
|
904
1055
|
const routerOpts = {
|
|
905
1056
|
config: routingConfig,
|
|
906
1057
|
modelPricing,
|
|
907
|
-
payFetch,
|
|
1058
|
+
payFetch: (input, init) => payFetch(input, init),
|
|
1059
|
+
// router doesn't need preAuth
|
|
908
1060
|
apiBase
|
|
909
1061
|
};
|
|
1062
|
+
const deduplicator = new RequestDeduplicator();
|
|
910
1063
|
const server = createServer(async (req, res) => {
|
|
911
1064
|
if (req.url === "/health") {
|
|
912
1065
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -919,7 +1072,7 @@ async function startProxy(options) {
|
|
|
919
1072
|
return;
|
|
920
1073
|
}
|
|
921
1074
|
try {
|
|
922
|
-
await proxyRequest(req, res, apiBase, payFetch, options, routerOpts);
|
|
1075
|
+
await proxyRequest(req, res, apiBase, payFetch, options, routerOpts, deduplicator);
|
|
923
1076
|
} catch (err) {
|
|
924
1077
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
925
1078
|
options.onError?.(error);
|
|
@@ -928,6 +1081,12 @@ async function startProxy(options) {
|
|
|
928
1081
|
res.end(JSON.stringify({
|
|
929
1082
|
error: { message: `Proxy error: ${error.message}`, type: "proxy_error" }
|
|
930
1083
|
}));
|
|
1084
|
+
} else if (!res.writableEnded) {
|
|
1085
|
+
res.write(`data: ${JSON.stringify({ error: { message: error.message, type: "proxy_error" } })}
|
|
1086
|
+
|
|
1087
|
+
`);
|
|
1088
|
+
res.write("data: [DONE]\n\n");
|
|
1089
|
+
res.end();
|
|
931
1090
|
}
|
|
932
1091
|
}
|
|
933
1092
|
});
|
|
@@ -949,7 +1108,7 @@ async function startProxy(options) {
|
|
|
949
1108
|
});
|
|
950
1109
|
});
|
|
951
1110
|
}
|
|
952
|
-
async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts) {
|
|
1111
|
+
async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, deduplicator) {
|
|
953
1112
|
const startTime = Date.now();
|
|
954
1113
|
const upstreamUrl = `${apiBase}${req.url}`;
|
|
955
1114
|
const bodyChunks = [];
|
|
@@ -958,10 +1117,16 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts) {
|
|
|
958
1117
|
}
|
|
959
1118
|
let body = Buffer.concat(bodyChunks);
|
|
960
1119
|
let routingDecision;
|
|
1120
|
+
let isStreaming = false;
|
|
1121
|
+
let modelId = "";
|
|
1122
|
+
let maxTokens = 4096;
|
|
961
1123
|
const isChatCompletion = req.url?.includes("/chat/completions");
|
|
962
1124
|
if (isChatCompletion && body.length > 0) {
|
|
963
1125
|
try {
|
|
964
1126
|
const parsed = JSON.parse(body.toString());
|
|
1127
|
+
isStreaming = parsed.stream === true;
|
|
1128
|
+
modelId = parsed.model || "";
|
|
1129
|
+
maxTokens = parsed.max_tokens || 4096;
|
|
965
1130
|
if (parsed.model === AUTO_MODEL) {
|
|
966
1131
|
const messages = parsed.messages;
|
|
967
1132
|
let lastUserMsg;
|
|
@@ -976,15 +1141,46 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts) {
|
|
|
976
1141
|
const systemMsg = messages?.find((m) => m.role === "system");
|
|
977
1142
|
const prompt = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : "";
|
|
978
1143
|
const systemPrompt = typeof systemMsg?.content === "string" ? systemMsg.content : void 0;
|
|
979
|
-
const maxTokens = parsed.max_tokens || 4096;
|
|
980
1144
|
routingDecision = await route(prompt, systemPrompt, maxTokens, routerOpts);
|
|
981
1145
|
parsed.model = routingDecision.model;
|
|
1146
|
+
modelId = routingDecision.model;
|
|
982
1147
|
body = Buffer.from(JSON.stringify(parsed));
|
|
983
1148
|
options.onRouted?.(routingDecision);
|
|
984
1149
|
}
|
|
985
1150
|
} catch {
|
|
986
1151
|
}
|
|
987
1152
|
}
|
|
1153
|
+
const dedupKey = RequestDeduplicator.hash(body);
|
|
1154
|
+
const cached = deduplicator.getCached(dedupKey);
|
|
1155
|
+
if (cached) {
|
|
1156
|
+
res.writeHead(cached.status, cached.headers);
|
|
1157
|
+
res.end(cached.body);
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
const inflight = deduplicator.getInflight(dedupKey);
|
|
1161
|
+
if (inflight) {
|
|
1162
|
+
const result = await inflight;
|
|
1163
|
+
res.writeHead(result.status, result.headers);
|
|
1164
|
+
res.end(result.body);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
deduplicator.markInflight(dedupKey);
|
|
1168
|
+
let heartbeatInterval;
|
|
1169
|
+
let headersSentEarly = false;
|
|
1170
|
+
if (isStreaming) {
|
|
1171
|
+
res.writeHead(200, {
|
|
1172
|
+
"content-type": "text/event-stream",
|
|
1173
|
+
"cache-control": "no-cache",
|
|
1174
|
+
"connection": "keep-alive"
|
|
1175
|
+
});
|
|
1176
|
+
headersSentEarly = true;
|
|
1177
|
+
res.write(": heartbeat\n\n");
|
|
1178
|
+
heartbeatInterval = setInterval(() => {
|
|
1179
|
+
if (!res.writableEnded) {
|
|
1180
|
+
res.write(": heartbeat\n\n");
|
|
1181
|
+
}
|
|
1182
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
1183
|
+
}
|
|
988
1184
|
const headers = {};
|
|
989
1185
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
990
1186
|
if (key === "host" || key === "connection" || key === "transfer-encoding" || key === "content-length") continue;
|
|
@@ -996,30 +1192,98 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts) {
|
|
|
996
1192
|
headers["content-type"] = "application/json";
|
|
997
1193
|
}
|
|
998
1194
|
headers["user-agent"] = USER_AGENT;
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1195
|
+
let preAuth;
|
|
1196
|
+
if (modelId) {
|
|
1197
|
+
const estimated = estimateAmount(modelId, body.length, maxTokens);
|
|
1198
|
+
if (estimated) {
|
|
1199
|
+
preAuth = { estimatedAmount: estimated };
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
try {
|
|
1203
|
+
const upstream = await payFetch(upstreamUrl, {
|
|
1204
|
+
method: req.method ?? "POST",
|
|
1205
|
+
headers,
|
|
1206
|
+
body: body.length > 0 ? body : void 0
|
|
1207
|
+
}, preAuth);
|
|
1208
|
+
if (heartbeatInterval) {
|
|
1209
|
+
clearInterval(heartbeatInterval);
|
|
1210
|
+
heartbeatInterval = void 0;
|
|
1211
|
+
}
|
|
1212
|
+
const responseChunks = [];
|
|
1213
|
+
if (headersSentEarly) {
|
|
1214
|
+
if (upstream.status !== 200) {
|
|
1215
|
+
const errBody = await upstream.text();
|
|
1216
|
+
const errEvent = `data: ${JSON.stringify({ error: { message: errBody, type: "upstream_error", status: upstream.status } })}
|
|
1217
|
+
|
|
1218
|
+
`;
|
|
1219
|
+
res.write(errEvent);
|
|
1220
|
+
res.write("data: [DONE]\n\n");
|
|
1221
|
+
res.end();
|
|
1222
|
+
const errBuf = Buffer.from(errEvent + "data: [DONE]\n\n");
|
|
1223
|
+
deduplicator.complete(dedupKey, {
|
|
1224
|
+
status: 200,
|
|
1225
|
+
// we already sent 200
|
|
1226
|
+
headers: { "content-type": "text/event-stream" },
|
|
1227
|
+
body: errBuf,
|
|
1228
|
+
completedAt: Date.now()
|
|
1229
|
+
});
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
if (upstream.body) {
|
|
1233
|
+
const reader = upstream.body.getReader();
|
|
1234
|
+
try {
|
|
1235
|
+
while (true) {
|
|
1236
|
+
const { done, value } = await reader.read();
|
|
1237
|
+
if (done) break;
|
|
1238
|
+
res.write(value);
|
|
1239
|
+
responseChunks.push(Buffer.from(value));
|
|
1240
|
+
}
|
|
1241
|
+
} finally {
|
|
1242
|
+
reader.releaseLock();
|
|
1243
|
+
}
|
|
1017
1244
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1245
|
+
res.end();
|
|
1246
|
+
deduplicator.complete(dedupKey, {
|
|
1247
|
+
status: 200,
|
|
1248
|
+
headers: { "content-type": "text/event-stream" },
|
|
1249
|
+
body: Buffer.concat(responseChunks),
|
|
1250
|
+
completedAt: Date.now()
|
|
1251
|
+
});
|
|
1252
|
+
} else {
|
|
1253
|
+
const responseHeaders = {};
|
|
1254
|
+
upstream.headers.forEach((value, key) => {
|
|
1255
|
+
if (key === "transfer-encoding" || key === "connection") return;
|
|
1256
|
+
responseHeaders[key] = value;
|
|
1257
|
+
});
|
|
1258
|
+
res.writeHead(upstream.status, responseHeaders);
|
|
1259
|
+
if (upstream.body) {
|
|
1260
|
+
const reader = upstream.body.getReader();
|
|
1261
|
+
try {
|
|
1262
|
+
while (true) {
|
|
1263
|
+
const { done, value } = await reader.read();
|
|
1264
|
+
if (done) break;
|
|
1265
|
+
res.write(value);
|
|
1266
|
+
responseChunks.push(Buffer.from(value));
|
|
1267
|
+
}
|
|
1268
|
+
} finally {
|
|
1269
|
+
reader.releaseLock();
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
res.end();
|
|
1273
|
+
deduplicator.complete(dedupKey, {
|
|
1274
|
+
status: upstream.status,
|
|
1275
|
+
headers: responseHeaders,
|
|
1276
|
+
body: Buffer.concat(responseChunks),
|
|
1277
|
+
completedAt: Date.now()
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
} catch (err) {
|
|
1281
|
+
if (heartbeatInterval) {
|
|
1282
|
+
clearInterval(heartbeatInterval);
|
|
1020
1283
|
}
|
|
1284
|
+
deduplicator.removeInflight(dedupKey);
|
|
1285
|
+
throw err;
|
|
1021
1286
|
}
|
|
1022
|
-
res.end();
|
|
1023
1287
|
if (routingDecision) {
|
|
1024
1288
|
const entry = {
|
|
1025
1289
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1066,7 +1330,7 @@ var plugin = {
|
|
|
1066
1330
|
id: "clawrouter",
|
|
1067
1331
|
name: "ClawRouter",
|
|
1068
1332
|
description: "Smart LLM router \u2014 30+ models, x402 micropayments, 78% cost savings",
|
|
1069
|
-
version: "0.
|
|
1333
|
+
version: "0.3.0",
|
|
1070
1334
|
register(api) {
|
|
1071
1335
|
api.registerProvider(blockrunProvider);
|
|
1072
1336
|
api.logger.info("BlockRun provider registered (30+ models via x402)");
|
|
@@ -1082,8 +1346,11 @@ export {
|
|
|
1082
1346
|
BLOCKRUN_MODELS,
|
|
1083
1347
|
DEFAULT_ROUTING_CONFIG,
|
|
1084
1348
|
OPENCLAW_MODELS,
|
|
1349
|
+
PaymentCache,
|
|
1350
|
+
RequestDeduplicator,
|
|
1085
1351
|
blockrunProvider,
|
|
1086
1352
|
buildProviderModels,
|
|
1353
|
+
createPaymentFetch,
|
|
1087
1354
|
index_default as default,
|
|
1088
1355
|
logUsage,
|
|
1089
1356
|
route,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth.ts","../src/models.ts","../src/provider.ts","../src/proxy.ts","../src/x402.ts","../src/router/rules.ts","../src/router/llm-classifier.ts","../src/router/selector.ts","../src/router/config.ts","../src/router/index.ts","../src/logger.ts","../src/index.ts"],"sourcesContent":["/**\n * BlockRun Auth Methods for OpenClaw\n *\n * Provides wallet-based authentication for the BlockRun provider.\n * Operators configure their wallet private key, which is used to\n * sign x402 micropayments for LLM inference.\n *\n * Three methods:\n * 1. Auto-generate — create a new wallet on first run, save to ~/.openclaw/blockrun/wallet.key\n * 2. Environment variable — read from BLOCKRUN_WALLET_KEY\n * 3. Manual input — operator enters private key via wizard\n */\n\nimport { writeFile, readFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { generatePrivateKey, privateKeyToAccount } from \"viem/accounts\";\nimport type { ProviderAuthMethod, ProviderAuthContext, ProviderAuthResult } from \"./types.js\";\n\nconst WALLET_DIR = join(homedir(), \".openclaw\", \"blockrun\");\nconst WALLET_FILE = join(WALLET_DIR, \"wallet.key\");\n\n/**\n * Try to load a previously auto-generated wallet key from disk.\n */\nasync function loadSavedWallet(): Promise<string | undefined> {\n try {\n const key = (await readFile(WALLET_FILE, \"utf-8\")).trim();\n if (key.startsWith(\"0x\") && key.length === 66) return key;\n } catch {\n // File doesn't exist yet\n }\n return undefined;\n}\n\n/**\n * Generate a new wallet, save to disk, return the private key.\n */\nasync function generateAndSaveWallet(): Promise<{ key: string; address: string }> {\n const key = generatePrivateKey();\n const account = privateKeyToAccount(key);\n await mkdir(WALLET_DIR, { recursive: true });\n await writeFile(WALLET_FILE, key + \"\\n\", { mode: 0o600 });\n return { key, address: account.address };\n}\n\n/**\n * Resolve wallet key: load saved → env var → auto-generate.\n * Called by index.ts before the auth wizard runs.\n */\nexport async function resolveOrGenerateWalletKey(): Promise<{ key: string; address: string; source: \"saved\" | \"env\" | \"generated\" }> {\n // 1. Previously saved wallet\n const saved = await loadSavedWallet();\n if (saved) {\n const account = privateKeyToAccount(saved as `0x${string}`);\n return { key: saved, address: account.address, source: \"saved\" };\n }\n\n // 2. Environment variable\n const envKey = process.env.BLOCKRUN_WALLET_KEY;\n if (typeof envKey === \"string\" && envKey.startsWith(\"0x\") && envKey.length === 66) {\n const account = privateKeyToAccount(envKey as `0x${string}`);\n return { key: envKey, address: account.address, source: \"env\" };\n }\n\n // 3. Auto-generate\n const { key, address } = await generateAndSaveWallet();\n return { key, address, source: \"generated\" };\n}\n\n/**\n * Auth method: operator enters their wallet private key directly.\n */\nexport const walletKeyAuth: ProviderAuthMethod = {\n id: \"wallet-key\",\n label: \"Wallet Private Key\",\n hint: \"Enter your EVM wallet private key (0x...) for x402 payments to BlockRun\",\n kind: \"api_key\",\n run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {\n const key = await ctx.prompter.text({\n message: \"Enter your wallet private key (0x...)\",\n validate: (value: string) => {\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"0x\")) return \"Key must start with 0x\";\n if (trimmed.length !== 66) return \"Key must be 66 characters (0x + 64 hex)\";\n if (!/^0x[0-9a-fA-F]{64}$/.test(trimmed)) return \"Key must be valid hex\";\n return undefined;\n },\n });\n\n if (!key || typeof key !== \"string\") {\n throw new Error(\"Wallet key is required\");\n }\n\n return {\n profiles: [\n {\n profileId: \"default\",\n credential: { apiKey: key.trim() },\n },\n ],\n notes: [\n \"Wallet key stored securely in OpenClaw credentials.\",\n \"Your wallet signs x402 USDC payments on Base for each LLM call.\",\n \"Fund your wallet with USDC on Base to start using BlockRun models.\",\n ],\n };\n },\n};\n\n/**\n * Auth method: read wallet key from BLOCKRUN_WALLET_KEY environment variable.\n */\nexport const envKeyAuth: ProviderAuthMethod = {\n id: \"env-key\",\n label: \"Environment Variable\",\n hint: \"Use BLOCKRUN_WALLET_KEY environment variable\",\n kind: \"api_key\",\n run: async (): Promise<ProviderAuthResult> => {\n const key = process.env.BLOCKRUN_WALLET_KEY;\n\n if (!key) {\n throw new Error(\n \"BLOCKRUN_WALLET_KEY environment variable is not set. \" +\n \"Set it to your EVM wallet private key (0x...).\",\n );\n }\n\n return {\n profiles: [\n {\n profileId: \"default\",\n credential: { apiKey: key.trim() },\n },\n ],\n notes: [\"Using wallet key from BLOCKRUN_WALLET_KEY environment variable.\"],\n };\n },\n};\n","/**\n * BlockRun Model Definitions for OpenClaw\n *\n * Maps BlockRun's 30+ AI models to OpenClaw's ModelDefinitionConfig format.\n * All models use the \"openai-completions\" API since BlockRun is OpenAI-compatible.\n *\n * Pricing is in USD per 1M tokens. Operators pay these rates via x402;\n * they set their own markup when reselling to end users (Phase 2).\n */\n\nimport type { ModelDefinitionConfig, ModelProviderConfig } from \"./types.js\";\n\ntype BlockRunModel = {\n id: string;\n name: string;\n inputPrice: number;\n outputPrice: number;\n contextWindow: number;\n maxOutput: number;\n reasoning?: boolean;\n vision?: boolean;\n};\n\nexport const BLOCKRUN_MODELS: BlockRunModel[] = [\n // Smart routing meta-model — proxy replaces with actual model\n { id: \"blockrun/auto\", name: \"BlockRun Smart Router\", inputPrice: 0, outputPrice: 0, contextWindow: 1_050_000, maxOutput: 128_000 },\n\n\n // OpenAI GPT-5 Family\n { id: \"openai/gpt-5.2\", name: \"GPT-5.2\", inputPrice: 1.75, outputPrice: 14.0, contextWindow: 400000, maxOutput: 128000, reasoning: true, vision: true },\n { id: \"openai/gpt-5-mini\", name: \"GPT-5 Mini\", inputPrice: 0.25, outputPrice: 2.0, contextWindow: 200000, maxOutput: 65536 },\n { id: \"openai/gpt-5-nano\", name: \"GPT-5 Nano\", inputPrice: 0.05, outputPrice: 0.4, contextWindow: 128000, maxOutput: 32768 },\n { id: \"openai/gpt-5.2-pro\", name: \"GPT-5.2 Pro\", inputPrice: 21.0, outputPrice: 168.0, contextWindow: 400000, maxOutput: 128000, reasoning: true },\n\n // OpenAI GPT-4 Family\n { id: \"openai/gpt-4.1\", name: \"GPT-4.1\", inputPrice: 2.0, outputPrice: 8.0, contextWindow: 128000, maxOutput: 16384, vision: true },\n { id: \"openai/gpt-4.1-mini\", name: \"GPT-4.1 Mini\", inputPrice: 0.4, outputPrice: 1.6, contextWindow: 128000, maxOutput: 16384 },\n { id: \"openai/gpt-4.1-nano\", name: \"GPT-4.1 Nano\", inputPrice: 0.1, outputPrice: 0.4, contextWindow: 128000, maxOutput: 16384 },\n { id: \"openai/gpt-4o\", name: \"GPT-4o\", inputPrice: 2.5, outputPrice: 10.0, contextWindow: 128000, maxOutput: 16384, vision: true },\n { id: \"openai/gpt-4o-mini\", name: \"GPT-4o Mini\", inputPrice: 0.15, outputPrice: 0.6, contextWindow: 128000, maxOutput: 16384 },\n\n // OpenAI O-series (Reasoning)\n { id: \"openai/o1\", name: \"o1\", inputPrice: 15.0, outputPrice: 60.0, contextWindow: 200000, maxOutput: 100000, reasoning: true },\n { id: \"openai/o1-mini\", name: \"o1-mini\", inputPrice: 1.1, outputPrice: 4.4, contextWindow: 128000, maxOutput: 65536, reasoning: true },\n { id: \"openai/o3\", name: \"o3\", inputPrice: 2.0, outputPrice: 8.0, contextWindow: 200000, maxOutput: 100000, reasoning: true },\n { id: \"openai/o3-mini\", name: \"o3-mini\", inputPrice: 1.1, outputPrice: 4.4, contextWindow: 128000, maxOutput: 65536, reasoning: true },\n { id: \"openai/o4-mini\", name: \"o4-mini\", inputPrice: 1.1, outputPrice: 4.4, contextWindow: 128000, maxOutput: 65536, reasoning: true },\n\n // Anthropic\n { id: \"anthropic/claude-haiku-4.5\", name: \"Claude Haiku 4.5\", inputPrice: 1.0, outputPrice: 5.0, contextWindow: 200000, maxOutput: 8192 },\n { id: \"anthropic/claude-sonnet-4\", name: \"Claude Sonnet 4\", inputPrice: 3.0, outputPrice: 15.0, contextWindow: 200000, maxOutput: 64000, reasoning: true },\n { id: \"anthropic/claude-opus-4\", name: \"Claude Opus 4\", inputPrice: 15.0, outputPrice: 75.0, contextWindow: 200000, maxOutput: 32000, reasoning: true },\n { id: \"anthropic/claude-opus-4.5\", name: \"Claude Opus 4.5\", inputPrice: 15.0, outputPrice: 75.0, contextWindow: 200000, maxOutput: 32000, reasoning: true },\n\n // Google\n { id: \"google/gemini-3-pro-preview\", name: \"Gemini 3 Pro Preview\", inputPrice: 2.0, outputPrice: 12.0, contextWindow: 1050000, maxOutput: 65536, reasoning: true, vision: true },\n { id: \"google/gemini-2.5-pro\", name: \"Gemini 2.5 Pro\", inputPrice: 1.25, outputPrice: 10.0, contextWindow: 1050000, maxOutput: 65536, reasoning: true, vision: true },\n { id: \"google/gemini-2.5-flash\", name: \"Gemini 2.5 Flash\", inputPrice: 0.15, outputPrice: 0.6, contextWindow: 1000000, maxOutput: 65536 },\n\n // DeepSeek\n { id: \"deepseek/deepseek-chat\", name: \"DeepSeek V3.2 Chat\", inputPrice: 0.28, outputPrice: 0.42, contextWindow: 128000, maxOutput: 8192 },\n { id: \"deepseek/deepseek-reasoner\", name: \"DeepSeek V3.2 Reasoner\", inputPrice: 0.28, outputPrice: 0.42, contextWindow: 128000, maxOutput: 8192, reasoning: true },\n\n // xAI / Grok\n { id: \"xai/grok-3\", name: \"Grok 3\", inputPrice: 3.0, outputPrice: 15.0, contextWindow: 131072, maxOutput: 16384, reasoning: true },\n { id: \"xai/grok-3-fast\", name: \"Grok 3 Fast\", inputPrice: 5.0, outputPrice: 25.0, contextWindow: 131072, maxOutput: 16384, reasoning: true },\n { id: \"xai/grok-3-mini\", name: \"Grok 3 Mini\", inputPrice: 0.3, outputPrice: 0.5, contextWindow: 131072, maxOutput: 16384 },\n];\n\n/**\n * Convert BlockRun model definitions to OpenClaw ModelDefinitionConfig format.\n */\nfunction toOpenClawModel(m: BlockRunModel): ModelDefinitionConfig {\n return {\n id: m.id,\n name: m.name,\n api: \"openai-completions\",\n reasoning: m.reasoning ?? false,\n input: m.vision ? [\"text\", \"image\"] : [\"text\"],\n cost: {\n input: m.inputPrice,\n output: m.outputPrice,\n cacheRead: 0,\n cacheWrite: 0,\n },\n contextWindow: m.contextWindow,\n maxTokens: m.maxOutput,\n };\n}\n\n/**\n * All BlockRun models in OpenClaw format.\n */\nexport const OPENCLAW_MODELS: ModelDefinitionConfig[] = BLOCKRUN_MODELS.map(toOpenClawModel);\n\n/**\n * Build a ModelProviderConfig for BlockRun.\n *\n * @param baseUrl - The proxy's local base URL (e.g., \"http://127.0.0.1:12345\")\n */\nexport function buildProviderModels(baseUrl: string): ModelProviderConfig {\n return {\n baseUrl: `${baseUrl}/v1`,\n api: \"openai-completions\",\n models: OPENCLAW_MODELS,\n };\n}\n","/**\n * BlockRun ProviderPlugin for OpenClaw\n *\n * Registers BlockRun as an LLM provider in OpenClaw.\n * Uses a local x402 proxy to handle micropayments transparently —\n * pi-ai sees a standard OpenAI-compatible API at localhost.\n */\n\nimport type { ProviderPlugin, AuthProfileCredential } from \"./types.js\";\nimport { walletKeyAuth, envKeyAuth } from \"./auth.js\";\nimport { buildProviderModels } from \"./models.js\";\nimport type { ProxyHandle } from \"./proxy.js\";\n\n/**\n * State for the running proxy (set when the plugin activates).\n */\nlet activeProxy: ProxyHandle | null = null;\n\n/**\n * Update the proxy handle (called from index.ts when the proxy starts).\n */\nexport function setActiveProxy(proxy: ProxyHandle): void {\n activeProxy = proxy;\n}\n\nexport function getActiveProxy(): ProxyHandle | null {\n return activeProxy;\n}\n\n/**\n * BlockRun provider plugin definition.\n */\nexport const blockrunProvider: ProviderPlugin = {\n id: \"blockrun\",\n label: \"BlockRun\",\n docsPath: \"https://blockrun.ai/docs\",\n aliases: [\"br\"],\n envVars: [\"BLOCKRUN_WALLET_KEY\"],\n\n // Model definitions — dynamically set to proxy URL\n get models() {\n if (!activeProxy) {\n // Fallback: point to BlockRun API directly (won't handle x402, but\n // allows config loading before proxy starts)\n return buildProviderModels(\"https://blockrun.ai/api\");\n }\n return buildProviderModels(activeProxy.baseUrl);\n },\n\n // Auth methods\n auth: [envKeyAuth, walletKeyAuth],\n\n // Format the stored credential as the wallet key\n formatApiKey: (cred: AuthProfileCredential): string => {\n if (\"apiKey\" in cred && typeof cred.apiKey === \"string\") {\n return cred.apiKey;\n }\n throw new Error(\"BlockRun credential must contain an apiKey (wallet private key)\");\n },\n};\n","/**\n * Local x402 Proxy Server\n *\n * Sits between OpenClaw's pi-ai (which makes standard OpenAI-format requests)\n * and BlockRun's API (which requires x402 micropayments).\n *\n * Flow:\n * pi-ai → http://localhost:{port}/v1/chat/completions\n * → proxy forwards to https://blockrun.ai/api/v1/chat/completions\n * → gets 402 → @x402/fetch signs payment → retries\n * → streams response back to pi-ai\n *\n * Phase 2 additions:\n * - Smart routing: when model is \"blockrun/auto\", classify query and pick cheapest model\n * - Usage logging: log every request as JSON line to ~/.openclaw/blockrun/logs/\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { createPaymentFetch } from \"./x402.js\";\nimport { route, getFallbackChain, DEFAULT_ROUTING_CONFIG, type RouterOptions, type RoutingDecision, type RoutingConfig, type ModelPricing } from \"./router/index.js\";\nimport { BLOCKRUN_MODELS } from \"./models.js\";\nimport { logUsage, type UsageEntry } from \"./logger.js\";\n\nconst BLOCKRUN_API = \"https://blockrun.ai/api\";\nconst AUTO_MODEL = \"blockrun/auto\";\nconst USER_AGENT = \"clawrouter/0.2.3\";\n\nexport type ProxyOptions = {\n walletKey: string;\n apiBase?: string;\n port?: number;\n routingConfig?: Partial<RoutingConfig>;\n onReady?: (port: number) => void;\n onError?: (error: Error) => void;\n onPayment?: (info: { model: string; amount: string; network: string }) => void;\n onRouted?: (decision: RoutingDecision) => void;\n};\n\nexport type ProxyHandle = {\n port: number;\n baseUrl: string;\n close: () => Promise<void>;\n};\n\n/**\n * Build model pricing map from BLOCKRUN_MODELS.\n */\nfunction buildModelPricing(): Map<string, ModelPricing> {\n const map = new Map<string, ModelPricing>();\n for (const m of BLOCKRUN_MODELS) {\n if (m.id === AUTO_MODEL) continue; // skip meta-model\n map.set(m.id, { inputPrice: m.inputPrice, outputPrice: m.outputPrice });\n }\n return map;\n}\n\n/**\n * Merge partial routing config overrides with defaults.\n */\nfunction mergeRoutingConfig(overrides?: Partial<RoutingConfig>): RoutingConfig {\n if (!overrides) return DEFAULT_ROUTING_CONFIG;\n return {\n ...DEFAULT_ROUTING_CONFIG,\n ...overrides,\n classifier: { ...DEFAULT_ROUTING_CONFIG.classifier, ...overrides.classifier },\n scoring: { ...DEFAULT_ROUTING_CONFIG.scoring, ...overrides.scoring },\n tiers: { ...DEFAULT_ROUTING_CONFIG.tiers, ...overrides.tiers },\n overrides: { ...DEFAULT_ROUTING_CONFIG.overrides, ...overrides.overrides },\n };\n}\n\n/**\n * Start the local x402 proxy server.\n *\n * Returns a handle with the assigned port, base URL, and a close function.\n */\nexport async function startProxy(options: ProxyOptions): Promise<ProxyHandle> {\n const apiBase = options.apiBase ?? BLOCKRUN_API;\n\n // Create x402 payment-enabled fetch from wallet private key\n const account = privateKeyToAccount(options.walletKey as `0x${string}`);\n const payFetch = createPaymentFetch(options.walletKey as `0x${string}`);\n\n // Build router options\n const routingConfig = mergeRoutingConfig(options.routingConfig);\n const modelPricing = buildModelPricing();\n const routerOpts: RouterOptions = {\n config: routingConfig,\n modelPricing,\n payFetch,\n apiBase,\n };\n\n const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Health check\n if (req.url === \"/health\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", wallet: account.address }));\n return;\n }\n\n // Only proxy paths starting with /v1\n if (!req.url?.startsWith(\"/v1\")) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n return;\n }\n\n try {\n await proxyRequest(req, res, apiBase, payFetch, options, routerOpts);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n options.onError?.(error);\n\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({\n error: { message: `Proxy error: ${error.message}`, type: \"proxy_error\" },\n }));\n }\n }\n });\n\n // Listen on requested port (0 = random available port)\n const listenPort = options.port ?? 0;\n\n return new Promise<ProxyHandle>((resolve, reject) => {\n server.on(\"error\", reject);\n\n server.listen(listenPort, \"127.0.0.1\", () => {\n const addr = server.address() as AddressInfo;\n const port = addr.port;\n const baseUrl = `http://127.0.0.1:${port}`;\n\n options.onReady?.(port);\n\n resolve({\n port,\n baseUrl,\n close: () =>\n new Promise<void>((res, rej) => {\n server.close((err) => (err ? rej(err) : res()));\n }),\n });\n });\n });\n}\n\n/**\n * Proxy a single request through x402 payment flow to BlockRun API.\n *\n * When model is \"blockrun/auto\", runs the smart router to pick the\n * cheapest capable model before forwarding.\n */\nasync function proxyRequest(\n req: IncomingMessage,\n res: ServerResponse,\n apiBase: string,\n payFetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>,\n options: ProxyOptions,\n routerOpts: RouterOptions,\n): Promise<void> {\n const startTime = Date.now();\n\n // Build upstream URL: /v1/chat/completions → https://blockrun.ai/api/v1/chat/completions\n const upstreamUrl = `${apiBase}${req.url}`;\n\n // Collect request body\n const bodyChunks: Buffer[] = [];\n for await (const chunk of req) {\n bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n let body = Buffer.concat(bodyChunks);\n\n // --- Smart routing ---\n let routingDecision: RoutingDecision | undefined;\n const isChatCompletion = req.url?.includes(\"/chat/completions\");\n\n if (isChatCompletion && body.length > 0) {\n try {\n const parsed = JSON.parse(body.toString()) as Record<string, unknown>;\n\n if (parsed.model === AUTO_MODEL) {\n // Extract prompt from messages\n type ChatMessage = { role: string; content: string };\n const messages = parsed.messages as ChatMessage[] | undefined;\n let lastUserMsg: ChatMessage | undefined;\n if (messages) {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === \"user\") { lastUserMsg = messages[i]; break; }\n }\n }\n const systemMsg = messages?.find((m: ChatMessage) => m.role === \"system\");\n const prompt = typeof lastUserMsg?.content === \"string\" ? lastUserMsg.content : \"\";\n const systemPrompt = typeof systemMsg?.content === \"string\" ? systemMsg.content : undefined;\n const maxTokens = (parsed.max_tokens as number) || 4096;\n\n routingDecision = await route(prompt, systemPrompt, maxTokens, routerOpts);\n\n // Replace model in body\n parsed.model = routingDecision.model;\n body = Buffer.from(JSON.stringify(parsed));\n\n options.onRouted?.(routingDecision);\n }\n } catch {\n // JSON parse error — forward body as-is\n }\n }\n\n // Forward headers, stripping host, connection, and content-length\n // (content-length may be wrong after body modification for routing)\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (key === \"host\" || key === \"connection\" || key === \"transfer-encoding\" || key === \"content-length\") continue;\n if (typeof value === \"string\") {\n headers[key] = value;\n }\n }\n // Ensure content-type is set\n if (!headers[\"content-type\"]) {\n headers[\"content-type\"] = \"application/json\";\n }\n // Set User-Agent for BlockRun API tracking\n headers[\"user-agent\"] = USER_AGENT;\n\n // Make the request through x402-wrapped fetch\n // This handles: request → 402 → sign payment → retry with PAYMENT-SIGNATURE header\n const upstream = await payFetch(upstreamUrl, {\n method: req.method ?? \"POST\",\n headers,\n body: body.length > 0 ? body : undefined,\n });\n\n // Forward status and headers from upstream\n const responseHeaders: Record<string, string> = {};\n upstream.headers.forEach((value, key) => {\n // Skip hop-by-hop headers\n if (key === \"transfer-encoding\" || key === \"connection\") return;\n responseHeaders[key] = value;\n });\n\n res.writeHead(upstream.status, responseHeaders);\n\n // Stream the response body\n if (upstream.body) {\n const reader = upstream.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n res.write(value);\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n res.end();\n\n // --- Usage logging (fire-and-forget) ---\n if (routingDecision) {\n const entry: UsageEntry = {\n timestamp: new Date().toISOString(),\n model: routingDecision.model,\n cost: routingDecision.costEstimate,\n latencyMs: Date.now() - startTime,\n };\n logUsage(entry).catch(() => {});\n }\n}\n","/**\n * x402 Payment Implementation\n *\n * Based on BlockRun's proven implementation.\n * Handles 402 Payment Required responses with EIP-712 signed USDC transfers.\n */\n\nimport { signTypedData, privateKeyToAccount } from \"viem/accounts\";\n\nconst BASE_CHAIN_ID = 8453;\nconst USDC_BASE = \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\" as const;\n\nconst USDC_DOMAIN = {\n name: \"USD Coin\",\n version: \"2\",\n chainId: BASE_CHAIN_ID,\n verifyingContract: USDC_BASE,\n} as const;\n\nconst TRANSFER_TYPES = {\n TransferWithAuthorization: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n ],\n} as const;\n\nfunction createNonce(): `0x${string}` {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return `0x${Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')}` as `0x${string}`;\n}\n\ninterface PaymentOption {\n scheme: string;\n network: string;\n amount?: string;\n maxAmountRequired?: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds?: number;\n extra?: { name?: string; version?: string };\n}\n\ninterface PaymentRequired {\n accepts: PaymentOption[];\n resource?: { url?: string; description?: string };\n}\n\nfunction parsePaymentRequired(headerValue: string): PaymentRequired {\n const decoded = atob(headerValue);\n return JSON.parse(decoded) as PaymentRequired;\n}\n\nasync function createPaymentPayload(\n privateKey: `0x${string}`,\n fromAddress: string,\n recipient: string,\n amount: string,\n resourceUrl: string\n): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n const validAfter = now - 600;\n const validBefore = now + 300;\n const nonce = createNonce();\n\n const signature = await signTypedData({\n privateKey,\n domain: USDC_DOMAIN,\n types: TRANSFER_TYPES,\n primaryType: \"TransferWithAuthorization\",\n message: {\n from: fromAddress as `0x${string}`,\n to: recipient as `0x${string}`,\n value: BigInt(amount),\n validAfter: BigInt(validAfter),\n validBefore: BigInt(validBefore),\n nonce,\n },\n });\n\n const paymentData = {\n x402Version: 2,\n resource: {\n url: resourceUrl,\n description: \"BlockRun AI API call\",\n mimeType: \"application/json\",\n },\n accepted: {\n scheme: \"exact\",\n network: \"eip155:8453\",\n amount,\n asset: USDC_BASE,\n payTo: recipient,\n maxTimeoutSeconds: 300,\n extra: { name: \"USD Coin\", version: \"2\" },\n },\n payload: {\n signature,\n authorization: {\n from: fromAddress,\n to: recipient,\n value: amount,\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n },\n },\n extensions: {},\n };\n\n return btoa(JSON.stringify(paymentData));\n}\n\n/**\n * Create a fetch wrapper that handles x402 payment automatically.\n */\nexport function createPaymentFetch(privateKey: `0x${string}`): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response> {\n const account = privateKeyToAccount(privateKey);\n const walletAddress = account.address;\n\n return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n // First request - may get 402\n const response = await fetch(input, init);\n\n if (response.status !== 402) {\n return response;\n }\n\n // Parse 402 payment requirements\n const paymentHeader = response.headers.get(\"x-payment-required\");\n if (!paymentHeader) {\n throw new Error(\"402 response missing x-payment-required header\");\n }\n\n const paymentRequired = parsePaymentRequired(paymentHeader);\n const option = paymentRequired.accepts?.[0];\n if (!option) {\n throw new Error(\"No payment options in 402 response\");\n }\n\n const amount = option.amount || option.maxAmountRequired;\n if (!amount) {\n throw new Error(\"No amount in payment requirements\");\n }\n\n // Create signed payment\n const paymentPayload = await createPaymentPayload(\n privateKey,\n walletAddress,\n option.payTo,\n amount,\n url\n );\n\n // Retry with payment\n const retryHeaders = new Headers(init?.headers);\n retryHeaders.set(\"payment-signature\", paymentPayload);\n\n return fetch(input, {\n ...init,\n headers: retryHeaders,\n });\n };\n}\n","/**\n * Rule-Based Classifier (v2 — Weighted Scoring)\n *\n * Scores a request across 14 weighted dimensions and maps the aggregate\n * score to a tier using configurable boundaries. Confidence is calibrated\n * via sigmoid — low confidence triggers the fallback classifier.\n *\n * Handles 70-80% of requests in < 1ms with zero cost.\n */\n\nimport type { Tier, ScoringResult, ScoringConfig } from \"./types.js\";\n\ntype DimensionScore = { name: string; score: number; signal: string | null };\n\n// ─── Dimension Scorers ───\n// Each returns a score in [-1, 1] and an optional signal string.\n\nfunction scoreTokenCount(\n estimatedTokens: number,\n thresholds: { simple: number; complex: number },\n): DimensionScore {\n if (estimatedTokens < thresholds.simple) {\n return { name: \"tokenCount\", score: -1.0, signal: `short (${estimatedTokens} tokens)` };\n }\n if (estimatedTokens > thresholds.complex) {\n return { name: \"tokenCount\", score: 1.0, signal: `long (${estimatedTokens} tokens)` };\n }\n return { name: \"tokenCount\", score: 0, signal: null };\n}\n\nfunction scoreKeywordMatch(\n text: string,\n keywords: string[],\n name: string,\n signalLabel: string,\n thresholds: { low: number; high: number },\n scores: { none: number; low: number; high: number },\n): DimensionScore {\n const matches = keywords.filter((kw) => text.includes(kw.toLowerCase()));\n if (matches.length >= thresholds.high) {\n return { name, score: scores.high, signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})` };\n }\n if (matches.length >= thresholds.low) {\n return { name, score: scores.low, signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})` };\n }\n return { name, score: scores.none, signal: null };\n}\n\nfunction scoreMultiStep(text: string): DimensionScore {\n const patterns = [/first.*then/i, /step \\d/i, /\\d\\.\\s/];\n const hits = patterns.filter((p) => p.test(text));\n if (hits.length > 0) {\n return { name: \"multiStepPatterns\", score: 0.5, signal: \"multi-step\" };\n }\n return { name: \"multiStepPatterns\", score: 0, signal: null };\n}\n\nfunction scoreQuestionComplexity(prompt: string): DimensionScore {\n const count = (prompt.match(/\\?/g) || []).length;\n if (count > 3) {\n return { name: \"questionComplexity\", score: 0.5, signal: `${count} questions` };\n }\n return { name: \"questionComplexity\", score: 0, signal: null };\n}\n\n// ─── Main Classifier ───\n\nexport function classifyByRules(\n prompt: string,\n systemPrompt: string | undefined,\n estimatedTokens: number,\n config: ScoringConfig,\n): ScoringResult {\n const text = `${systemPrompt ?? \"\"} ${prompt}`.toLowerCase();\n\n // Score all 14 dimensions\n const dimensions: DimensionScore[] = [\n // Original 8 dimensions\n scoreTokenCount(estimatedTokens, config.tokenCountThresholds),\n scoreKeywordMatch(text, config.codeKeywords, \"codePresence\", \"code\",\n { low: 1, high: 2 }, { none: 0, low: 0.5, high: 1.0 }),\n scoreKeywordMatch(text, config.reasoningKeywords, \"reasoningMarkers\", \"reasoning\",\n { low: 1, high: 2 }, { none: 0, low: 0.7, high: 1.0 }),\n scoreKeywordMatch(text, config.technicalKeywords, \"technicalTerms\", \"technical\",\n { low: 2, high: 4 }, { none: 0, low: 0.5, high: 1.0 }),\n scoreKeywordMatch(text, config.creativeKeywords, \"creativeMarkers\", \"creative\",\n { low: 1, high: 2 }, { none: 0, low: 0.5, high: 0.7 }),\n scoreKeywordMatch(text, config.simpleKeywords, \"simpleIndicators\", \"simple\",\n { low: 1, high: 2 }, { none: 0, low: -1.0, high: -1.0 }),\n scoreMultiStep(text),\n scoreQuestionComplexity(prompt),\n\n // 6 new dimensions\n scoreKeywordMatch(text, config.imperativeVerbs, \"imperativeVerbs\", \"imperative\",\n { low: 1, high: 2 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(text, config.constraintIndicators, \"constraintCount\", \"constraints\",\n { low: 1, high: 3 }, { none: 0, low: 0.3, high: 0.7 }),\n scoreKeywordMatch(text, config.outputFormatKeywords, \"outputFormat\", \"format\",\n { low: 1, high: 2 }, { none: 0, low: 0.4, high: 0.7 }),\n scoreKeywordMatch(text, config.referenceKeywords, \"referenceComplexity\", \"references\",\n { low: 1, high: 2 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(text, config.negationKeywords, \"negationComplexity\", \"negation\",\n { low: 2, high: 3 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(text, config.domainSpecificKeywords, \"domainSpecificity\", \"domain-specific\",\n { low: 1, high: 2 }, { none: 0, low: 0.5, high: 0.8 }),\n ];\n\n // Collect signals\n const signals = dimensions\n .filter((d) => d.signal !== null)\n .map((d) => d.signal!);\n\n // Compute weighted score\n const weights = config.dimensionWeights;\n let weightedScore = 0;\n for (const d of dimensions) {\n const w = weights[d.name] ?? 0;\n weightedScore += d.score * w;\n }\n\n // Count reasoning markers for override\n const reasoningMatches = config.reasoningKeywords.filter((kw) =>\n text.includes(kw.toLowerCase()),\n );\n\n // Direct reasoning override: 2+ reasoning markers = high confidence REASONING\n if (reasoningMatches.length >= 2) {\n const confidence = calibrateConfidence(\n Math.max(weightedScore, 0.3), // ensure positive for confidence calc\n config.confidenceSteepness,\n );\n return {\n score: weightedScore,\n tier: \"REASONING\",\n confidence: Math.max(confidence, 0.85),\n signals,\n };\n }\n\n // Map weighted score to tier using boundaries\n const { simpleMedium, mediumComplex, complexReasoning } = config.tierBoundaries;\n let tier: Tier;\n let distanceFromBoundary: number;\n\n if (weightedScore < simpleMedium) {\n tier = \"SIMPLE\";\n distanceFromBoundary = simpleMedium - weightedScore;\n } else if (weightedScore < mediumComplex) {\n tier = \"MEDIUM\";\n distanceFromBoundary = Math.min(\n weightedScore - simpleMedium,\n mediumComplex - weightedScore,\n );\n } else if (weightedScore < complexReasoning) {\n tier = \"COMPLEX\";\n distanceFromBoundary = Math.min(\n weightedScore - mediumComplex,\n complexReasoning - weightedScore,\n );\n } else {\n tier = \"REASONING\";\n distanceFromBoundary = weightedScore - complexReasoning;\n }\n\n // Calibrate confidence via sigmoid of distance from nearest boundary\n const confidence = calibrateConfidence(distanceFromBoundary, config.confidenceSteepness);\n\n // If confidence is below threshold → ambiguous\n if (confidence < config.confidenceThreshold) {\n return { score: weightedScore, tier: null, confidence, signals };\n }\n\n return { score: weightedScore, tier, confidence, signals };\n}\n\n/**\n * Sigmoid confidence calibration.\n * Maps distance from tier boundary to [0.5, 1.0] confidence range.\n */\nfunction calibrateConfidence(distance: number, steepness: number): number {\n return 1 / (1 + Math.exp(-steepness * distance));\n}\n","/**\n * LLM Classifier (Fallback)\n *\n * When the rule-based classifier returns ambiguous (score 1-2),\n * we send a classification request to the cheapest model.\n *\n * Cost per classification: ~$0.00003\n * Latency: ~200-400ms\n * Only triggered for ~20-30% of requests.\n */\n\nimport type { Tier } from \"./types.js\";\n\nconst CLASSIFIER_PROMPT = `You are a query complexity classifier. Classify the user's query into exactly one category.\n\nCategories:\n- SIMPLE: Factual Q&A, definitions, translations, short answers\n- MEDIUM: Summaries, explanations, moderate code generation\n- COMPLEX: Multi-step code, system design, creative writing, analysis\n- REASONING: Mathematical proofs, formal logic, step-by-step problem solving\n\nRespond with ONLY one word: SIMPLE, MEDIUM, COMPLEX, or REASONING.`;\n\n// In-memory cache: hash → { tier, expires }\nconst cache = new Map<string, { tier: Tier; expires: number }>();\n\nexport type LLMClassifierConfig = {\n model: string;\n maxTokens: number;\n temperature: number;\n truncationChars: number;\n cacheTtlMs: number;\n};\n\ntype PayFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Classify a prompt using a cheap LLM.\n * Returns tier and confidence. Defaults to MEDIUM on any failure.\n */\nexport async function classifyByLLM(\n prompt: string,\n config: LLMClassifierConfig,\n payFetch: PayFetch,\n apiBase: string,\n): Promise<{ tier: Tier; confidence: number }> {\n const truncated = prompt.slice(0, config.truncationChars);\n\n // Check cache\n const cacheKey = simpleHash(truncated);\n const cached = cache.get(cacheKey);\n if (cached && cached.expires > Date.now()) {\n return { tier: cached.tier, confidence: 0.75 };\n }\n\n try {\n const response = await payFetch(`${apiBase}/v1/chat/completions`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: config.model,\n messages: [\n { role: \"system\", content: CLASSIFIER_PROMPT },\n { role: \"user\", content: truncated },\n ],\n max_tokens: config.maxTokens,\n temperature: config.temperature,\n stream: false,\n }),\n });\n\n if (!response.ok) {\n return { tier: \"MEDIUM\", confidence: 0.5 };\n }\n\n const data = (await response.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n const content = data.choices?.[0]?.message?.content?.trim().toUpperCase() ?? \"\";\n const tier = parseTier(content);\n\n // Cache result\n cache.set(cacheKey, { tier, expires: Date.now() + config.cacheTtlMs });\n\n // Prune if cache grows too large\n if (cache.size > 1000) {\n pruneCache();\n }\n\n return { tier, confidence: 0.75 };\n } catch {\n // Any error → safe default\n return { tier: \"MEDIUM\", confidence: 0.5 };\n }\n}\n\n/**\n * Parse tier from LLM response. Handles \"SIMPLE\", \"The query is SIMPLE\", etc.\n */\nfunction parseTier(text: string): Tier {\n if (/\\bREASONING\\b/.test(text)) return \"REASONING\";\n if (/\\bCOMPLEX\\b/.test(text)) return \"COMPLEX\";\n if (/\\bMEDIUM\\b/.test(text)) return \"MEDIUM\";\n if (/\\bSIMPLE\\b/.test(text)) return \"SIMPLE\";\n return \"MEDIUM\"; // safe default\n}\n\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash |= 0;\n }\n return hash.toString(36);\n}\n\nfunction pruneCache(): void {\n const now = Date.now();\n for (const [key, value] of cache) {\n if (value.expires <= now) {\n cache.delete(key);\n }\n }\n}\n","/**\n * Tier → Model Selection\n *\n * Maps a classification tier to the cheapest capable model.\n * Builds RoutingDecision metadata with cost estimates and savings.\n */\n\nimport type { Tier, TierConfig, RoutingDecision } from \"./types.js\";\n\nexport type ModelPricing = {\n inputPrice: number; // per 1M tokens\n outputPrice: number; // per 1M tokens\n};\n\n/**\n * Select the primary model for a tier and build the RoutingDecision.\n */\nexport function selectModel(\n tier: Tier,\n confidence: number,\n method: \"rules\" | \"llm\",\n reasoning: string,\n tierConfigs: Record<Tier, TierConfig>,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n): RoutingDecision {\n const tierConfig = tierConfigs[tier];\n const model = tierConfig.primary;\n const pricing = modelPricing.get(model);\n\n const inputCost = pricing\n ? (estimatedInputTokens / 1_000_000) * pricing.inputPrice\n : 0;\n const outputCost = pricing\n ? (maxOutputTokens / 1_000_000) * pricing.outputPrice\n : 0;\n const costEstimate = inputCost + outputCost;\n\n // Baseline: what Claude Opus would cost (the premium default)\n const opusPricing = modelPricing.get(\"anthropic/claude-opus-4\");\n const baselineInput = opusPricing\n ? (estimatedInputTokens / 1_000_000) * opusPricing.inputPrice\n : 0;\n const baselineOutput = opusPricing\n ? (maxOutputTokens / 1_000_000) * opusPricing.outputPrice\n : 0;\n const baselineCost = baselineInput + baselineOutput;\n\n const savings =\n baselineCost > 0\n ? Math.max(0, (baselineCost - costEstimate) / baselineCost)\n : 0;\n\n return {\n model,\n tier,\n confidence,\n method,\n reasoning,\n costEstimate,\n baselineCost,\n savings,\n };\n}\n\n/**\n * Get the ordered fallback chain for a tier: [primary, ...fallbacks].\n */\nexport function getFallbackChain(\n tier: Tier,\n tierConfigs: Record<Tier, TierConfig>,\n): string[] {\n const config = tierConfigs[tier];\n return [config.primary, ...config.fallback];\n}\n","/**\n * Default Routing Config\n *\n * All routing parameters as a TypeScript constant.\n * Operators override via openclaw.yaml plugin config.\n *\n * Scoring uses 14 weighted dimensions with sigmoid confidence calibration.\n */\n\nimport type { RoutingConfig } from \"./types.js\";\n\nexport const DEFAULT_ROUTING_CONFIG: RoutingConfig = {\n version: \"2.0\",\n\n classifier: {\n llmModel: \"google/gemini-2.5-flash\",\n llmMaxTokens: 10,\n llmTemperature: 0,\n promptTruncationChars: 500,\n cacheTtlMs: 3_600_000, // 1 hour\n },\n\n scoring: {\n tokenCountThresholds: { simple: 50, complex: 500 },\n codeKeywords: [\n \"function\", \"class\", \"import\", \"def\", \"SELECT\", \"async\", \"await\",\n \"const\", \"let\", \"var\", \"return\", \"```\",\n ],\n reasoningKeywords: [\n \"prove\", \"theorem\", \"derive\", \"step by step\", \"chain of thought\",\n \"formally\", \"mathematical\", \"proof\", \"logically\",\n ],\n simpleKeywords: [\n \"what is\", \"define\", \"translate\", \"hello\", \"yes or no\",\n \"capital of\", \"how old\", \"who is\", \"when was\",\n ],\n technicalKeywords: [\n \"algorithm\", \"optimize\", \"architecture\", \"distributed\",\n \"kubernetes\", \"microservice\", \"database\", \"infrastructure\",\n ],\n creativeKeywords: [\n \"story\", \"poem\", \"compose\", \"brainstorm\", \"creative\",\n \"imagine\", \"write a\",\n ],\n\n // New dimension keyword lists\n imperativeVerbs: [\n \"build\", \"create\", \"implement\", \"design\", \"develop\", \"construct\",\n \"generate\", \"deploy\", \"configure\", \"set up\",\n ],\n constraintIndicators: [\n \"under\", \"at most\", \"at least\", \"within\", \"no more than\",\n \"o(\", \"maximum\", \"minimum\", \"limit\", \"budget\",\n ],\n outputFormatKeywords: [\n \"json\", \"yaml\", \"xml\", \"table\", \"csv\", \"markdown\",\n \"schema\", \"format as\", \"structured\",\n ],\n referenceKeywords: [\n \"above\", \"below\", \"previous\", \"following\", \"the docs\",\n \"the api\", \"the code\", \"earlier\", \"attached\",\n ],\n negationKeywords: [\n \"don't\", \"do not\", \"avoid\", \"never\", \"without\",\n \"except\", \"exclude\", \"no longer\",\n ],\n domainSpecificKeywords: [\n \"quantum\", \"fpga\", \"vlsi\", \"risc-v\", \"asic\", \"photonics\",\n \"genomics\", \"proteomics\", \"topological\", \"homomorphic\",\n \"zero-knowledge\", \"lattice-based\",\n ],\n\n // Dimension weights (sum to 1.0)\n dimensionWeights: {\n tokenCount: 0.08,\n codePresence: 0.15,\n reasoningMarkers: 0.18,\n technicalTerms: 0.10,\n creativeMarkers: 0.05,\n simpleIndicators: 0.12,\n multiStepPatterns: 0.12,\n questionComplexity: 0.05,\n imperativeVerbs: 0.03,\n constraintCount: 0.04,\n outputFormat: 0.03,\n referenceComplexity: 0.02,\n negationComplexity: 0.01,\n domainSpecificity: 0.02,\n },\n\n // Tier boundaries on weighted score axis\n tierBoundaries: {\n simpleMedium: 0.0,\n mediumComplex: 0.15,\n complexReasoning: 0.25,\n },\n\n // Sigmoid steepness for confidence calibration\n confidenceSteepness: 12,\n // Below this confidence → ambiguous (null tier)\n confidenceThreshold: 0.70,\n },\n\n tiers: {\n SIMPLE: {\n primary: \"google/gemini-2.5-flash\",\n fallback: [\"deepseek/deepseek-chat\", \"openai/gpt-4o-mini\"],\n },\n MEDIUM: {\n primary: \"deepseek/deepseek-chat\",\n fallback: [\"google/gemini-2.5-flash\", \"openai/gpt-4o-mini\"],\n },\n COMPLEX: {\n primary: \"anthropic/claude-opus-4\",\n fallback: [\"anthropic/claude-sonnet-4\", \"openai/gpt-4o\"],\n },\n REASONING: {\n primary: \"openai/o3\",\n fallback: [\"google/gemini-2.5-pro\", \"anthropic/claude-sonnet-4\"],\n },\n },\n\n overrides: {\n maxTokensForceComplex: 100_000,\n structuredOutputMinTier: \"MEDIUM\",\n },\n};\n","/**\n * Smart Router Entry Point\n *\n * Classifies requests and routes to the cheapest capable model.\n * Uses hybrid approach: rules first (< 1ms), LLM fallback for ambiguous cases.\n */\n\nimport type { Tier, RoutingDecision, RoutingConfig } from \"./types.js\";\nimport { classifyByRules } from \"./rules.js\";\nimport { classifyByLLM } from \"./llm-classifier.js\";\nimport { selectModel, getFallbackChain, type ModelPricing } from \"./selector.js\";\n\nexport type RouterOptions = {\n config: RoutingConfig;\n modelPricing: Map<string, ModelPricing>;\n payFetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n apiBase: string;\n};\n\n/**\n * Route a request to the cheapest capable model.\n *\n * 1. Check overrides (large context, structured output)\n * 2. Run rule-based classifier\n * 3. If ambiguous, run LLM classifier\n * 4. Select model for tier\n * 5. Return RoutingDecision with metadata\n */\nexport async function route(\n prompt: string,\n systemPrompt: string | undefined,\n maxOutputTokens: number,\n options: RouterOptions,\n): Promise<RoutingDecision> {\n const { config, modelPricing, payFetch, apiBase } = options;\n\n // Estimate input tokens (~4 chars per token)\n const fullText = `${systemPrompt ?? \"\"} ${prompt}`;\n const estimatedTokens = Math.ceil(fullText.length / 4);\n\n // --- Override: large context → force COMPLEX ---\n if (estimatedTokens > config.overrides.maxTokensForceComplex) {\n return selectModel(\n \"COMPLEX\",\n 0.95,\n \"rules\",\n `Input exceeds ${config.overrides.maxTokensForceComplex} tokens`,\n config.tiers,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n );\n }\n\n // Structured output detection\n const hasStructuredOutput = systemPrompt\n ? /json|structured|schema/i.test(systemPrompt)\n : false;\n\n // --- Rule-based classification ---\n const ruleResult = classifyByRules(\n prompt,\n systemPrompt,\n estimatedTokens,\n config.scoring,\n );\n\n let tier: Tier;\n let confidence: number;\n let method: \"rules\" | \"llm\" = \"rules\";\n let reasoning = `score=${ruleResult.score} | ${ruleResult.signals.join(\", \")}`;\n\n if (ruleResult.tier !== null) {\n tier = ruleResult.tier;\n confidence = ruleResult.confidence;\n } else {\n // Ambiguous — LLM classifier fallback\n const llmResult = await classifyByLLM(\n prompt,\n {\n model: config.classifier.llmModel,\n maxTokens: config.classifier.llmMaxTokens,\n temperature: config.classifier.llmTemperature,\n truncationChars: config.classifier.promptTruncationChars,\n cacheTtlMs: config.classifier.cacheTtlMs,\n },\n payFetch,\n apiBase,\n );\n\n tier = llmResult.tier;\n confidence = llmResult.confidence;\n method = \"llm\";\n reasoning += ` | ambiguous -> LLM: ${tier}`;\n }\n\n // Apply structured output minimum tier\n if (hasStructuredOutput) {\n const tierRank: Record<Tier, number> = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };\n const minTier = config.overrides.structuredOutputMinTier;\n if (tierRank[tier] < tierRank[minTier]) {\n reasoning += ` | upgraded to ${minTier} (structured output)`;\n tier = minTier;\n }\n }\n\n return selectModel(\n tier,\n confidence,\n method,\n reasoning,\n config.tiers,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n );\n}\n\nexport { getFallbackChain } from \"./selector.js\";\nexport { DEFAULT_ROUTING_CONFIG } from \"./config.js\";\nexport type { RoutingDecision, Tier, RoutingConfig } from \"./types.js\";\nexport type { ModelPricing } from \"./selector.js\";\n","/**\n * Usage Logger\n *\n * Logs every LLM request as a JSON line to a daily log file.\n * Files: ~/.openclaw/blockrun/logs/usage-YYYY-MM-DD.jsonl\n *\n * MVP: append-only JSON lines. No rotation, no cleanup.\n * Logging never breaks the request flow — all errors are swallowed.\n */\n\nimport { appendFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport type UsageEntry = {\n timestamp: string;\n model: string;\n cost: number;\n latencyMs: number;\n};\n\nconst LOG_DIR = join(homedir(), \".openclaw\", \"blockrun\", \"logs\");\nlet dirReady = false;\n\nasync function ensureDir(): Promise<void> {\n if (dirReady) return;\n await mkdir(LOG_DIR, { recursive: true });\n dirReady = true;\n}\n\n/**\n * Log a usage entry as a JSON line.\n */\nexport async function logUsage(entry: UsageEntry): Promise<void> {\n try {\n await ensureDir();\n const date = entry.timestamp.slice(0, 10); // YYYY-MM-DD\n const file = join(LOG_DIR, `usage-${date}.jsonl`);\n await appendFile(file, JSON.stringify(entry) + \"\\n\");\n } catch {\n // Never break the request flow\n }\n}\n","/**\n * @blockrun/clawrouter\n *\n * Smart LLM router for OpenClaw — 30+ models, x402 micropayments, 78% cost savings.\n * Routes each request to the cheapest model that can handle it.\n *\n * Usage:\n * # Install the plugin\n * openclaw plugin install @blockrun/clawrouter\n *\n * # Fund your wallet with USDC on Base (address printed on install)\n *\n * # Use smart routing (auto-picks cheapest model)\n * openclaw config set model blockrun/auto\n *\n * # Or use any specific BlockRun model\n * openclaw config set model openai/gpt-5.2\n */\n\nimport type { OpenClawPluginDefinition, OpenClawPluginApi } from \"./types.js\";\nimport { blockrunProvider, setActiveProxy } from \"./provider.js\";\nimport { startProxy } from \"./proxy.js\";\nimport { resolveOrGenerateWalletKey } from \"./auth.js\";\nimport type { RoutingConfig } from \"./router/index.js\";\n\n/**\n * Start the x402 proxy in the background.\n * Called from register() because OpenClaw's loader only invokes register(),\n * treating activate() as an alias (def.register ?? def.activate).\n */\nasync function startProxyInBackground(api: OpenClawPluginApi): Promise<void> {\n // Resolve wallet key: saved file → env var → auto-generate\n const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();\n\n // Log wallet source\n if (source === \"generated\") {\n api.logger.info(`Generated new wallet: ${address}`);\n api.logger.info(`Fund with USDC on Base to start using ClawRouter.`);\n } else if (source === \"saved\") {\n api.logger.info(`Using saved wallet: ${address}`);\n } else {\n api.logger.info(`Using wallet from BLOCKRUN_WALLET_KEY: ${address}`);\n }\n\n // Resolve routing config overrides from plugin config\n const routingConfig = api.pluginConfig?.routing as Partial<RoutingConfig> | undefined;\n\n const proxy = await startProxy({\n walletKey,\n routingConfig,\n onReady: (port) => {\n api.logger.info(`BlockRun x402 proxy listening on port ${port}`);\n },\n onError: (error) => {\n api.logger.error(`BlockRun proxy error: ${error.message}`);\n },\n onRouted: (decision) => {\n const cost = decision.costEstimate.toFixed(4);\n const saved = (decision.savings * 100).toFixed(0);\n api.logger.info(`${decision.model} $${cost} (saved ${saved}%)`);\n },\n });\n\n setActiveProxy(proxy);\n api.logger.info(`BlockRun provider active — ${proxy.baseUrl}/v1 (smart routing enabled)`);\n}\n\nconst plugin: OpenClawPluginDefinition = {\n id: \"clawrouter\",\n name: \"ClawRouter\",\n description: \"Smart LLM router — 30+ models, x402 micropayments, 78% cost savings\",\n version: \"0.2.3\",\n\n register(api: OpenClawPluginApi) {\n // Register BlockRun as a provider (sync — available immediately)\n api.registerProvider(blockrunProvider);\n api.logger.info(\"BlockRun provider registered (30+ models via x402)\");\n\n // Start x402 proxy in background (fire-and-forget)\n // OpenClaw only calls register(), not activate() — so all init goes here.\n // The loader ignores async returns, but the proxy starts in the background\n // and setActiveProxy() makes it available to the provider once ready.\n startProxyInBackground(api).catch((err) => {\n api.logger.error(\n `Failed to start BlockRun proxy: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n },\n};\n\n\nexport default plugin;\n\n// Re-export for programmatic use\nexport { startProxy } from \"./proxy.js\";\nexport { blockrunProvider } from \"./provider.js\";\nexport { OPENCLAW_MODELS, BLOCKRUN_MODELS, buildProviderModels } from \"./models.js\";\nexport { route, DEFAULT_ROUTING_CONFIG } from \"./router/index.js\";\nexport type { RoutingDecision, RoutingConfig, Tier } from \"./router/index.js\";\nexport { logUsage } from \"./logger.js\";\nexport type { UsageEntry } from \"./logger.js\";\n"],"mappings":"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","names":["privateKeyToAccount","privateKeyToAccount","confidence","mkdir","join","homedir","privateKeyToAccount"]}
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts","../src/models.ts","../src/provider.ts","../src/proxy.ts","../src/x402.ts","../src/payment-cache.ts","../src/router/rules.ts","../src/router/llm-classifier.ts","../src/router/selector.ts","../src/router/config.ts","../src/router/index.ts","../src/logger.ts","../src/dedup.ts","../src/index.ts"],"sourcesContent":["/**\n * BlockRun Auth Methods for OpenClaw\n *\n * Provides wallet-based authentication for the BlockRun provider.\n * Operators configure their wallet private key, which is used to\n * sign x402 micropayments for LLM inference.\n *\n * Three methods:\n * 1. Auto-generate — create a new wallet on first run, save to ~/.openclaw/blockrun/wallet.key\n * 2. Environment variable — read from BLOCKRUN_WALLET_KEY\n * 3. Manual input — operator enters private key via wizard\n */\n\nimport { writeFile, readFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { generatePrivateKey, privateKeyToAccount } from \"viem/accounts\";\nimport type { ProviderAuthMethod, ProviderAuthContext, ProviderAuthResult } from \"./types.js\";\n\nconst WALLET_DIR = join(homedir(), \".openclaw\", \"blockrun\");\nconst WALLET_FILE = join(WALLET_DIR, \"wallet.key\");\n\n/**\n * Try to load a previously auto-generated wallet key from disk.\n */\nasync function loadSavedWallet(): Promise<string | undefined> {\n try {\n const key = (await readFile(WALLET_FILE, \"utf-8\")).trim();\n if (key.startsWith(\"0x\") && key.length === 66) return key;\n } catch {\n // File doesn't exist yet\n }\n return undefined;\n}\n\n/**\n * Generate a new wallet, save to disk, return the private key.\n */\nasync function generateAndSaveWallet(): Promise<{ key: string; address: string }> {\n const key = generatePrivateKey();\n const account = privateKeyToAccount(key);\n await mkdir(WALLET_DIR, { recursive: true });\n await writeFile(WALLET_FILE, key + \"\\n\", { mode: 0o600 });\n return { key, address: account.address };\n}\n\n/**\n * Resolve wallet key: load saved → env var → auto-generate.\n * Called by index.ts before the auth wizard runs.\n */\nexport async function resolveOrGenerateWalletKey(): Promise<{ key: string; address: string; source: \"saved\" | \"env\" | \"generated\" }> {\n // 1. Previously saved wallet\n const saved = await loadSavedWallet();\n if (saved) {\n const account = privateKeyToAccount(saved as `0x${string}`);\n return { key: saved, address: account.address, source: \"saved\" };\n }\n\n // 2. Environment variable\n const envKey = process.env.BLOCKRUN_WALLET_KEY;\n if (typeof envKey === \"string\" && envKey.startsWith(\"0x\") && envKey.length === 66) {\n const account = privateKeyToAccount(envKey as `0x${string}`);\n return { key: envKey, address: account.address, source: \"env\" };\n }\n\n // 3. Auto-generate\n const { key, address } = await generateAndSaveWallet();\n return { key, address, source: \"generated\" };\n}\n\n/**\n * Auth method: operator enters their wallet private key directly.\n */\nexport const walletKeyAuth: ProviderAuthMethod = {\n id: \"wallet-key\",\n label: \"Wallet Private Key\",\n hint: \"Enter your EVM wallet private key (0x...) for x402 payments to BlockRun\",\n kind: \"api_key\",\n run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {\n const key = await ctx.prompter.text({\n message: \"Enter your wallet private key (0x...)\",\n validate: (value: string) => {\n const trimmed = value.trim();\n if (!trimmed.startsWith(\"0x\")) return \"Key must start with 0x\";\n if (trimmed.length !== 66) return \"Key must be 66 characters (0x + 64 hex)\";\n if (!/^0x[0-9a-fA-F]{64}$/.test(trimmed)) return \"Key must be valid hex\";\n return undefined;\n },\n });\n\n if (!key || typeof key !== \"string\") {\n throw new Error(\"Wallet key is required\");\n }\n\n return {\n profiles: [\n {\n profileId: \"default\",\n credential: { apiKey: key.trim() },\n },\n ],\n notes: [\n \"Wallet key stored securely in OpenClaw credentials.\",\n \"Your wallet signs x402 USDC payments on Base for each LLM call.\",\n \"Fund your wallet with USDC on Base to start using BlockRun models.\",\n ],\n };\n },\n};\n\n/**\n * Auth method: read wallet key from BLOCKRUN_WALLET_KEY environment variable.\n */\nexport const envKeyAuth: ProviderAuthMethod = {\n id: \"env-key\",\n label: \"Environment Variable\",\n hint: \"Use BLOCKRUN_WALLET_KEY environment variable\",\n kind: \"api_key\",\n run: async (): Promise<ProviderAuthResult> => {\n const key = process.env.BLOCKRUN_WALLET_KEY;\n\n if (!key) {\n throw new Error(\n \"BLOCKRUN_WALLET_KEY environment variable is not set. \" +\n \"Set it to your EVM wallet private key (0x...).\",\n );\n }\n\n return {\n profiles: [\n {\n profileId: \"default\",\n credential: { apiKey: key.trim() },\n },\n ],\n notes: [\"Using wallet key from BLOCKRUN_WALLET_KEY environment variable.\"],\n };\n },\n};\n","/**\n * BlockRun Model Definitions for OpenClaw\n *\n * Maps BlockRun's 30+ AI models to OpenClaw's ModelDefinitionConfig format.\n * All models use the \"openai-completions\" API since BlockRun is OpenAI-compatible.\n *\n * Pricing is in USD per 1M tokens. Operators pay these rates via x402;\n * they set their own markup when reselling to end users (Phase 2).\n */\n\nimport type { ModelDefinitionConfig, ModelProviderConfig } from \"./types.js\";\n\ntype BlockRunModel = {\n id: string;\n name: string;\n inputPrice: number;\n outputPrice: number;\n contextWindow: number;\n maxOutput: number;\n reasoning?: boolean;\n vision?: boolean;\n};\n\nexport const BLOCKRUN_MODELS: BlockRunModel[] = [\n // Smart routing meta-model — proxy replaces with actual model\n { id: \"blockrun/auto\", name: \"BlockRun Smart Router\", inputPrice: 0, outputPrice: 0, contextWindow: 1_050_000, maxOutput: 128_000 },\n\n\n // OpenAI GPT-5 Family\n { id: \"openai/gpt-5.2\", name: \"GPT-5.2\", inputPrice: 1.75, outputPrice: 14.0, contextWindow: 400000, maxOutput: 128000, reasoning: true, vision: true },\n { id: \"openai/gpt-5-mini\", name: \"GPT-5 Mini\", inputPrice: 0.25, outputPrice: 2.0, contextWindow: 200000, maxOutput: 65536 },\n { id: \"openai/gpt-5-nano\", name: \"GPT-5 Nano\", inputPrice: 0.05, outputPrice: 0.4, contextWindow: 128000, maxOutput: 32768 },\n { id: \"openai/gpt-5.2-pro\", name: \"GPT-5.2 Pro\", inputPrice: 21.0, outputPrice: 168.0, contextWindow: 400000, maxOutput: 128000, reasoning: true },\n\n // OpenAI GPT-4 Family\n { id: \"openai/gpt-4.1\", name: \"GPT-4.1\", inputPrice: 2.0, outputPrice: 8.0, contextWindow: 128000, maxOutput: 16384, vision: true },\n { id: \"openai/gpt-4.1-mini\", name: \"GPT-4.1 Mini\", inputPrice: 0.4, outputPrice: 1.6, contextWindow: 128000, maxOutput: 16384 },\n { id: \"openai/gpt-4.1-nano\", name: \"GPT-4.1 Nano\", inputPrice: 0.1, outputPrice: 0.4, contextWindow: 128000, maxOutput: 16384 },\n { id: \"openai/gpt-4o\", name: \"GPT-4o\", inputPrice: 2.5, outputPrice: 10.0, contextWindow: 128000, maxOutput: 16384, vision: true },\n { id: \"openai/gpt-4o-mini\", name: \"GPT-4o Mini\", inputPrice: 0.15, outputPrice: 0.6, contextWindow: 128000, maxOutput: 16384 },\n\n // OpenAI O-series (Reasoning)\n { id: \"openai/o1\", name: \"o1\", inputPrice: 15.0, outputPrice: 60.0, contextWindow: 200000, maxOutput: 100000, reasoning: true },\n { id: \"openai/o1-mini\", name: \"o1-mini\", inputPrice: 1.1, outputPrice: 4.4, contextWindow: 128000, maxOutput: 65536, reasoning: true },\n { id: \"openai/o3\", name: \"o3\", inputPrice: 2.0, outputPrice: 8.0, contextWindow: 200000, maxOutput: 100000, reasoning: true },\n { id: \"openai/o3-mini\", name: \"o3-mini\", inputPrice: 1.1, outputPrice: 4.4, contextWindow: 128000, maxOutput: 65536, reasoning: true },\n { id: \"openai/o4-mini\", name: \"o4-mini\", inputPrice: 1.1, outputPrice: 4.4, contextWindow: 128000, maxOutput: 65536, reasoning: true },\n\n // Anthropic\n { id: \"anthropic/claude-haiku-4.5\", name: \"Claude Haiku 4.5\", inputPrice: 1.0, outputPrice: 5.0, contextWindow: 200000, maxOutput: 8192 },\n { id: \"anthropic/claude-sonnet-4\", name: \"Claude Sonnet 4\", inputPrice: 3.0, outputPrice: 15.0, contextWindow: 200000, maxOutput: 64000, reasoning: true },\n { id: \"anthropic/claude-opus-4\", name: \"Claude Opus 4\", inputPrice: 15.0, outputPrice: 75.0, contextWindow: 200000, maxOutput: 32000, reasoning: true },\n { id: \"anthropic/claude-opus-4.5\", name: \"Claude Opus 4.5\", inputPrice: 15.0, outputPrice: 75.0, contextWindow: 200000, maxOutput: 32000, reasoning: true },\n\n // Google\n { id: \"google/gemini-3-pro-preview\", name: \"Gemini 3 Pro Preview\", inputPrice: 2.0, outputPrice: 12.0, contextWindow: 1050000, maxOutput: 65536, reasoning: true, vision: true },\n { id: \"google/gemini-2.5-pro\", name: \"Gemini 2.5 Pro\", inputPrice: 1.25, outputPrice: 10.0, contextWindow: 1050000, maxOutput: 65536, reasoning: true, vision: true },\n { id: \"google/gemini-2.5-flash\", name: \"Gemini 2.5 Flash\", inputPrice: 0.15, outputPrice: 0.6, contextWindow: 1000000, maxOutput: 65536 },\n\n // DeepSeek\n { id: \"deepseek/deepseek-chat\", name: \"DeepSeek V3.2 Chat\", inputPrice: 0.28, outputPrice: 0.42, contextWindow: 128000, maxOutput: 8192 },\n { id: \"deepseek/deepseek-reasoner\", name: \"DeepSeek V3.2 Reasoner\", inputPrice: 0.28, outputPrice: 0.42, contextWindow: 128000, maxOutput: 8192, reasoning: true },\n\n // xAI / Grok\n { id: \"xai/grok-3\", name: \"Grok 3\", inputPrice: 3.0, outputPrice: 15.0, contextWindow: 131072, maxOutput: 16384, reasoning: true },\n { id: \"xai/grok-3-fast\", name: \"Grok 3 Fast\", inputPrice: 5.0, outputPrice: 25.0, contextWindow: 131072, maxOutput: 16384, reasoning: true },\n { id: \"xai/grok-3-mini\", name: \"Grok 3 Mini\", inputPrice: 0.3, outputPrice: 0.5, contextWindow: 131072, maxOutput: 16384 },\n];\n\n/**\n * Convert BlockRun model definitions to OpenClaw ModelDefinitionConfig format.\n */\nfunction toOpenClawModel(m: BlockRunModel): ModelDefinitionConfig {\n return {\n id: m.id,\n name: m.name,\n api: \"openai-completions\",\n reasoning: m.reasoning ?? false,\n input: m.vision ? [\"text\", \"image\"] : [\"text\"],\n cost: {\n input: m.inputPrice,\n output: m.outputPrice,\n cacheRead: 0,\n cacheWrite: 0,\n },\n contextWindow: m.contextWindow,\n maxTokens: m.maxOutput,\n };\n}\n\n/**\n * All BlockRun models in OpenClaw format.\n */\nexport const OPENCLAW_MODELS: ModelDefinitionConfig[] = BLOCKRUN_MODELS.map(toOpenClawModel);\n\n/**\n * Build a ModelProviderConfig for BlockRun.\n *\n * @param baseUrl - The proxy's local base URL (e.g., \"http://127.0.0.1:12345\")\n */\nexport function buildProviderModels(baseUrl: string): ModelProviderConfig {\n return {\n baseUrl: `${baseUrl}/v1`,\n api: \"openai-completions\",\n models: OPENCLAW_MODELS,\n };\n}\n","/**\n * BlockRun ProviderPlugin for OpenClaw\n *\n * Registers BlockRun as an LLM provider in OpenClaw.\n * Uses a local x402 proxy to handle micropayments transparently —\n * pi-ai sees a standard OpenAI-compatible API at localhost.\n */\n\nimport type { ProviderPlugin, AuthProfileCredential } from \"./types.js\";\nimport { walletKeyAuth, envKeyAuth } from \"./auth.js\";\nimport { buildProviderModels } from \"./models.js\";\nimport type { ProxyHandle } from \"./proxy.js\";\n\n/**\n * State for the running proxy (set when the plugin activates).\n */\nlet activeProxy: ProxyHandle | null = null;\n\n/**\n * Update the proxy handle (called from index.ts when the proxy starts).\n */\nexport function setActiveProxy(proxy: ProxyHandle): void {\n activeProxy = proxy;\n}\n\nexport function getActiveProxy(): ProxyHandle | null {\n return activeProxy;\n}\n\n/**\n * BlockRun provider plugin definition.\n */\nexport const blockrunProvider: ProviderPlugin = {\n id: \"blockrun\",\n label: \"BlockRun\",\n docsPath: \"https://blockrun.ai/docs\",\n aliases: [\"br\"],\n envVars: [\"BLOCKRUN_WALLET_KEY\"],\n\n // Model definitions — dynamically set to proxy URL\n get models() {\n if (!activeProxy) {\n // Fallback: point to BlockRun API directly (won't handle x402, but\n // allows config loading before proxy starts)\n return buildProviderModels(\"https://blockrun.ai/api\");\n }\n return buildProviderModels(activeProxy.baseUrl);\n },\n\n // Auth methods\n auth: [envKeyAuth, walletKeyAuth],\n\n // Format the stored credential as the wallet key\n formatApiKey: (cred: AuthProfileCredential): string => {\n if (\"apiKey\" in cred && typeof cred.apiKey === \"string\") {\n return cred.apiKey;\n }\n throw new Error(\"BlockRun credential must contain an apiKey (wallet private key)\");\n },\n};\n","/**\n * Local x402 Proxy Server\n *\n * Sits between OpenClaw's pi-ai (which makes standard OpenAI-format requests)\n * and BlockRun's API (which requires x402 micropayments).\n *\n * Flow:\n * pi-ai → http://localhost:{port}/v1/chat/completions\n * → proxy forwards to https://blockrun.ai/api/v1/chat/completions\n * → gets 402 → @x402/fetch signs payment → retries\n * → streams response back to pi-ai\n *\n * Optimizations (v0.3.0):\n * - SSE heartbeat: for streaming requests, sends headers + heartbeat immediately\n * before the x402 flow, preventing OpenClaw's 10-15s timeout from firing.\n * - Response dedup: hashes request bodies and caches responses for 30s,\n * preventing double-charging when OpenClaw retries after timeout.\n * - Payment cache: after first 402, pre-signs subsequent requests to skip\n * the 402 round trip (~200ms savings per request).\n * - Smart routing: when model is \"blockrun/auto\", classify query and pick cheapest model.\n * - Usage logging: log every request as JSON line to ~/.openclaw/blockrun/logs/\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { createPaymentFetch, type PreAuthParams } from \"./x402.js\";\nimport { route, getFallbackChain, DEFAULT_ROUTING_CONFIG, type RouterOptions, type RoutingDecision, type RoutingConfig, type ModelPricing } from \"./router/index.js\";\nimport { BLOCKRUN_MODELS } from \"./models.js\";\nimport { logUsage, type UsageEntry } from \"./logger.js\";\nimport { RequestDeduplicator, type CachedResponse } from \"./dedup.js\";\n\nconst BLOCKRUN_API = \"https://blockrun.ai/api\";\nconst AUTO_MODEL = \"blockrun/auto\";\nconst USER_AGENT = \"clawrouter/0.3.0\";\nconst HEARTBEAT_INTERVAL_MS = 2_000;\n\nexport type ProxyOptions = {\n walletKey: string;\n apiBase?: string;\n port?: number;\n routingConfig?: Partial<RoutingConfig>;\n onReady?: (port: number) => void;\n onError?: (error: Error) => void;\n onPayment?: (info: { model: string; amount: string; network: string }) => void;\n onRouted?: (decision: RoutingDecision) => void;\n};\n\nexport type ProxyHandle = {\n port: number;\n baseUrl: string;\n close: () => Promise<void>;\n};\n\n/**\n * Build model pricing map from BLOCKRUN_MODELS.\n */\nfunction buildModelPricing(): Map<string, ModelPricing> {\n const map = new Map<string, ModelPricing>();\n for (const m of BLOCKRUN_MODELS) {\n if (m.id === AUTO_MODEL) continue; // skip meta-model\n map.set(m.id, { inputPrice: m.inputPrice, outputPrice: m.outputPrice });\n }\n return map;\n}\n\n/**\n * Merge partial routing config overrides with defaults.\n */\nfunction mergeRoutingConfig(overrides?: Partial<RoutingConfig>): RoutingConfig {\n if (!overrides) return DEFAULT_ROUTING_CONFIG;\n return {\n ...DEFAULT_ROUTING_CONFIG,\n ...overrides,\n classifier: { ...DEFAULT_ROUTING_CONFIG.classifier, ...overrides.classifier },\n scoring: { ...DEFAULT_ROUTING_CONFIG.scoring, ...overrides.scoring },\n tiers: { ...DEFAULT_ROUTING_CONFIG.tiers, ...overrides.tiers },\n overrides: { ...DEFAULT_ROUTING_CONFIG.overrides, ...overrides.overrides },\n };\n}\n\n/**\n * Estimate USDC cost for a request based on model pricing.\n * Returns amount string in USDC smallest unit (6 decimals) or undefined if unknown.\n */\nfunction estimateAmount(modelId: string, bodyLength: number, maxTokens: number): string | undefined {\n const model = BLOCKRUN_MODELS.find(m => m.id === modelId);\n if (!model) return undefined;\n\n // Rough estimate: ~4 chars per token for input\n const estimatedInputTokens = Math.ceil(bodyLength / 4);\n const estimatedOutputTokens = maxTokens || model.maxOutput || 4096;\n\n const costUsd =\n (estimatedInputTokens / 1_000_000) * model.inputPrice +\n (estimatedOutputTokens / 1_000_000) * model.outputPrice;\n\n // Convert to USDC 6-decimal integer, add 20% buffer for estimation error\n const amountMicros = Math.ceil(costUsd * 1.2 * 1_000_000);\n return amountMicros.toString();\n}\n\n/**\n * Start the local x402 proxy server.\n *\n * Returns a handle with the assigned port, base URL, and a close function.\n */\nexport async function startProxy(options: ProxyOptions): Promise<ProxyHandle> {\n const apiBase = options.apiBase ?? BLOCKRUN_API;\n\n // Create x402 payment-enabled fetch from wallet private key\n const account = privateKeyToAccount(options.walletKey as `0x${string}`);\n const { fetch: payFetch, cache: paymentCache } = createPaymentFetch(options.walletKey as `0x${string}`);\n\n // Build router options (pass the new payFetch signature — it accepts preAuth as 3rd arg)\n const routingConfig = mergeRoutingConfig(options.routingConfig);\n const modelPricing = buildModelPricing();\n const routerOpts: RouterOptions = {\n config: routingConfig,\n modelPricing,\n payFetch: (input, init) => payFetch(input, init), // router doesn't need preAuth\n apiBase,\n };\n\n // Request deduplicator (shared across all requests)\n const deduplicator = new RequestDeduplicator();\n\n const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Health check\n if (req.url === \"/health\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", wallet: account.address }));\n return;\n }\n\n // Only proxy paths starting with /v1\n if (!req.url?.startsWith(\"/v1\")) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n return;\n }\n\n try {\n await proxyRequest(req, res, apiBase, payFetch, options, routerOpts, deduplicator);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n options.onError?.(error);\n\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({\n error: { message: `Proxy error: ${error.message}`, type: \"proxy_error\" },\n }));\n } else if (!res.writableEnded) {\n // Headers already sent (streaming) — send error as SSE event\n res.write(`data: ${JSON.stringify({ error: { message: error.message, type: \"proxy_error\" } })}\\n\\n`);\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n }\n }\n });\n\n // Listen on requested port (0 = random available port)\n const listenPort = options.port ?? 0;\n\n return new Promise<ProxyHandle>((resolve, reject) => {\n server.on(\"error\", reject);\n\n server.listen(listenPort, \"127.0.0.1\", () => {\n const addr = server.address() as AddressInfo;\n const port = addr.port;\n const baseUrl = `http://127.0.0.1:${port}`;\n\n options.onReady?.(port);\n\n resolve({\n port,\n baseUrl,\n close: () =>\n new Promise<void>((res, rej) => {\n server.close((err) => (err ? rej(err) : res()));\n }),\n });\n });\n });\n}\n\n/**\n * Proxy a single request through x402 payment flow to BlockRun API.\n *\n * Optimizations applied in order:\n * 1. Dedup check — if same request body seen within 30s, replay cached response\n * 2. Streaming heartbeat — for stream:true, send 200 + heartbeats immediately\n * 3. Payment pre-auth — estimate USDC amount and pre-sign to skip 402 round trip\n * 4. Smart routing — when model is \"blockrun/auto\", pick cheapest capable model\n */\nasync function proxyRequest(\n req: IncomingMessage,\n res: ServerResponse,\n apiBase: string,\n payFetch: (input: RequestInfo | URL, init?: RequestInit, preAuth?: PreAuthParams) => Promise<Response>,\n options: ProxyOptions,\n routerOpts: RouterOptions,\n deduplicator: RequestDeduplicator,\n): Promise<void> {\n const startTime = Date.now();\n\n // Build upstream URL: /v1/chat/completions → https://blockrun.ai/api/v1/chat/completions\n const upstreamUrl = `${apiBase}${req.url}`;\n\n // Collect request body\n const bodyChunks: Buffer[] = [];\n for await (const chunk of req) {\n bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n let body = Buffer.concat(bodyChunks);\n\n // --- Smart routing ---\n let routingDecision: RoutingDecision | undefined;\n let isStreaming = false;\n let modelId = \"\";\n let maxTokens = 4096;\n const isChatCompletion = req.url?.includes(\"/chat/completions\");\n\n if (isChatCompletion && body.length > 0) {\n try {\n const parsed = JSON.parse(body.toString()) as Record<string, unknown>;\n isStreaming = parsed.stream === true;\n modelId = (parsed.model as string) || \"\";\n maxTokens = (parsed.max_tokens as number) || 4096;\n\n if (parsed.model === AUTO_MODEL) {\n // Extract prompt from messages\n type ChatMessage = { role: string; content: string };\n const messages = parsed.messages as ChatMessage[] | undefined;\n let lastUserMsg: ChatMessage | undefined;\n if (messages) {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === \"user\") { lastUserMsg = messages[i]; break; }\n }\n }\n const systemMsg = messages?.find((m: ChatMessage) => m.role === \"system\");\n const prompt = typeof lastUserMsg?.content === \"string\" ? lastUserMsg.content : \"\";\n const systemPrompt = typeof systemMsg?.content === \"string\" ? systemMsg.content : undefined;\n\n routingDecision = await route(prompt, systemPrompt, maxTokens, routerOpts);\n\n // Replace model in body\n parsed.model = routingDecision.model;\n modelId = routingDecision.model;\n body = Buffer.from(JSON.stringify(parsed));\n\n options.onRouted?.(routingDecision);\n }\n } catch {\n // JSON parse error — forward body as-is\n }\n }\n\n // --- Dedup check ---\n const dedupKey = RequestDeduplicator.hash(body);\n\n // Check completed cache first\n const cached = deduplicator.getCached(dedupKey);\n if (cached) {\n res.writeHead(cached.status, cached.headers);\n res.end(cached.body);\n return;\n }\n\n // Check in-flight — wait for the original request to complete\n const inflight = deduplicator.getInflight(dedupKey);\n if (inflight) {\n const result = await inflight;\n res.writeHead(result.status, result.headers);\n res.end(result.body);\n return;\n }\n\n // Register this request as in-flight\n deduplicator.markInflight(dedupKey);\n\n // --- Streaming: early header flush + heartbeat ---\n let heartbeatInterval: ReturnType<typeof setInterval> | undefined;\n let headersSentEarly = false;\n\n if (isStreaming) {\n // Send 200 + SSE headers immediately, before x402 flow\n res.writeHead(200, {\n \"content-type\": \"text/event-stream\",\n \"cache-control\": \"no-cache\",\n \"connection\": \"keep-alive\",\n });\n headersSentEarly = true;\n\n // First heartbeat immediately\n res.write(\": heartbeat\\n\\n\");\n\n // Continue heartbeats every 2s while waiting for upstream\n heartbeatInterval = setInterval(() => {\n if (!res.writableEnded) {\n res.write(\": heartbeat\\n\\n\");\n }\n }, HEARTBEAT_INTERVAL_MS);\n }\n\n // Forward headers, stripping host, connection, and content-length\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (key === \"host\" || key === \"connection\" || key === \"transfer-encoding\" || key === \"content-length\") continue;\n if (typeof value === \"string\") {\n headers[key] = value;\n }\n }\n if (!headers[\"content-type\"]) {\n headers[\"content-type\"] = \"application/json\";\n }\n headers[\"user-agent\"] = USER_AGENT;\n\n // --- Payment pre-auth: estimate amount to skip 402 round trip ---\n let preAuth: PreAuthParams | undefined;\n if (modelId) {\n const estimated = estimateAmount(modelId, body.length, maxTokens);\n if (estimated) {\n preAuth = { estimatedAmount: estimated };\n }\n }\n\n try {\n // Make the request through x402-wrapped fetch (with optional pre-auth)\n const upstream = await payFetch(upstreamUrl, {\n method: req.method ?? \"POST\",\n headers,\n body: body.length > 0 ? body : undefined,\n }, preAuth);\n\n // Clear heartbeat — real data is about to flow\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = undefined;\n }\n\n // --- Stream response and collect for dedup cache ---\n const responseChunks: Buffer[] = [];\n\n if (headersSentEarly) {\n // Streaming: headers already sent. Check for upstream errors.\n if (upstream.status !== 200) {\n const errBody = await upstream.text();\n const errEvent = `data: ${JSON.stringify({ error: { message: errBody, type: \"upstream_error\", status: upstream.status } })}\\n\\n`;\n res.write(errEvent);\n res.write(\"data: [DONE]\\n\\n\");\n res.end();\n\n // Cache the error response for dedup\n const errBuf = Buffer.from(errEvent + \"data: [DONE]\\n\\n\");\n deduplicator.complete(dedupKey, {\n status: 200, // we already sent 200\n headers: { \"content-type\": \"text/event-stream\" },\n body: errBuf,\n completedAt: Date.now(),\n });\n return;\n }\n\n // Pipe upstream SSE data to client\n if (upstream.body) {\n const reader = upstream.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n res.write(value);\n responseChunks.push(Buffer.from(value));\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n res.end();\n\n // Cache for dedup\n deduplicator.complete(dedupKey, {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n body: Buffer.concat(responseChunks),\n completedAt: Date.now(),\n });\n } else {\n // Non-streaming: forward status and headers from upstream\n const responseHeaders: Record<string, string> = {};\n upstream.headers.forEach((value, key) => {\n if (key === \"transfer-encoding\" || key === \"connection\") return;\n responseHeaders[key] = value;\n });\n\n res.writeHead(upstream.status, responseHeaders);\n\n if (upstream.body) {\n const reader = upstream.body.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n res.write(value);\n responseChunks.push(Buffer.from(value));\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n res.end();\n\n // Cache for dedup\n deduplicator.complete(dedupKey, {\n status: upstream.status,\n headers: responseHeaders,\n body: Buffer.concat(responseChunks),\n completedAt: Date.now(),\n });\n }\n } catch (err) {\n // Clear heartbeat on error\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n }\n\n // Remove in-flight entry so retries aren't blocked\n deduplicator.removeInflight(dedupKey);\n\n throw err;\n }\n\n // --- Usage logging (fire-and-forget) ---\n if (routingDecision) {\n const entry: UsageEntry = {\n timestamp: new Date().toISOString(),\n model: routingDecision.model,\n cost: routingDecision.costEstimate,\n latencyMs: Date.now() - startTime,\n };\n logUsage(entry).catch(() => {});\n }\n}\n","/**\n * x402 Payment Implementation\n *\n * Based on BlockRun's proven implementation.\n * Handles 402 Payment Required responses with EIP-712 signed USDC transfers.\n *\n * Optimizations (v0.3.0):\n * - Payment cache: after first 402, caches {payTo, asset, network} per endpoint.\n * On subsequent requests, pre-signs payment and sends with first request,\n * skipping the 402 round trip (~200ms savings).\n * - Falls back to normal 402 flow if pre-signed payment is rejected.\n */\n\nimport { signTypedData, privateKeyToAccount } from \"viem/accounts\";\nimport { PaymentCache, type CachedPaymentParams } from \"./payment-cache.js\";\n\nconst BASE_CHAIN_ID = 8453;\nconst USDC_BASE = \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\" as const;\n\nconst USDC_DOMAIN = {\n name: \"USD Coin\",\n version: \"2\",\n chainId: BASE_CHAIN_ID,\n verifyingContract: USDC_BASE,\n} as const;\n\nconst TRANSFER_TYPES = {\n TransferWithAuthorization: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n ],\n} as const;\n\nfunction createNonce(): `0x${string}` {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return `0x${Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')}` as `0x${string}`;\n}\n\ninterface PaymentOption {\n scheme: string;\n network: string;\n amount?: string;\n maxAmountRequired?: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds?: number;\n extra?: { name?: string; version?: string };\n}\n\ninterface PaymentRequired {\n accepts: PaymentOption[];\n resource?: { url?: string; description?: string };\n}\n\nfunction parsePaymentRequired(headerValue: string): PaymentRequired {\n const decoded = atob(headerValue);\n return JSON.parse(decoded) as PaymentRequired;\n}\n\nasync function createPaymentPayload(\n privateKey: `0x${string}`,\n fromAddress: string,\n recipient: string,\n amount: string,\n resourceUrl: string\n): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n const validAfter = now - 600;\n const validBefore = now + 300;\n const nonce = createNonce();\n\n const signature = await signTypedData({\n privateKey,\n domain: USDC_DOMAIN,\n types: TRANSFER_TYPES,\n primaryType: \"TransferWithAuthorization\",\n message: {\n from: fromAddress as `0x${string}`,\n to: recipient as `0x${string}`,\n value: BigInt(amount),\n validAfter: BigInt(validAfter),\n validBefore: BigInt(validBefore),\n nonce,\n },\n });\n\n const paymentData = {\n x402Version: 2,\n resource: {\n url: resourceUrl,\n description: \"BlockRun AI API call\",\n mimeType: \"application/json\",\n },\n accepted: {\n scheme: \"exact\",\n network: \"eip155:8453\",\n amount,\n asset: USDC_BASE,\n payTo: recipient,\n maxTimeoutSeconds: 300,\n extra: { name: \"USD Coin\", version: \"2\" },\n },\n payload: {\n signature,\n authorization: {\n from: fromAddress,\n to: recipient,\n value: amount,\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n },\n },\n extensions: {},\n };\n\n return btoa(JSON.stringify(paymentData));\n}\n\n/** Pre-auth parameters for skipping the 402 round trip. */\nexport type PreAuthParams = {\n estimatedAmount: string; // USDC amount in smallest unit (6 decimals)\n};\n\n/** Result from createPaymentFetch — includes the fetch wrapper and payment cache. */\nexport type PaymentFetchResult = {\n fetch: (input: RequestInfo | URL, init?: RequestInit, preAuth?: PreAuthParams) => Promise<Response>;\n cache: PaymentCache;\n};\n\n/**\n * Create a fetch wrapper that handles x402 payment automatically.\n *\n * Supports pre-auth: if cached payment params + estimated amount are available,\n * pre-signs and attaches payment to the first request, skipping the 402 round trip.\n * Falls back to normal 402 flow if pre-signed payment is rejected.\n */\nexport function createPaymentFetch(privateKey: `0x${string}`): PaymentFetchResult {\n const account = privateKeyToAccount(privateKey);\n const walletAddress = account.address;\n const paymentCache = new PaymentCache();\n\n const payFetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n preAuth?: PreAuthParams,\n ): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const endpointPath = new URL(url).pathname;\n\n // --- Pre-auth path: skip 402 round trip ---\n const cached = paymentCache.get(endpointPath);\n if (cached && preAuth?.estimatedAmount) {\n const paymentPayload = await createPaymentPayload(\n privateKey,\n walletAddress,\n cached.payTo,\n preAuth.estimatedAmount,\n url,\n );\n\n const preAuthHeaders = new Headers(init?.headers);\n preAuthHeaders.set(\"payment-signature\", paymentPayload);\n\n const response = await fetch(input, { ...init, headers: preAuthHeaders });\n\n // Pre-auth accepted — skip 402 entirely\n if (response.status !== 402) {\n return response;\n }\n\n // Pre-auth rejected (wrong amount, payTo changed, etc.)\n // Fall through to normal 402 flow using THIS 402 response\n const paymentHeader = response.headers.get(\"x-payment-required\");\n if (paymentHeader) {\n return handle402(input, init, url, endpointPath, paymentHeader);\n }\n // No payment header in rejection — return as-is\n return response;\n }\n\n // --- Normal path: first request may get 402 ---\n const response = await fetch(input, init);\n\n if (response.status !== 402) {\n return response;\n }\n\n const paymentHeader = response.headers.get(\"x-payment-required\");\n if (!paymentHeader) {\n throw new Error(\"402 response missing x-payment-required header\");\n }\n\n return handle402(input, init, url, endpointPath, paymentHeader);\n };\n\n /** Handle a 402 response: parse, cache params, sign, retry. */\n async function handle402(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n url: string,\n endpointPath: string,\n paymentHeader: string,\n ): Promise<Response> {\n const paymentRequired = parsePaymentRequired(paymentHeader);\n const option = paymentRequired.accepts?.[0];\n if (!option) {\n throw new Error(\"No payment options in 402 response\");\n }\n\n const amount = option.amount || option.maxAmountRequired;\n if (!amount) {\n throw new Error(\"No amount in payment requirements\");\n }\n\n // Cache payment params for future pre-auth\n paymentCache.set(endpointPath, {\n payTo: option.payTo,\n asset: option.asset,\n scheme: option.scheme,\n network: option.network,\n extra: option.extra,\n maxTimeoutSeconds: option.maxTimeoutSeconds,\n });\n\n // Create signed payment\n const paymentPayload = await createPaymentPayload(\n privateKey,\n walletAddress,\n option.payTo,\n amount,\n url,\n );\n\n // Retry with payment\n const retryHeaders = new Headers(init?.headers);\n retryHeaders.set(\"payment-signature\", paymentPayload);\n\n return fetch(input, {\n ...init,\n headers: retryHeaders,\n });\n }\n\n return { fetch: payFetch, cache: paymentCache };\n}\n","/**\n * Payment Parameter Cache\n *\n * Caches the 402 payment parameters (payTo, asset, network, etc.) after the first\n * request to each endpoint. On subsequent requests, pre-signs the payment and\n * attaches it to the first request, skipping the 402 round trip (~200ms savings).\n */\n\nexport type CachedPaymentParams = {\n payTo: string;\n asset: string;\n scheme: string;\n network: string;\n extra?: { name?: string; version?: string };\n maxTimeoutSeconds?: number;\n cachedAt: number;\n};\n\nconst DEFAULT_TTL_MS = 3_600_000; // 1 hour\n\nexport class PaymentCache {\n private cache = new Map<string, CachedPaymentParams>();\n private ttlMs: number;\n\n constructor(ttlMs = DEFAULT_TTL_MS) {\n this.ttlMs = ttlMs;\n }\n\n /** Get cached payment params for an endpoint path. */\n get(endpointPath: string): CachedPaymentParams | undefined {\n const entry = this.cache.get(endpointPath);\n if (!entry) return undefined;\n if (Date.now() - entry.cachedAt > this.ttlMs) {\n this.cache.delete(endpointPath);\n return undefined;\n }\n return entry;\n }\n\n /** Cache payment params from a 402 response. */\n set(endpointPath: string, params: Omit<CachedPaymentParams, \"cachedAt\">): void {\n this.cache.set(endpointPath, { ...params, cachedAt: Date.now() });\n }\n\n /** Invalidate cache for an endpoint (e.g., if payTo changed). */\n invalidate(endpointPath: string): void {\n this.cache.delete(endpointPath);\n }\n}\n","/**\n * Rule-Based Classifier (v2 — Weighted Scoring)\n *\n * Scores a request across 14 weighted dimensions and maps the aggregate\n * score to a tier using configurable boundaries. Confidence is calibrated\n * via sigmoid — low confidence triggers the fallback classifier.\n *\n * Handles 70-80% of requests in < 1ms with zero cost.\n */\n\nimport type { Tier, ScoringResult, ScoringConfig } from \"./types.js\";\n\ntype DimensionScore = { name: string; score: number; signal: string | null };\n\n// ─── Dimension Scorers ───\n// Each returns a score in [-1, 1] and an optional signal string.\n\nfunction scoreTokenCount(\n estimatedTokens: number,\n thresholds: { simple: number; complex: number },\n): DimensionScore {\n if (estimatedTokens < thresholds.simple) {\n return { name: \"tokenCount\", score: -1.0, signal: `short (${estimatedTokens} tokens)` };\n }\n if (estimatedTokens > thresholds.complex) {\n return { name: \"tokenCount\", score: 1.0, signal: `long (${estimatedTokens} tokens)` };\n }\n return { name: \"tokenCount\", score: 0, signal: null };\n}\n\nfunction scoreKeywordMatch(\n text: string,\n keywords: string[],\n name: string,\n signalLabel: string,\n thresholds: { low: number; high: number },\n scores: { none: number; low: number; high: number },\n): DimensionScore {\n const matches = keywords.filter((kw) => text.includes(kw.toLowerCase()));\n if (matches.length >= thresholds.high) {\n return { name, score: scores.high, signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})` };\n }\n if (matches.length >= thresholds.low) {\n return { name, score: scores.low, signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})` };\n }\n return { name, score: scores.none, signal: null };\n}\n\nfunction scoreMultiStep(text: string): DimensionScore {\n const patterns = [/first.*then/i, /step \\d/i, /\\d\\.\\s/];\n const hits = patterns.filter((p) => p.test(text));\n if (hits.length > 0) {\n return { name: \"multiStepPatterns\", score: 0.5, signal: \"multi-step\" };\n }\n return { name: \"multiStepPatterns\", score: 0, signal: null };\n}\n\nfunction scoreQuestionComplexity(prompt: string): DimensionScore {\n const count = (prompt.match(/\\?/g) || []).length;\n if (count > 3) {\n return { name: \"questionComplexity\", score: 0.5, signal: `${count} questions` };\n }\n return { name: \"questionComplexity\", score: 0, signal: null };\n}\n\n// ─── Main Classifier ───\n\nexport function classifyByRules(\n prompt: string,\n systemPrompt: string | undefined,\n estimatedTokens: number,\n config: ScoringConfig,\n): ScoringResult {\n const text = `${systemPrompt ?? \"\"} ${prompt}`.toLowerCase();\n\n // Score all 14 dimensions\n const dimensions: DimensionScore[] = [\n // Original 8 dimensions\n scoreTokenCount(estimatedTokens, config.tokenCountThresholds),\n scoreKeywordMatch(text, config.codeKeywords, \"codePresence\", \"code\",\n { low: 1, high: 2 }, { none: 0, low: 0.5, high: 1.0 }),\n scoreKeywordMatch(text, config.reasoningKeywords, \"reasoningMarkers\", \"reasoning\",\n { low: 1, high: 2 }, { none: 0, low: 0.7, high: 1.0 }),\n scoreKeywordMatch(text, config.technicalKeywords, \"technicalTerms\", \"technical\",\n { low: 2, high: 4 }, { none: 0, low: 0.5, high: 1.0 }),\n scoreKeywordMatch(text, config.creativeKeywords, \"creativeMarkers\", \"creative\",\n { low: 1, high: 2 }, { none: 0, low: 0.5, high: 0.7 }),\n scoreKeywordMatch(text, config.simpleKeywords, \"simpleIndicators\", \"simple\",\n { low: 1, high: 2 }, { none: 0, low: -1.0, high: -1.0 }),\n scoreMultiStep(text),\n scoreQuestionComplexity(prompt),\n\n // 6 new dimensions\n scoreKeywordMatch(text, config.imperativeVerbs, \"imperativeVerbs\", \"imperative\",\n { low: 1, high: 2 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(text, config.constraintIndicators, \"constraintCount\", \"constraints\",\n { low: 1, high: 3 }, { none: 0, low: 0.3, high: 0.7 }),\n scoreKeywordMatch(text, config.outputFormatKeywords, \"outputFormat\", \"format\",\n { low: 1, high: 2 }, { none: 0, low: 0.4, high: 0.7 }),\n scoreKeywordMatch(text, config.referenceKeywords, \"referenceComplexity\", \"references\",\n { low: 1, high: 2 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(text, config.negationKeywords, \"negationComplexity\", \"negation\",\n { low: 2, high: 3 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(text, config.domainSpecificKeywords, \"domainSpecificity\", \"domain-specific\",\n { low: 1, high: 2 }, { none: 0, low: 0.5, high: 0.8 }),\n ];\n\n // Collect signals\n const signals = dimensions\n .filter((d) => d.signal !== null)\n .map((d) => d.signal!);\n\n // Compute weighted score\n const weights = config.dimensionWeights;\n let weightedScore = 0;\n for (const d of dimensions) {\n const w = weights[d.name] ?? 0;\n weightedScore += d.score * w;\n }\n\n // Count reasoning markers for override\n const reasoningMatches = config.reasoningKeywords.filter((kw) =>\n text.includes(kw.toLowerCase()),\n );\n\n // Direct reasoning override: 2+ reasoning markers = high confidence REASONING\n if (reasoningMatches.length >= 2) {\n const confidence = calibrateConfidence(\n Math.max(weightedScore, 0.3), // ensure positive for confidence calc\n config.confidenceSteepness,\n );\n return {\n score: weightedScore,\n tier: \"REASONING\",\n confidence: Math.max(confidence, 0.85),\n signals,\n };\n }\n\n // Map weighted score to tier using boundaries\n const { simpleMedium, mediumComplex, complexReasoning } = config.tierBoundaries;\n let tier: Tier;\n let distanceFromBoundary: number;\n\n if (weightedScore < simpleMedium) {\n tier = \"SIMPLE\";\n distanceFromBoundary = simpleMedium - weightedScore;\n } else if (weightedScore < mediumComplex) {\n tier = \"MEDIUM\";\n distanceFromBoundary = Math.min(\n weightedScore - simpleMedium,\n mediumComplex - weightedScore,\n );\n } else if (weightedScore < complexReasoning) {\n tier = \"COMPLEX\";\n distanceFromBoundary = Math.min(\n weightedScore - mediumComplex,\n complexReasoning - weightedScore,\n );\n } else {\n tier = \"REASONING\";\n distanceFromBoundary = weightedScore - complexReasoning;\n }\n\n // Calibrate confidence via sigmoid of distance from nearest boundary\n const confidence = calibrateConfidence(distanceFromBoundary, config.confidenceSteepness);\n\n // If confidence is below threshold → ambiguous\n if (confidence < config.confidenceThreshold) {\n return { score: weightedScore, tier: null, confidence, signals };\n }\n\n return { score: weightedScore, tier, confidence, signals };\n}\n\n/**\n * Sigmoid confidence calibration.\n * Maps distance from tier boundary to [0.5, 1.0] confidence range.\n */\nfunction calibrateConfidence(distance: number, steepness: number): number {\n return 1 / (1 + Math.exp(-steepness * distance));\n}\n","/**\n * LLM Classifier (Fallback)\n *\n * When the rule-based classifier returns ambiguous (score 1-2),\n * we send a classification request to the cheapest model.\n *\n * Cost per classification: ~$0.00003\n * Latency: ~200-400ms\n * Only triggered for ~20-30% of requests.\n */\n\nimport type { Tier } from \"./types.js\";\n\nconst CLASSIFIER_PROMPT = `You are a query complexity classifier. Classify the user's query into exactly one category.\n\nCategories:\n- SIMPLE: Factual Q&A, definitions, translations, short answers\n- MEDIUM: Summaries, explanations, moderate code generation\n- COMPLEX: Multi-step code, system design, creative writing, analysis\n- REASONING: Mathematical proofs, formal logic, step-by-step problem solving\n\nRespond with ONLY one word: SIMPLE, MEDIUM, COMPLEX, or REASONING.`;\n\n// In-memory cache: hash → { tier, expires }\nconst cache = new Map<string, { tier: Tier; expires: number }>();\n\nexport type LLMClassifierConfig = {\n model: string;\n maxTokens: number;\n temperature: number;\n truncationChars: number;\n cacheTtlMs: number;\n};\n\ntype PayFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Classify a prompt using a cheap LLM.\n * Returns tier and confidence. Defaults to MEDIUM on any failure.\n */\nexport async function classifyByLLM(\n prompt: string,\n config: LLMClassifierConfig,\n payFetch: PayFetch,\n apiBase: string,\n): Promise<{ tier: Tier; confidence: number }> {\n const truncated = prompt.slice(0, config.truncationChars);\n\n // Check cache\n const cacheKey = simpleHash(truncated);\n const cached = cache.get(cacheKey);\n if (cached && cached.expires > Date.now()) {\n return { tier: cached.tier, confidence: 0.75 };\n }\n\n try {\n const response = await payFetch(`${apiBase}/v1/chat/completions`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model: config.model,\n messages: [\n { role: \"system\", content: CLASSIFIER_PROMPT },\n { role: \"user\", content: truncated },\n ],\n max_tokens: config.maxTokens,\n temperature: config.temperature,\n stream: false,\n }),\n });\n\n if (!response.ok) {\n return { tier: \"MEDIUM\", confidence: 0.5 };\n }\n\n const data = (await response.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n const content = data.choices?.[0]?.message?.content?.trim().toUpperCase() ?? \"\";\n const tier = parseTier(content);\n\n // Cache result\n cache.set(cacheKey, { tier, expires: Date.now() + config.cacheTtlMs });\n\n // Prune if cache grows too large\n if (cache.size > 1000) {\n pruneCache();\n }\n\n return { tier, confidence: 0.75 };\n } catch {\n // Any error → safe default\n return { tier: \"MEDIUM\", confidence: 0.5 };\n }\n}\n\n/**\n * Parse tier from LLM response. Handles \"SIMPLE\", \"The query is SIMPLE\", etc.\n */\nfunction parseTier(text: string): Tier {\n if (/\\bREASONING\\b/.test(text)) return \"REASONING\";\n if (/\\bCOMPLEX\\b/.test(text)) return \"COMPLEX\";\n if (/\\bMEDIUM\\b/.test(text)) return \"MEDIUM\";\n if (/\\bSIMPLE\\b/.test(text)) return \"SIMPLE\";\n return \"MEDIUM\"; // safe default\n}\n\nfunction simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash |= 0;\n }\n return hash.toString(36);\n}\n\nfunction pruneCache(): void {\n const now = Date.now();\n for (const [key, value] of cache) {\n if (value.expires <= now) {\n cache.delete(key);\n }\n }\n}\n","/**\n * Tier → Model Selection\n *\n * Maps a classification tier to the cheapest capable model.\n * Builds RoutingDecision metadata with cost estimates and savings.\n */\n\nimport type { Tier, TierConfig, RoutingDecision } from \"./types.js\";\n\nexport type ModelPricing = {\n inputPrice: number; // per 1M tokens\n outputPrice: number; // per 1M tokens\n};\n\n/**\n * Select the primary model for a tier and build the RoutingDecision.\n */\nexport function selectModel(\n tier: Tier,\n confidence: number,\n method: \"rules\" | \"llm\",\n reasoning: string,\n tierConfigs: Record<Tier, TierConfig>,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n): RoutingDecision {\n const tierConfig = tierConfigs[tier];\n const model = tierConfig.primary;\n const pricing = modelPricing.get(model);\n\n const inputCost = pricing\n ? (estimatedInputTokens / 1_000_000) * pricing.inputPrice\n : 0;\n const outputCost = pricing\n ? (maxOutputTokens / 1_000_000) * pricing.outputPrice\n : 0;\n const costEstimate = inputCost + outputCost;\n\n // Baseline: what Claude Opus would cost (the premium default)\n const opusPricing = modelPricing.get(\"anthropic/claude-opus-4\");\n const baselineInput = opusPricing\n ? (estimatedInputTokens / 1_000_000) * opusPricing.inputPrice\n : 0;\n const baselineOutput = opusPricing\n ? (maxOutputTokens / 1_000_000) * opusPricing.outputPrice\n : 0;\n const baselineCost = baselineInput + baselineOutput;\n\n const savings =\n baselineCost > 0\n ? Math.max(0, (baselineCost - costEstimate) / baselineCost)\n : 0;\n\n return {\n model,\n tier,\n confidence,\n method,\n reasoning,\n costEstimate,\n baselineCost,\n savings,\n };\n}\n\n/**\n * Get the ordered fallback chain for a tier: [primary, ...fallbacks].\n */\nexport function getFallbackChain(\n tier: Tier,\n tierConfigs: Record<Tier, TierConfig>,\n): string[] {\n const config = tierConfigs[tier];\n return [config.primary, ...config.fallback];\n}\n","/**\n * Default Routing Config\n *\n * All routing parameters as a TypeScript constant.\n * Operators override via openclaw.yaml plugin config.\n *\n * Scoring uses 14 weighted dimensions with sigmoid confidence calibration.\n */\n\nimport type { RoutingConfig } from \"./types.js\";\n\nexport const DEFAULT_ROUTING_CONFIG: RoutingConfig = {\n version: \"2.0\",\n\n classifier: {\n llmModel: \"google/gemini-2.5-flash\",\n llmMaxTokens: 10,\n llmTemperature: 0,\n promptTruncationChars: 500,\n cacheTtlMs: 3_600_000, // 1 hour\n },\n\n scoring: {\n tokenCountThresholds: { simple: 50, complex: 500 },\n codeKeywords: [\n \"function\", \"class\", \"import\", \"def\", \"SELECT\", \"async\", \"await\",\n \"const\", \"let\", \"var\", \"return\", \"```\",\n ],\n reasoningKeywords: [\n \"prove\", \"theorem\", \"derive\", \"step by step\", \"chain of thought\",\n \"formally\", \"mathematical\", \"proof\", \"logically\",\n ],\n simpleKeywords: [\n \"what is\", \"define\", \"translate\", \"hello\", \"yes or no\",\n \"capital of\", \"how old\", \"who is\", \"when was\",\n ],\n technicalKeywords: [\n \"algorithm\", \"optimize\", \"architecture\", \"distributed\",\n \"kubernetes\", \"microservice\", \"database\", \"infrastructure\",\n ],\n creativeKeywords: [\n \"story\", \"poem\", \"compose\", \"brainstorm\", \"creative\",\n \"imagine\", \"write a\",\n ],\n\n // New dimension keyword lists\n imperativeVerbs: [\n \"build\", \"create\", \"implement\", \"design\", \"develop\", \"construct\",\n \"generate\", \"deploy\", \"configure\", \"set up\",\n ],\n constraintIndicators: [\n \"under\", \"at most\", \"at least\", \"within\", \"no more than\",\n \"o(\", \"maximum\", \"minimum\", \"limit\", \"budget\",\n ],\n outputFormatKeywords: [\n \"json\", \"yaml\", \"xml\", \"table\", \"csv\", \"markdown\",\n \"schema\", \"format as\", \"structured\",\n ],\n referenceKeywords: [\n \"above\", \"below\", \"previous\", \"following\", \"the docs\",\n \"the api\", \"the code\", \"earlier\", \"attached\",\n ],\n negationKeywords: [\n \"don't\", \"do not\", \"avoid\", \"never\", \"without\",\n \"except\", \"exclude\", \"no longer\",\n ],\n domainSpecificKeywords: [\n \"quantum\", \"fpga\", \"vlsi\", \"risc-v\", \"asic\", \"photonics\",\n \"genomics\", \"proteomics\", \"topological\", \"homomorphic\",\n \"zero-knowledge\", \"lattice-based\",\n ],\n\n // Dimension weights (sum to 1.0)\n dimensionWeights: {\n tokenCount: 0.08,\n codePresence: 0.15,\n reasoningMarkers: 0.18,\n technicalTerms: 0.10,\n creativeMarkers: 0.05,\n simpleIndicators: 0.12,\n multiStepPatterns: 0.12,\n questionComplexity: 0.05,\n imperativeVerbs: 0.03,\n constraintCount: 0.04,\n outputFormat: 0.03,\n referenceComplexity: 0.02,\n negationComplexity: 0.01,\n domainSpecificity: 0.02,\n },\n\n // Tier boundaries on weighted score axis\n tierBoundaries: {\n simpleMedium: 0.0,\n mediumComplex: 0.15,\n complexReasoning: 0.25,\n },\n\n // Sigmoid steepness for confidence calibration\n confidenceSteepness: 12,\n // Below this confidence → ambiguous (null tier)\n confidenceThreshold: 0.70,\n },\n\n tiers: {\n SIMPLE: {\n primary: \"google/gemini-2.5-flash\",\n fallback: [\"deepseek/deepseek-chat\", \"openai/gpt-4o-mini\"],\n },\n MEDIUM: {\n primary: \"deepseek/deepseek-chat\",\n fallback: [\"google/gemini-2.5-flash\", \"openai/gpt-4o-mini\"],\n },\n COMPLEX: {\n primary: \"anthropic/claude-opus-4\",\n fallback: [\"anthropic/claude-sonnet-4\", \"openai/gpt-4o\"],\n },\n REASONING: {\n primary: \"openai/o3\",\n fallback: [\"google/gemini-2.5-pro\", \"anthropic/claude-sonnet-4\"],\n },\n },\n\n overrides: {\n maxTokensForceComplex: 100_000,\n structuredOutputMinTier: \"MEDIUM\",\n },\n};\n","/**\n * Smart Router Entry Point\n *\n * Classifies requests and routes to the cheapest capable model.\n * Uses hybrid approach: rules first (< 1ms), LLM fallback for ambiguous cases.\n */\n\nimport type { Tier, RoutingDecision, RoutingConfig } from \"./types.js\";\nimport { classifyByRules } from \"./rules.js\";\nimport { classifyByLLM } from \"./llm-classifier.js\";\nimport { selectModel, getFallbackChain, type ModelPricing } from \"./selector.js\";\n\nexport type RouterOptions = {\n config: RoutingConfig;\n modelPricing: Map<string, ModelPricing>;\n payFetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;\n apiBase: string;\n};\n\n/**\n * Route a request to the cheapest capable model.\n *\n * 1. Check overrides (large context, structured output)\n * 2. Run rule-based classifier\n * 3. If ambiguous, run LLM classifier\n * 4. Select model for tier\n * 5. Return RoutingDecision with metadata\n */\nexport async function route(\n prompt: string,\n systemPrompt: string | undefined,\n maxOutputTokens: number,\n options: RouterOptions,\n): Promise<RoutingDecision> {\n const { config, modelPricing, payFetch, apiBase } = options;\n\n // Estimate input tokens (~4 chars per token)\n const fullText = `${systemPrompt ?? \"\"} ${prompt}`;\n const estimatedTokens = Math.ceil(fullText.length / 4);\n\n // --- Override: large context → force COMPLEX ---\n if (estimatedTokens > config.overrides.maxTokensForceComplex) {\n return selectModel(\n \"COMPLEX\",\n 0.95,\n \"rules\",\n `Input exceeds ${config.overrides.maxTokensForceComplex} tokens`,\n config.tiers,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n );\n }\n\n // Structured output detection\n const hasStructuredOutput = systemPrompt\n ? /json|structured|schema/i.test(systemPrompt)\n : false;\n\n // --- Rule-based classification ---\n const ruleResult = classifyByRules(\n prompt,\n systemPrompt,\n estimatedTokens,\n config.scoring,\n );\n\n let tier: Tier;\n let confidence: number;\n let method: \"rules\" | \"llm\" = \"rules\";\n let reasoning = `score=${ruleResult.score} | ${ruleResult.signals.join(\", \")}`;\n\n if (ruleResult.tier !== null) {\n tier = ruleResult.tier;\n confidence = ruleResult.confidence;\n } else {\n // Ambiguous — LLM classifier fallback\n const llmResult = await classifyByLLM(\n prompt,\n {\n model: config.classifier.llmModel,\n maxTokens: config.classifier.llmMaxTokens,\n temperature: config.classifier.llmTemperature,\n truncationChars: config.classifier.promptTruncationChars,\n cacheTtlMs: config.classifier.cacheTtlMs,\n },\n payFetch,\n apiBase,\n );\n\n tier = llmResult.tier;\n confidence = llmResult.confidence;\n method = \"llm\";\n reasoning += ` | ambiguous -> LLM: ${tier}`;\n }\n\n // Apply structured output minimum tier\n if (hasStructuredOutput) {\n const tierRank: Record<Tier, number> = { SIMPLE: 0, MEDIUM: 1, COMPLEX: 2, REASONING: 3 };\n const minTier = config.overrides.structuredOutputMinTier;\n if (tierRank[tier] < tierRank[minTier]) {\n reasoning += ` | upgraded to ${minTier} (structured output)`;\n tier = minTier;\n }\n }\n\n return selectModel(\n tier,\n confidence,\n method,\n reasoning,\n config.tiers,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n );\n}\n\nexport { getFallbackChain } from \"./selector.js\";\nexport { DEFAULT_ROUTING_CONFIG } from \"./config.js\";\nexport type { RoutingDecision, Tier, RoutingConfig } from \"./types.js\";\nexport type { ModelPricing } from \"./selector.js\";\n","/**\n * Usage Logger\n *\n * Logs every LLM request as a JSON line to a daily log file.\n * Files: ~/.openclaw/blockrun/logs/usage-YYYY-MM-DD.jsonl\n *\n * MVP: append-only JSON lines. No rotation, no cleanup.\n * Logging never breaks the request flow — all errors are swallowed.\n */\n\nimport { appendFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport type UsageEntry = {\n timestamp: string;\n model: string;\n cost: number;\n latencyMs: number;\n};\n\nconst LOG_DIR = join(homedir(), \".openclaw\", \"blockrun\", \"logs\");\nlet dirReady = false;\n\nasync function ensureDir(): Promise<void> {\n if (dirReady) return;\n await mkdir(LOG_DIR, { recursive: true });\n dirReady = true;\n}\n\n/**\n * Log a usage entry as a JSON line.\n */\nexport async function logUsage(entry: UsageEntry): Promise<void> {\n try {\n await ensureDir();\n const date = entry.timestamp.slice(0, 10); // YYYY-MM-DD\n const file = join(LOG_DIR, `usage-${date}.jsonl`);\n await appendFile(file, JSON.stringify(entry) + \"\\n\");\n } catch {\n // Never break the request flow\n }\n}\n","/**\n * Request Deduplication\n *\n * Prevents double-charging when OpenClaw retries a request after timeout.\n * Tracks in-flight requests and caches completed responses for a short TTL.\n */\n\nimport { createHash } from \"node:crypto\";\n\nexport type CachedResponse = {\n status: number;\n headers: Record<string, string>;\n body: Buffer;\n completedAt: number;\n};\n\ntype InflightEntry = {\n resolve: (result: CachedResponse) => void;\n waiters: Promise<CachedResponse>[];\n};\n\nconst DEFAULT_TTL_MS = 30_000; // 30 seconds\nconst MAX_BODY_SIZE = 1_048_576; // 1MB\n\nexport class RequestDeduplicator {\n private inflight = new Map<string, InflightEntry>();\n private completed = new Map<string, CachedResponse>();\n private ttlMs: number;\n\n constructor(ttlMs = DEFAULT_TTL_MS) {\n this.ttlMs = ttlMs;\n }\n\n /** Hash request body to create a dedup key. */\n static hash(body: Buffer): string {\n return createHash(\"sha256\").update(body).digest(\"hex\").slice(0, 16);\n }\n\n /** Check if a response is cached for this key. */\n getCached(key: string): CachedResponse | undefined {\n const entry = this.completed.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.completedAt > this.ttlMs) {\n this.completed.delete(key);\n return undefined;\n }\n return entry;\n }\n\n /** Check if a request with this key is currently in-flight. Returns a promise to wait on. */\n getInflight(key: string): Promise<CachedResponse> | undefined {\n const entry = this.inflight.get(key);\n if (!entry) return undefined;\n const promise = new Promise<CachedResponse>((resolve) => {\n // Will be resolved when the original request completes\n entry.waiters.push(new Promise<CachedResponse>((r) => {\n const orig = entry.resolve;\n entry.resolve = (result) => {\n orig(result);\n resolve(result);\n r(result);\n };\n }));\n });\n return promise;\n }\n\n /** Mark a request as in-flight. */\n markInflight(key: string): void {\n this.inflight.set(key, {\n resolve: () => {},\n waiters: [],\n });\n }\n\n /** Complete an in-flight request — cache result and notify waiters. */\n complete(key: string, result: CachedResponse): void {\n // Only cache responses within size limit\n if (result.body.length <= MAX_BODY_SIZE) {\n this.completed.set(key, result);\n }\n\n const entry = this.inflight.get(key);\n if (entry) {\n entry.resolve(result);\n this.inflight.delete(key);\n }\n\n this.prune();\n }\n\n /** Remove an in-flight entry on error (don't cache failures). */\n removeInflight(key: string): void {\n this.inflight.delete(key);\n }\n\n /** Prune expired completed entries. */\n private prune(): void {\n const now = Date.now();\n for (const [key, entry] of this.completed) {\n if (now - entry.completedAt > this.ttlMs) {\n this.completed.delete(key);\n }\n }\n }\n}\n","/**\n * @blockrun/clawrouter\n *\n * Smart LLM router for OpenClaw — 30+ models, x402 micropayments, 78% cost savings.\n * Routes each request to the cheapest model that can handle it.\n *\n * Usage:\n * # Install the plugin\n * openclaw plugin install @blockrun/clawrouter\n *\n * # Fund your wallet with USDC on Base (address printed on install)\n *\n * # Use smart routing (auto-picks cheapest model)\n * openclaw config set model blockrun/auto\n *\n * # Or use any specific BlockRun model\n * openclaw config set model openai/gpt-5.2\n */\n\nimport type { OpenClawPluginDefinition, OpenClawPluginApi } from \"./types.js\";\nimport { blockrunProvider, setActiveProxy } from \"./provider.js\";\nimport { startProxy } from \"./proxy.js\";\nimport { resolveOrGenerateWalletKey } from \"./auth.js\";\nimport type { RoutingConfig } from \"./router/index.js\";\n\n/**\n * Start the x402 proxy in the background.\n * Called from register() because OpenClaw's loader only invokes register(),\n * treating activate() as an alias (def.register ?? def.activate).\n */\nasync function startProxyInBackground(api: OpenClawPluginApi): Promise<void> {\n // Resolve wallet key: saved file → env var → auto-generate\n const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();\n\n // Log wallet source\n if (source === \"generated\") {\n api.logger.info(`Generated new wallet: ${address}`);\n api.logger.info(`Fund with USDC on Base to start using ClawRouter.`);\n } else if (source === \"saved\") {\n api.logger.info(`Using saved wallet: ${address}`);\n } else {\n api.logger.info(`Using wallet from BLOCKRUN_WALLET_KEY: ${address}`);\n }\n\n // Resolve routing config overrides from plugin config\n const routingConfig = api.pluginConfig?.routing as Partial<RoutingConfig> | undefined;\n\n const proxy = await startProxy({\n walletKey,\n routingConfig,\n onReady: (port) => {\n api.logger.info(`BlockRun x402 proxy listening on port ${port}`);\n },\n onError: (error) => {\n api.logger.error(`BlockRun proxy error: ${error.message}`);\n },\n onRouted: (decision) => {\n const cost = decision.costEstimate.toFixed(4);\n const saved = (decision.savings * 100).toFixed(0);\n api.logger.info(`${decision.model} $${cost} (saved ${saved}%)`);\n },\n });\n\n setActiveProxy(proxy);\n api.logger.info(`BlockRun provider active — ${proxy.baseUrl}/v1 (smart routing enabled)`);\n}\n\nconst plugin: OpenClawPluginDefinition = {\n id: \"clawrouter\",\n name: \"ClawRouter\",\n description: \"Smart LLM router — 30+ models, x402 micropayments, 78% cost savings\",\n version: \"0.3.0\",\n\n register(api: OpenClawPluginApi) {\n // Register BlockRun as a provider (sync — available immediately)\n api.registerProvider(blockrunProvider);\n api.logger.info(\"BlockRun provider registered (30+ models via x402)\");\n\n // Start x402 proxy in background (fire-and-forget)\n // OpenClaw only calls register(), not activate() — so all init goes here.\n // The loader ignores async returns, but the proxy starts in the background\n // and setActiveProxy() makes it available to the provider once ready.\n startProxyInBackground(api).catch((err) => {\n api.logger.error(\n `Failed to start BlockRun proxy: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n },\n};\n\n\nexport default plugin;\n\n// Re-export for programmatic use\nexport { startProxy } from \"./proxy.js\";\nexport { blockrunProvider } from \"./provider.js\";\nexport { OPENCLAW_MODELS, BLOCKRUN_MODELS, buildProviderModels } from \"./models.js\";\nexport { route, DEFAULT_ROUTING_CONFIG } from \"./router/index.js\";\nexport type { RoutingDecision, RoutingConfig, Tier } from \"./router/index.js\";\nexport { logUsage } from \"./logger.js\";\nexport type { UsageEntry } from \"./logger.js\";\nexport { RequestDeduplicator } from \"./dedup.js\";\nexport type { CachedResponse } from \"./dedup.js\";\nexport { PaymentCache } from \"./payment-cache.js\";\nexport type { CachedPaymentParams } from \"./payment-cache.js\";\nexport { createPaymentFetch } from \"./x402.js\";\nexport type { PreAuthParams, PaymentFetchResult } from \"./x402.js\";\n"],"mappings":"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","names":["privateKeyToAccount","privateKeyToAccount","privateKeyToAccount","response","paymentHeader","confidence","mkdir","join","homedir","DEFAULT_TTL_MS","privateKeyToAccount"]}
|