@blockrun/clawrouter 0.12.94 → 0.12.96
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/auth.d.ts +82 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +388 -0
- package/dist/auth.js.map +1 -0
- package/dist/balance.d.ts +97 -0
- package/dist/balance.d.ts.map +1 -0
- package/dist/balance.js +157 -0
- package/dist/balance.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +256 -54991
- package/dist/cli.js.map +1 -1
- package/dist/compression/codebook.d.ts +24 -0
- package/dist/compression/codebook.d.ts.map +1 -0
- package/dist/compression/codebook.js +119 -0
- package/dist/compression/codebook.js.map +1 -0
- package/dist/compression/index.d.ts +33 -0
- package/dist/compression/index.d.ts.map +1 -0
- package/dist/compression/index.js +246 -0
- package/dist/compression/index.js.map +1 -0
- package/dist/compression/layers/deduplication.d.ts +28 -0
- package/dist/compression/layers/deduplication.d.ts.map +1 -0
- package/dist/compression/layers/deduplication.js +101 -0
- package/dist/compression/layers/deduplication.js.map +1 -0
- package/dist/compression/layers/dictionary.d.ts +21 -0
- package/dist/compression/layers/dictionary.d.ts.map +1 -0
- package/dist/compression/layers/dictionary.js +73 -0
- package/dist/compression/layers/dictionary.js.map +1 -0
- package/dist/compression/layers/dynamic-codebook.d.ts +26 -0
- package/dist/compression/layers/dynamic-codebook.d.ts.map +1 -0
- package/dist/compression/layers/dynamic-codebook.js +149 -0
- package/dist/compression/layers/dynamic-codebook.js.map +1 -0
- package/dist/compression/layers/json-compact.d.ts +23 -0
- package/dist/compression/layers/json-compact.d.ts.map +1 -0
- package/dist/compression/layers/json-compact.js +79 -0
- package/dist/compression/layers/json-compact.js.map +1 -0
- package/dist/compression/layers/observation.d.ts +21 -0
- package/dist/compression/layers/observation.d.ts.map +1 -0
- package/dist/compression/layers/observation.js +130 -0
- package/dist/compression/layers/observation.js.map +1 -0
- package/dist/compression/layers/paths.d.ts +24 -0
- package/dist/compression/layers/paths.d.ts.map +1 -0
- package/dist/compression/layers/paths.js +110 -0
- package/dist/compression/layers/paths.js.map +1 -0
- package/dist/compression/layers/whitespace.d.ts +27 -0
- package/dist/compression/layers/whitespace.d.ts.map +1 -0
- package/dist/compression/layers/whitespace.js +60 -0
- package/dist/compression/layers/whitespace.js.map +1 -0
- package/dist/compression/types.d.ts +84 -0
- package/dist/compression/types.d.ts.map +1 -0
- package/dist/compression/types.js +27 -0
- package/dist/compression/types.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +22 -0
- package/dist/config.js.map +1 -0
- package/dist/dedup.d.ts +34 -0
- package/dist/dedup.d.ts.map +1 -0
- package/dist/dedup.js +150 -0
- package/dist/dedup.js.map +1 -0
- package/dist/doctor.d.ts +8 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +375 -0
- package/dist/doctor.js.map +1 -0
- package/dist/error-classification.test.d.ts +2 -0
- package/dist/error-classification.test.d.ts.map +1 -0
- package/dist/error-classification.test.js +59 -0
- package/dist/error-classification.test.js.map +1 -0
- package/dist/errors.d.ts +54 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +85 -0
- package/dist/errors.js.map +1 -0
- package/dist/exclude-models.d.ts +28 -0
- package/dist/exclude-models.d.ts.map +1 -0
- package/dist/exclude-models.integration.test.d.ts +2 -0
- package/dist/exclude-models.integration.test.d.ts.map +1 -0
- package/dist/exclude-models.integration.test.js +46 -0
- package/dist/exclude-models.integration.test.js.map +1 -0
- package/dist/exclude-models.js +70 -0
- package/dist/exclude-models.js.map +1 -0
- package/dist/exclude-models.test.d.ts +5 -0
- package/dist/exclude-models.test.d.ts.map +1 -0
- package/dist/exclude-models.test.js +95 -0
- package/dist/exclude-models.test.js.map +1 -0
- package/dist/fs-read.d.ts +11 -0
- package/dist/fs-read.d.ts.map +1 -0
- package/dist/fs-read.js +47 -0
- package/dist/fs-read.js.map +1 -0
- package/dist/index.d.ts +34 -1368
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1071 -56191
- package/dist/index.js.map +1 -1
- package/dist/journal.d.ts +67 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +184 -0
- package/dist/journal.js.map +1 -0
- package/dist/journal.test.d.ts +2 -0
- package/dist/journal.test.d.ts.map +1 -0
- package/dist/journal.test.js +201 -0
- package/dist/journal.test.js.map +1 -0
- package/dist/logger.d.ts +33 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +35 -0
- package/dist/logger.js.map +1 -0
- package/dist/models.d.ts +107 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +941 -0
- package/dist/models.js.map +1 -0
- package/dist/models.test.d.ts +2 -0
- package/dist/models.test.d.ts.map +1 -0
- package/dist/models.test.js +38 -0
- package/dist/models.test.js.map +1 -0
- package/dist/partners/index.d.ts +8 -0
- package/dist/partners/index.d.ts.map +1 -0
- package/dist/partners/index.js +6 -0
- package/dist/partners/index.js.map +1 -0
- package/dist/partners/registry.d.ts +51 -0
- package/dist/partners/registry.d.ts.map +1 -0
- package/dist/partners/registry.js +256 -0
- package/dist/partners/registry.js.map +1 -0
- package/dist/partners/tools.d.ts +24 -0
- package/dist/partners/tools.d.ts.map +1 -0
- package/dist/partners/tools.js +97 -0
- package/dist/partners/tools.js.map +1 -0
- package/dist/payment-preauth.d.ts +17 -0
- package/dist/payment-preauth.d.ts.map +1 -0
- package/dist/payment-preauth.js +102 -0
- package/dist/payment-preauth.js.map +1 -0
- package/dist/provider.d.ts +19 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +45 -0
- package/dist/provider.js.map +1 -0
- package/dist/proxy.d.ts +210 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.debrand.test.d.ts +2 -0
- package/dist/proxy.debrand.test.d.ts.map +1 -0
- package/dist/proxy.debrand.test.js +101 -0
- package/dist/proxy.debrand.test.js.map +1 -0
- package/dist/proxy.degraded-response.test.d.ts +2 -0
- package/dist/proxy.degraded-response.test.d.ts.map +1 -0
- package/dist/proxy.degraded-response.test.js +105 -0
- package/dist/proxy.degraded-response.test.js.map +1 -0
- package/dist/proxy.image-download.test.d.ts +2 -0
- package/dist/proxy.image-download.test.d.ts.map +1 -0
- package/dist/proxy.image-download.test.js +92 -0
- package/dist/proxy.image-download.test.js.map +1 -0
- package/dist/proxy.js +3810 -0
- package/dist/proxy.js.map +1 -0
- package/dist/proxy.models-endpoint.test.d.ts +2 -0
- package/dist/proxy.models-endpoint.test.d.ts.map +1 -0
- package/dist/proxy.models-endpoint.test.js +19 -0
- package/dist/proxy.models-endpoint.test.js.map +1 -0
- package/dist/proxy.multiturn-thinking.test.d.ts +2 -0
- package/dist/proxy.multiturn-thinking.test.d.ts.map +1 -0
- package/dist/proxy.multiturn-thinking.test.js +82 -0
- package/dist/proxy.multiturn-thinking.test.js.map +1 -0
- package/dist/proxy.payment-chain-reuse.test.d.ts +2 -0
- package/dist/proxy.payment-chain-reuse.test.d.ts.map +1 -0
- package/dist/proxy.payment-chain-reuse.test.js +30 -0
- package/dist/proxy.payment-chain-reuse.test.js.map +1 -0
- package/dist/proxy.solana-resilience.test.d.ts +12 -0
- package/dist/proxy.solana-resilience.test.d.ts.map +1 -0
- package/dist/proxy.solana-resilience.test.js +39 -0
- package/dist/proxy.solana-resilience.test.js.map +1 -0
- package/dist/report.d.ts +6 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +44 -0
- package/dist/report.js.map +1 -0
- package/dist/response-cache.advanced.test.d.ts +2 -0
- package/dist/response-cache.advanced.test.d.ts.map +1 -0
- package/dist/response-cache.advanced.test.js +566 -0
- package/dist/response-cache.advanced.test.js.map +1 -0
- package/dist/response-cache.d.ts +85 -0
- package/dist/response-cache.d.ts.map +1 -0
- package/dist/response-cache.edge-cases.test.d.ts +2 -0
- package/dist/response-cache.edge-cases.test.d.ts.map +1 -0
- package/dist/response-cache.edge-cases.test.js +488 -0
- package/dist/response-cache.edge-cases.test.js.map +1 -0
- package/dist/response-cache.extreme.test.d.ts +2 -0
- package/dist/response-cache.extreme.test.d.ts.map +1 -0
- package/dist/response-cache.extreme.test.js +561 -0
- package/dist/response-cache.extreme.test.js.map +1 -0
- package/dist/response-cache.js +240 -0
- package/dist/response-cache.js.map +1 -0
- package/dist/response-cache.test.d.ts +2 -0
- package/dist/response-cache.test.d.ts.map +1 -0
- package/dist/response-cache.test.js +242 -0
- package/dist/response-cache.test.js.map +1 -0
- package/dist/retry.d.ts +42 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +99 -0
- package/dist/retry.js.map +1 -0
- package/dist/router/config.d.ts +11 -0
- package/dist/router/config.d.ts.map +1 -0
- package/dist/router/config.js +1215 -0
- package/dist/router/config.js.map +1 -0
- package/dist/router/index.d.ts +18 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +19 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/llm-classifier.d.ts +29 -0
- package/dist/router/llm-classifier.d.ts.map +1 -0
- package/dist/router/llm-classifier.js +99 -0
- package/dist/router/llm-classifier.js.map +1 -0
- package/dist/router/rules.d.ts +12 -0
- package/dist/router/rules.d.ts.map +1 -0
- package/dist/router/rules.js +202 -0
- package/dist/router/rules.js.map +1 -0
- package/dist/router/selector.d.ts +58 -0
- package/dist/router/selector.d.ts.map +1 -0
- package/dist/router/selector.js +165 -0
- package/dist/router/selector.js.map +1 -0
- package/dist/router/selector.test.d.ts +2 -0
- package/dist/router/selector.test.d.ts.map +1 -0
- package/dist/router/selector.test.js +67 -0
- package/dist/router/selector.test.js.map +1 -0
- package/dist/router/strategy.d.ts +19 -0
- package/dist/router/strategy.d.ts.map +1 -0
- package/dist/router/strategy.js +138 -0
- package/dist/router/strategy.js.map +1 -0
- package/dist/router/strategy.test.d.ts +2 -0
- package/dist/router/strategy.test.d.ts.map +1 -0
- package/dist/router/strategy.test.js +131 -0
- package/dist/router/strategy.test.js.map +1 -0
- package/dist/router/types.d.ts +127 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/types.js +11 -0
- package/dist/router/types.js.map +1 -0
- package/dist/session.d.ts +124 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +256 -0
- package/dist/session.js.map +1 -0
- package/dist/session.test.d.ts +2 -0
- package/dist/session.test.d.ts.map +1 -0
- package/dist/session.test.js +163 -0
- package/dist/session.test.js.map +1 -0
- package/dist/solana-balance.d.ts +48 -0
- package/dist/solana-balance.d.ts.map +1 -0
- package/dist/solana-balance.js +142 -0
- package/dist/solana-balance.js.map +1 -0
- package/dist/spend-control.d.ts +112 -0
- package/dist/spend-control.d.ts.map +1 -0
- package/dist/spend-control.js +278 -0
- package/dist/spend-control.js.map +1 -0
- package/dist/spend-control.test.d.ts +5 -0
- package/dist/spend-control.test.d.ts.map +1 -0
- package/dist/spend-control.test.js +210 -0
- package/dist/spend-control.test.js.map +1 -0
- package/dist/stats.d.ts +64 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +295 -0
- package/dist/stats.js.map +1 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/update-hint.test.d.ts +9 -0
- package/dist/update-hint.test.d.ts.map +1 -0
- package/dist/update-hint.test.js +156 -0
- package/dist/update-hint.test.js.map +1 -0
- package/dist/updater.d.ts +10 -0
- package/dist/updater.d.ts.map +1 -0
- package/dist/updater.js +55 -0
- package/dist/updater.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/dist/wallet.d.ts +52 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +93 -0
- package/dist/wallet.js.map +1 -0
- package/dist/wallet.test.d.ts +6 -0
- package/dist/wallet.test.d.ts.map +1 -0
- package/dist/wallet.test.js +117 -0
- package/dist/wallet.test.js.map +1 -0
- package/dist/x402-sdk.test.d.ts +6 -0
- package/dist/x402-sdk.test.d.ts.map +1 -0
- package/dist/x402-sdk.test.js +161 -0
- package/dist/x402-sdk.test.js.map +1 -0
- package/package.json +2 -2
- package/scripts/e2e-multiturn.mjs +4 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment Pre-Auth Cache
|
|
3
|
+
*
|
|
4
|
+
* Wraps the @x402/fetch SDK with pre-authorization caching.
|
|
5
|
+
* After the first 402 response, caches payment requirements per endpoint.
|
|
6
|
+
* On subsequent requests, pre-signs payment and attaches it to the first
|
|
7
|
+
* request, skipping the 402 round trip (~200ms savings per request).
|
|
8
|
+
*
|
|
9
|
+
* Falls back to normal 402 flow if pre-signed payment is rejected.
|
|
10
|
+
*/
|
|
11
|
+
import { x402HTTPClient } from "@x402/fetch";
|
|
12
|
+
const DEFAULT_TTL_MS = 3_600_000; // 1 hour
|
|
13
|
+
export function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, options) {
|
|
14
|
+
const httpClient = new x402HTTPClient(client);
|
|
15
|
+
const cache = new Map();
|
|
16
|
+
return async (input, init) => {
|
|
17
|
+
const request = new Request(input, init);
|
|
18
|
+
const urlPath = new URL(request.url).pathname;
|
|
19
|
+
// Extract model from request body to create model-specific cache keys.
|
|
20
|
+
// Without this, a cached payment from a paid model (e.g. sonnet) would be
|
|
21
|
+
// incorrectly applied to a free model (nvidia/gpt-oss-120b), causing
|
|
22
|
+
// payment errors even when the server wouldn't charge for the request.
|
|
23
|
+
let requestModel = "";
|
|
24
|
+
if (init?.body) {
|
|
25
|
+
try {
|
|
26
|
+
const bodyStr = init.body instanceof Uint8Array
|
|
27
|
+
? new TextDecoder().decode(init.body)
|
|
28
|
+
: typeof init.body === "string"
|
|
29
|
+
? init.body
|
|
30
|
+
: "";
|
|
31
|
+
if (bodyStr) {
|
|
32
|
+
const parsed = JSON.parse(bodyStr);
|
|
33
|
+
requestModel = parsed.model ?? "";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* not JSON, use empty model */
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const cacheKey = `${urlPath}:${requestModel}`;
|
|
41
|
+
// Try pre-auth if we have cached payment requirements
|
|
42
|
+
// Skip for Solana: payments use per-tx blockhashes that expire ~60-90s,
|
|
43
|
+
// making cached requirements useless and causing double charges.
|
|
44
|
+
const cached = !options?.skipPreAuth ? cache.get(cacheKey) : undefined;
|
|
45
|
+
if (cached && Date.now() - cached.cachedAt < ttlMs) {
|
|
46
|
+
try {
|
|
47
|
+
const payload = await client.createPaymentPayload(cached.paymentRequired);
|
|
48
|
+
const headers = httpClient.encodePaymentSignatureHeader(payload);
|
|
49
|
+
const preAuthRequest = request.clone();
|
|
50
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
51
|
+
preAuthRequest.headers.set(key, value);
|
|
52
|
+
}
|
|
53
|
+
const response = await baseFetch(preAuthRequest);
|
|
54
|
+
if (response.status !== 402) {
|
|
55
|
+
return response; // Pre-auth worked — saved ~200ms
|
|
56
|
+
}
|
|
57
|
+
// Pre-auth rejected (params may have changed) — invalidate and fall through
|
|
58
|
+
cache.delete(cacheKey);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Pre-auth signing failed — invalidate and fall through
|
|
62
|
+
cache.delete(cacheKey);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Normal flow: make request, handle 402 if needed
|
|
66
|
+
const clonedRequest = request.clone();
|
|
67
|
+
const response = await baseFetch(request);
|
|
68
|
+
if (response.status !== 402) {
|
|
69
|
+
return response;
|
|
70
|
+
}
|
|
71
|
+
// Parse 402 response and cache for future pre-auth
|
|
72
|
+
let paymentRequired;
|
|
73
|
+
try {
|
|
74
|
+
const getHeader = (name) => response.headers.get(name);
|
|
75
|
+
let body;
|
|
76
|
+
try {
|
|
77
|
+
const responseText = await Promise.race([
|
|
78
|
+
response.text(),
|
|
79
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Body read timeout")), 30_000)),
|
|
80
|
+
]);
|
|
81
|
+
if (responseText)
|
|
82
|
+
body = JSON.parse(responseText);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
/* empty body is fine */
|
|
86
|
+
}
|
|
87
|
+
paymentRequired = httpClient.getPaymentRequiredResponse(getHeader, body);
|
|
88
|
+
cache.set(cacheKey, { paymentRequired, cachedAt: Date.now() });
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
throw new Error(`Failed to parse payment requirements: ${error instanceof Error ? error.message : "Unknown error"}`, { cause: error });
|
|
92
|
+
}
|
|
93
|
+
// Sign payment and retry
|
|
94
|
+
const payload = await client.createPaymentPayload(paymentRequired);
|
|
95
|
+
const paymentHeaders = httpClient.encodePaymentSignatureHeader(payload);
|
|
96
|
+
for (const [key, value] of Object.entries(paymentHeaders)) {
|
|
97
|
+
clonedRequest.headers.set(key, value);
|
|
98
|
+
}
|
|
99
|
+
return baseFetch(clonedRequest);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=payment-preauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-preauth.js","sourceRoot":"","sources":["../src/payment-preauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAS7C,MAAM,cAAc,GAAG,SAAS,CAAC,CAAC,SAAS;AAI3C,MAAM,UAAU,yBAAyB,CACvC,SAAkB,EAClB,MAAkB,EAClB,KAAK,GAAG,cAAc,EACtB,OAAmC;IAEnC,MAAM,UAAU,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE7C,OAAO,KAAK,EAAE,KAAwB,EAAE,IAAkB,EAAqB,EAAE;QAC/E,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QAE9C,uEAAuE;QACvE,0EAA0E;QAC1E,qEAAqE;QACrE,uEAAuE;QACvE,IAAI,YAAY,GAAG,EAAE,CAAC;QACtB,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,OAAO,GACX,IAAI,CAAC,IAAI,YAAY,UAAU;oBAC7B,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;oBACrC,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;wBAC7B,CAAC,CAAC,IAAI,CAAC,IAAI;wBACX,CAAC,CAAC,EAAE,CAAC;gBACX,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAuB,CAAC;oBACzD,YAAY,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;gBACpC,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,+BAA+B;YACjC,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,GAAG,OAAO,IAAI,YAAY,EAAE,CAAC;QAE9C,sDAAsD;QACtD,wEAAwE;QACxE,iEAAiE;QACjE,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvE,IAAI,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,KAAK,EAAE,CAAC;YACnD,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;gBAC1E,MAAM,OAAO,GAAG,UAAU,CAAC,4BAA4B,CAAC,OAAO,CAAC,CAAC;gBACjE,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;gBACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;oBACnD,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBACzC,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC,CAAC;gBACjD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBAC5B,OAAO,QAAQ,CAAC,CAAC,iCAAiC;gBACpD,CAAC;gBACD,4EAA4E;gBAC5E,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;gBACxD,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,mDAAmD;QACnD,IAAI,eAAgC,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/D,IAAI,IAAa,CAAC;YAClB,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;oBACtC,QAAQ,CAAC,IAAI,EAAE;oBACf,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,EAAE,MAAM,CAAC,CACjE;iBACF,CAAC,CAAC;gBACH,IAAI,YAAY;oBAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACpD,CAAC;YAAC,MAAM,CAAC;gBACP,wBAAwB;YAC1B,CAAC;YACD,eAAe,GAAG,UAAU,CAAC,0BAA0B,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACzE,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,eAAe,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,yCAAyC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EACnG,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;QAED,yBAAyB;QACzB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;QACnE,MAAM,cAAc,GAAG,UAAU,CAAC,4BAA4B,CAAC,OAAO,CAAC,CAAC;QACxE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1D,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,SAAS,CAAC,aAAa,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlockRun ProviderPlugin for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Registers BlockRun as an LLM provider in OpenClaw.
|
|
5
|
+
* Uses a local x402 proxy to handle micropayments transparently —
|
|
6
|
+
* pi-ai sees a standard OpenAI-compatible API at localhost.
|
|
7
|
+
*/
|
|
8
|
+
import type { ProviderPlugin } from "./types.js";
|
|
9
|
+
import type { ProxyHandle } from "./proxy.js";
|
|
10
|
+
/**
|
|
11
|
+
* Update the proxy handle (called from index.ts when the proxy starts).
|
|
12
|
+
*/
|
|
13
|
+
export declare function setActiveProxy(proxy: ProxyHandle): void;
|
|
14
|
+
export declare function getActiveProxy(): ProxyHandle | null;
|
|
15
|
+
/**
|
|
16
|
+
* BlockRun provider plugin definition.
|
|
17
|
+
*/
|
|
18
|
+
export declare const blockrunProvider: ProviderPlugin;
|
|
19
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAO9C;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEvD;AAED,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAqB9B,CAAC"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlockRun ProviderPlugin for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Registers BlockRun as an LLM provider in OpenClaw.
|
|
5
|
+
* Uses a local x402 proxy to handle micropayments transparently —
|
|
6
|
+
* pi-ai sees a standard OpenAI-compatible API at localhost.
|
|
7
|
+
*/
|
|
8
|
+
import { buildProviderModels } from "./models.js";
|
|
9
|
+
/**
|
|
10
|
+
* State for the running proxy (set when the plugin activates).
|
|
11
|
+
*/
|
|
12
|
+
let activeProxy = null;
|
|
13
|
+
/**
|
|
14
|
+
* Update the proxy handle (called from index.ts when the proxy starts).
|
|
15
|
+
*/
|
|
16
|
+
export function setActiveProxy(proxy) {
|
|
17
|
+
activeProxy = proxy;
|
|
18
|
+
}
|
|
19
|
+
export function getActiveProxy() {
|
|
20
|
+
return activeProxy;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* BlockRun provider plugin definition.
|
|
24
|
+
*/
|
|
25
|
+
export const blockrunProvider = {
|
|
26
|
+
id: "blockrun",
|
|
27
|
+
label: "BlockRun",
|
|
28
|
+
docsPath: "https://blockrun.ai/docs",
|
|
29
|
+
aliases: ["br"],
|
|
30
|
+
envVars: ["BLOCKRUN_WALLET_KEY"],
|
|
31
|
+
// Model definitions — dynamically set to proxy URL
|
|
32
|
+
get models() {
|
|
33
|
+
if (!activeProxy) {
|
|
34
|
+
// Fallback: point to BlockRun API directly (won't handle x402, but
|
|
35
|
+
// allows config loading before proxy starts)
|
|
36
|
+
return buildProviderModels("https://blockrun.ai/api");
|
|
37
|
+
}
|
|
38
|
+
return buildProviderModels(activeProxy.baseUrl);
|
|
39
|
+
},
|
|
40
|
+
// No auth required — the x402 proxy handles wallet-based payments internally.
|
|
41
|
+
// The proxy auto-generates a wallet on first run and stores it at
|
|
42
|
+
// ~/.openclaw/blockrun/wallet.key. Users just fund that wallet with USDC.
|
|
43
|
+
auth: [],
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD;;GAEG;AACH,IAAI,WAAW,GAAuB,IAAI,CAAC;AAE3C;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,WAAW,GAAG,KAAK,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,EAAE,EAAE,UAAU;IACd,KAAK,EAAE,UAAU;IACjB,QAAQ,EAAE,0BAA0B;IACpC,OAAO,EAAE,CAAC,IAAI,CAAC;IACf,OAAO,EAAE,CAAC,qBAAqB,CAAC;IAEhC,mDAAmD;IACnD,IAAI,MAAM;QACR,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,mEAAmE;YACnE,6CAA6C;YAC7C,OAAO,mBAAmB,CAAC,yBAAyB,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,mBAAmB,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAClD,CAAC;IAED,8EAA8E;IAC9E,kEAAkE;IAClE,0EAA0E;IAC1E,IAAI,EAAE,EAAE;CACT,CAAC"}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local x402 Proxy Server
|
|
3
|
+
*
|
|
4
|
+
* Sits between OpenClaw's pi-ai (which makes standard OpenAI-format requests)
|
|
5
|
+
* and BlockRun's API (which requires x402 micropayments).
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* pi-ai → http://localhost:{port}/v1/chat/completions
|
|
9
|
+
* → proxy forwards to https://blockrun.ai/api/v1/chat/completions
|
|
10
|
+
* → gets 402 → @x402/fetch signs payment → retries
|
|
11
|
+
* → streams response back to pi-ai
|
|
12
|
+
*
|
|
13
|
+
* Optimizations (v0.3.0):
|
|
14
|
+
* - SSE heartbeat: for streaming requests, sends headers + heartbeat immediately
|
|
15
|
+
* before the x402 flow, preventing OpenClaw's 10-15s timeout from firing.
|
|
16
|
+
* - Response dedup: hashes request bodies and caches responses for 30s,
|
|
17
|
+
* preventing double-charging when OpenClaw retries after timeout.
|
|
18
|
+
* - Smart routing: when model is "blockrun/auto", classify query and pick cheapest model.
|
|
19
|
+
* - Usage logging: log every request as JSON line to ~/.openclaw/blockrun/logs/
|
|
20
|
+
*/
|
|
21
|
+
import { type RoutingDecision, type RoutingConfig } from "./router/index.js";
|
|
22
|
+
import { type ResponseCacheConfig } from "./response-cache.js";
|
|
23
|
+
import { BalanceMonitor } from "./balance.js";
|
|
24
|
+
import type { SolanaBalanceMonitor } from "./solana-balance.js";
|
|
25
|
+
/** Union type for chain-agnostic balance monitoring */
|
|
26
|
+
type AnyBalanceMonitor = BalanceMonitor | SolanaBalanceMonitor;
|
|
27
|
+
import { type SessionConfig } from "./session.js";
|
|
28
|
+
/**
|
|
29
|
+
* Semantic error categories from upstream provider responses.
|
|
30
|
+
* Used to distinguish auth failures from rate limits from server errors
|
|
31
|
+
* so each category can be handled independently without cross-contamination.
|
|
32
|
+
*/
|
|
33
|
+
export type ErrorCategory = "auth_failure" | "quota_exceeded" | "rate_limited" | "overloaded" | "server_error" | "payment_error" | "config_error";
|
|
34
|
+
/**
|
|
35
|
+
* Classify an upstream error response into a semantic category.
|
|
36
|
+
* Returns null if the status+body is not a provider-side issue worth retrying.
|
|
37
|
+
*/
|
|
38
|
+
export declare function categorizeError(status: number, body: string): ErrorCategory | null;
|
|
39
|
+
/**
|
|
40
|
+
* Get the proxy port from pre-loaded configuration.
|
|
41
|
+
* Port is validated at module load time, this just returns the cached value.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getProxyPort(): number;
|
|
44
|
+
/**
|
|
45
|
+
* Detect degraded 200-response payloads that should trigger model fallback.
|
|
46
|
+
* Returns a short reason when fallback should happen, otherwise undefined.
|
|
47
|
+
*/
|
|
48
|
+
export declare function detectDegradedSuccessResponse(body: string): string | undefined;
|
|
49
|
+
type ChatMessage = {
|
|
50
|
+
role: string;
|
|
51
|
+
content: string | unknown;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Extended message type for thinking-enabled conversations.
|
|
55
|
+
*/
|
|
56
|
+
type ExtendedChatMessage = ChatMessage & {
|
|
57
|
+
tool_calls?: unknown[];
|
|
58
|
+
reasoning_content?: unknown;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Normalize messages for thinking-enabled requests.
|
|
62
|
+
* When thinking/extended_thinking is enabled, ALL assistant messages in history
|
|
63
|
+
* must have reasoning_content (can be empty string if not present).
|
|
64
|
+
*
|
|
65
|
+
* Reasoning models like Kimi K2.5, DeepSeek-R1, etc. strip their thinking tokens
|
|
66
|
+
* before we forward the response to the client. So when the client sends back the
|
|
67
|
+
* assistant message in a follow-up turn, it lacks reasoning_content. These models
|
|
68
|
+
* reject the multi-turn history with 400 if any assistant message is missing it.
|
|
69
|
+
*
|
|
70
|
+
* Previously only tool-call messages were patched — this missed plain text assistant
|
|
71
|
+
* messages, causing 100% failures on existing (multi-turn) chats.
|
|
72
|
+
*
|
|
73
|
+
* Error examples:
|
|
74
|
+
* "400 thinking is enabled but reasoning_content is missing in assistant tool call message"
|
|
75
|
+
* "400 thinking is enabled but reasoning_content is missing in assistant message"
|
|
76
|
+
*/
|
|
77
|
+
export declare function normalizeMessagesForThinking(messages: ExtendedChatMessage[]): ExtendedChatMessage[];
|
|
78
|
+
/**
|
|
79
|
+
* Remove "blockrun" branding from system messages before sending upstream.
|
|
80
|
+
*
|
|
81
|
+
* OpenClaw embeds `model=blockrun/auto` and `default_model=blockrun/auto` in its
|
|
82
|
+
* system prompt Runtime section. LLMs pick up "blockrun" and adopt it as their
|
|
83
|
+
* identity (e.g. "I'm Blockrun"), overriding the user's SOUL.md persona.
|
|
84
|
+
*
|
|
85
|
+
* This function replaces `blockrun/<profile>` references with the actual resolved
|
|
86
|
+
* model name, and strips any remaining "blockrun/" prefix so the upstream LLM
|
|
87
|
+
* never sees "blockrun" as an identity to adopt.
|
|
88
|
+
*/
|
|
89
|
+
export declare function debrandSystemMessages(messages: ChatMessage[], resolvedModel: string): ChatMessage[];
|
|
90
|
+
/** Callback info for low balance warning */
|
|
91
|
+
export type LowBalanceInfo = {
|
|
92
|
+
balanceUSD: string;
|
|
93
|
+
walletAddress: string;
|
|
94
|
+
};
|
|
95
|
+
/** Callback info for insufficient funds error */
|
|
96
|
+
export type InsufficientFundsInfo = {
|
|
97
|
+
balanceUSD: string;
|
|
98
|
+
requiredUSD: string;
|
|
99
|
+
walletAddress: string;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Wallet config: either a plain EVM private key string, or the full
|
|
103
|
+
* resolution object from resolveOrGenerateWalletKey() which may include
|
|
104
|
+
* Solana keys. Using the full object prevents callers from accidentally
|
|
105
|
+
* forgetting to forward Solana key bytes.
|
|
106
|
+
*/
|
|
107
|
+
export type WalletConfig = string | {
|
|
108
|
+
key: string;
|
|
109
|
+
solanaPrivateKeyBytes?: Uint8Array;
|
|
110
|
+
};
|
|
111
|
+
export type PaymentChain = "base" | "solana";
|
|
112
|
+
export type ProxyOptions = {
|
|
113
|
+
wallet: WalletConfig;
|
|
114
|
+
apiBase?: string;
|
|
115
|
+
/** Payment chain: "base" (default) or "solana". Can also be set via CLAWROUTER_PAYMENT_CHAIN env var. */
|
|
116
|
+
paymentChain?: PaymentChain;
|
|
117
|
+
/** Port to listen on (default: 8402) */
|
|
118
|
+
port?: number;
|
|
119
|
+
routingConfig?: Partial<RoutingConfig>;
|
|
120
|
+
/** Request timeout in ms (default: 180000 = 3 minutes). Covers on-chain tx + LLM response. */
|
|
121
|
+
requestTimeoutMs?: number;
|
|
122
|
+
/** Skip balance checks (for testing only). Default: false */
|
|
123
|
+
skipBalanceCheck?: boolean;
|
|
124
|
+
/** Override the balance monitor with a mock (for testing only). */
|
|
125
|
+
_balanceMonitorOverride?: AnyBalanceMonitor;
|
|
126
|
+
/**
|
|
127
|
+
* Session persistence config. When enabled, maintains model selection
|
|
128
|
+
* across requests within a session to prevent mid-task model switching.
|
|
129
|
+
*/
|
|
130
|
+
sessionConfig?: Partial<SessionConfig>;
|
|
131
|
+
/**
|
|
132
|
+
* Auto-compress large requests to reduce network usage.
|
|
133
|
+
* When enabled, requests are automatically compressed using
|
|
134
|
+
* LLM-safe context compression (15-40% reduction).
|
|
135
|
+
* Default: true
|
|
136
|
+
*/
|
|
137
|
+
autoCompressRequests?: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Threshold in KB to trigger auto-compression (default: 180).
|
|
140
|
+
* Requests larger than this are compressed before sending.
|
|
141
|
+
* Set to 0 to compress all requests.
|
|
142
|
+
*/
|
|
143
|
+
compressionThresholdKB?: number;
|
|
144
|
+
/**
|
|
145
|
+
* Response caching config. When enabled, identical requests return
|
|
146
|
+
* cached responses instead of making new API calls.
|
|
147
|
+
* Default: enabled with 10 minute TTL, 200 max entries.
|
|
148
|
+
*/
|
|
149
|
+
cacheConfig?: ResponseCacheConfig;
|
|
150
|
+
/**
|
|
151
|
+
* Maximum total spend (in USD) per session run.
|
|
152
|
+
* Default: undefined (no limit). Example: 0.5 = $0.50 per session.
|
|
153
|
+
*/
|
|
154
|
+
maxCostPerRunUsd?: number;
|
|
155
|
+
/**
|
|
156
|
+
* How to enforce the per-run cost cap.
|
|
157
|
+
* - 'graceful' (default): when budget runs low, downgrade to cheaper models; use free model
|
|
158
|
+
* as last resort. Only hard-stops when no model can serve the request.
|
|
159
|
+
* - 'strict': immediately return 429 once the session spend reaches the cap.
|
|
160
|
+
*/
|
|
161
|
+
maxCostPerRunMode?: "graceful" | "strict";
|
|
162
|
+
/**
|
|
163
|
+
* Set of model IDs to exclude from routing.
|
|
164
|
+
* Excluded models are filtered out of fallback chains.
|
|
165
|
+
* Loaded from ~/.openclaw/blockrun/exclude-models.json
|
|
166
|
+
*/
|
|
167
|
+
excludeModels?: Set<string>;
|
|
168
|
+
onReady?: (port: number) => void;
|
|
169
|
+
onError?: (error: Error) => void;
|
|
170
|
+
onPayment?: (info: {
|
|
171
|
+
model: string;
|
|
172
|
+
amount: string;
|
|
173
|
+
network: string;
|
|
174
|
+
}) => void;
|
|
175
|
+
onRouted?: (decision: RoutingDecision) => void;
|
|
176
|
+
/** Called when balance drops below $1.00 (warning, request still proceeds) */
|
|
177
|
+
onLowBalance?: (info: LowBalanceInfo) => void;
|
|
178
|
+
/** Called when balance is insufficient for a request (request fails) */
|
|
179
|
+
onInsufficientFunds?: (info: InsufficientFundsInfo) => void;
|
|
180
|
+
};
|
|
181
|
+
export type ProxyHandle = {
|
|
182
|
+
port: number;
|
|
183
|
+
baseUrl: string;
|
|
184
|
+
walletAddress: string;
|
|
185
|
+
solanaAddress?: string;
|
|
186
|
+
balanceMonitor: AnyBalanceMonitor;
|
|
187
|
+
close: () => Promise<void>;
|
|
188
|
+
};
|
|
189
|
+
type ModelListEntry = {
|
|
190
|
+
id: string;
|
|
191
|
+
object: "model";
|
|
192
|
+
created: number;
|
|
193
|
+
owned_by: string;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Build `/v1/models` response entries from the full OpenClaw model registry.
|
|
197
|
+
* This includes alias IDs (e.g., `flash`, `kimi`) so `/model <alias>` works reliably.
|
|
198
|
+
*/
|
|
199
|
+
export declare function buildProxyModelList(createdAt?: number): ModelListEntry[];
|
|
200
|
+
/**
|
|
201
|
+
* Start the local x402 proxy server.
|
|
202
|
+
*
|
|
203
|
+
* If a proxy is already running on the target port, reuses it instead of failing.
|
|
204
|
+
* Port can be configured via BLOCKRUN_PROXY_PORT environment variable.
|
|
205
|
+
*
|
|
206
|
+
* Returns a handle with the assigned port, base URL, and a close function.
|
|
207
|
+
*/
|
|
208
|
+
export declare function startProxy(options: ProxyOptions): Promise<ProxyHandle>;
|
|
209
|
+
export {};
|
|
210
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.debrand.test.d.ts","sourceRoot":"","sources":["../src/proxy.debrand.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { debrandSystemMessages } from "./proxy.js";
|
|
3
|
+
describe("debrandSystemMessages", () => {
|
|
4
|
+
it("replaces blockrun/auto with the resolved model in system messages", () => {
|
|
5
|
+
const messages = [
|
|
6
|
+
{
|
|
7
|
+
role: "system",
|
|
8
|
+
content: "You are a personal assistant.\n\n## Runtime\nRuntime: model=blockrun/auto | default_model=blockrun/auto",
|
|
9
|
+
},
|
|
10
|
+
{ role: "user", content: "Hello" },
|
|
11
|
+
];
|
|
12
|
+
const result = debrandSystemMessages(messages, "deepseek/deepseek-chat");
|
|
13
|
+
expect(result[0].content).toBe("You are a personal assistant.\n\n## Runtime\nRuntime: model=deepseek/deepseek-chat | default_model=deepseek/deepseek-chat");
|
|
14
|
+
// User message untouched
|
|
15
|
+
expect(result[1].content).toBe("Hello");
|
|
16
|
+
});
|
|
17
|
+
it("replaces blockrun/eco and blockrun/premium profiles", () => {
|
|
18
|
+
const messages = [
|
|
19
|
+
{
|
|
20
|
+
role: "system",
|
|
21
|
+
content: "model=blockrun/eco | default_model=blockrun/premium",
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
const result = debrandSystemMessages(messages, "google/gemini-2.5-flash");
|
|
25
|
+
expect(result[0].content).toBe("model=google/gemini-2.5-flash | default_model=google/gemini-2.5-flash");
|
|
26
|
+
});
|
|
27
|
+
it("strips blockrun/ prefix from explicit model names", () => {
|
|
28
|
+
const messages = [
|
|
29
|
+
{
|
|
30
|
+
role: "system",
|
|
31
|
+
content: "model=blockrun/openai/gpt-4o | default_model=blockrun/auto",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
const result = debrandSystemMessages(messages, "openai/gpt-4o");
|
|
35
|
+
expect(result[0].content).toBe("model=openai/gpt-4o | default_model=openai/gpt-4o");
|
|
36
|
+
});
|
|
37
|
+
it("does not modify non-system messages", () => {
|
|
38
|
+
const messages = [
|
|
39
|
+
{ role: "user", content: "I use blockrun/auto for my tasks" },
|
|
40
|
+
{ role: "assistant", content: "I'm Blockrun!" },
|
|
41
|
+
];
|
|
42
|
+
const result = debrandSystemMessages(messages, "deepseek/deepseek-chat");
|
|
43
|
+
expect(result[0].content).toBe("I use blockrun/auto for my tasks");
|
|
44
|
+
expect(result[1].content).toBe("I'm Blockrun!");
|
|
45
|
+
});
|
|
46
|
+
it("returns same array reference when no changes needed", () => {
|
|
47
|
+
const messages = [
|
|
48
|
+
{ role: "system", content: "You are a helpful assistant." },
|
|
49
|
+
{ role: "user", content: "Hi" },
|
|
50
|
+
];
|
|
51
|
+
const result = debrandSystemMessages(messages, "deepseek/deepseek-chat");
|
|
52
|
+
expect(result).toBe(messages); // same reference = no copy
|
|
53
|
+
});
|
|
54
|
+
it("handles non-string system content gracefully", () => {
|
|
55
|
+
const messages = [{ role: "system", content: [{ type: "text", text: "blockrun/auto" }] }];
|
|
56
|
+
const result = debrandSystemMessages(messages, "deepseek/deepseek-chat");
|
|
57
|
+
// Array content is not modified (only string content)
|
|
58
|
+
expect(result).toBe(messages);
|
|
59
|
+
});
|
|
60
|
+
it("is case-insensitive for blockrun prefix", () => {
|
|
61
|
+
const messages = [{ role: "system", content: "model=Blockrun/Auto" }];
|
|
62
|
+
const result = debrandSystemMessages(messages, "deepseek/deepseek-chat");
|
|
63
|
+
expect(result[0].content).toBe("model=deepseek/deepseek-chat");
|
|
64
|
+
});
|
|
65
|
+
it("replaces blockrun/free profile", () => {
|
|
66
|
+
const messages = [{ role: "system", content: "default_model=blockrun/free" }];
|
|
67
|
+
const result = debrandSystemMessages(messages, "nvidia/gpt-oss-120b");
|
|
68
|
+
expect(result[0].content).toBe("default_model=nvidia/gpt-oss-120b");
|
|
69
|
+
});
|
|
70
|
+
it("handles realistic OpenClaw system prompt with SOUL.md", () => {
|
|
71
|
+
// This simulates the actual system prompt OpenClaw generates,
|
|
72
|
+
// which caused the model to say "I'm Blockrun" (issue #99)
|
|
73
|
+
const systemContent = [
|
|
74
|
+
"You are a personal assistant running inside OpenClaw.",
|
|
75
|
+
"",
|
|
76
|
+
"## Runtime",
|
|
77
|
+
"Runtime: agent=main | host=openclaw | os=Darwin 25.3.0 (arm64) | node=v22.14.0 | model=blockrun/auto | default_model=blockrun/auto | channel=control-ui | thinking=off",
|
|
78
|
+
"",
|
|
79
|
+
"# Project Context",
|
|
80
|
+
"",
|
|
81
|
+
"## SOUL.md",
|
|
82
|
+
"",
|
|
83
|
+
"AIBot = Watson",
|
|
84
|
+
"User = Fred",
|
|
85
|
+
].join("\n");
|
|
86
|
+
const messages = [
|
|
87
|
+
{ role: "system", content: systemContent },
|
|
88
|
+
{ role: "user", content: "Hey there!" },
|
|
89
|
+
];
|
|
90
|
+
const result = debrandSystemMessages(messages, "deepseek/deepseek-chat");
|
|
91
|
+
// "blockrun" should be completely gone from system prompt
|
|
92
|
+
expect(result[0].content).not.toContain("blockrun");
|
|
93
|
+
// Resolved model should appear instead
|
|
94
|
+
expect(result[0].content).toContain("model=deepseek/deepseek-chat");
|
|
95
|
+
expect(result[0].content).toContain("default_model=deepseek/deepseek-chat");
|
|
96
|
+
// SOUL.md content should be preserved
|
|
97
|
+
expect(result[0].content).toContain("AIBot = Watson");
|
|
98
|
+
expect(result[0].content).toContain("User = Fred");
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=proxy.debrand.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.debrand.test.js","sourceRoot":"","sources":["../src/proxy.debrand.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.degraded-response.test.d.ts","sourceRoot":"","sources":["../src/proxy.degraded-response.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { detectDegradedSuccessResponse } from "./proxy.js";
|
|
3
|
+
describe("detectDegradedSuccessResponse", () => {
|
|
4
|
+
it("flags plain overload placeholder text", () => {
|
|
5
|
+
const result = detectDegradedSuccessResponse("The AI service is temporarily overloaded. Please try again in a moment.");
|
|
6
|
+
expect(result).toBeDefined();
|
|
7
|
+
});
|
|
8
|
+
it("flags overload placeholder inside successful chat response JSON", () => {
|
|
9
|
+
const payload = JSON.stringify({
|
|
10
|
+
choices: [
|
|
11
|
+
{
|
|
12
|
+
message: {
|
|
13
|
+
role: "assistant",
|
|
14
|
+
content: "The AI service is temporarily overloaded. Please try again in a moment.",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
});
|
|
19
|
+
const result = detectDegradedSuccessResponse(payload);
|
|
20
|
+
expect(result).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
it("flags known repetitive hallucination loop patterns", () => {
|
|
23
|
+
const payload = JSON.stringify({
|
|
24
|
+
choices: [
|
|
25
|
+
{
|
|
26
|
+
message: {
|
|
27
|
+
role: "assistant",
|
|
28
|
+
content: `The boxed is the response.
|
|
29
|
+
|
|
30
|
+
Yes.
|
|
31
|
+
|
|
32
|
+
The response is the text.
|
|
33
|
+
|
|
34
|
+
Yes.
|
|
35
|
+
|
|
36
|
+
The final answer is the boxed.
|
|
37
|
+
|
|
38
|
+
Yes.`,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
const result = detectDegradedSuccessResponse(payload);
|
|
44
|
+
expect(result).toBeDefined();
|
|
45
|
+
});
|
|
46
|
+
it("flags empty turn: no content and no tool_calls with finish_reason stop", () => {
|
|
47
|
+
const payload = JSON.stringify({
|
|
48
|
+
choices: [
|
|
49
|
+
{
|
|
50
|
+
message: { role: "assistant", content: "" },
|
|
51
|
+
finish_reason: "stop",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
const result = detectDegradedSuccessResponse(payload);
|
|
56
|
+
expect(result).toMatch(/empty turn/);
|
|
57
|
+
});
|
|
58
|
+
it("flags empty turn: null content, no tool_calls, finish_reason stop", () => {
|
|
59
|
+
const payload = JSON.stringify({
|
|
60
|
+
choices: [{ message: { role: "assistant", content: null }, finish_reason: "stop" }],
|
|
61
|
+
});
|
|
62
|
+
const result = detectDegradedSuccessResponse(payload);
|
|
63
|
+
expect(result).toMatch(/empty turn/);
|
|
64
|
+
});
|
|
65
|
+
it("does not flag responses with tool_calls even if content is empty", () => {
|
|
66
|
+
const payload = JSON.stringify({
|
|
67
|
+
choices: [
|
|
68
|
+
{
|
|
69
|
+
message: {
|
|
70
|
+
role: "assistant",
|
|
71
|
+
content: null,
|
|
72
|
+
tool_calls: [
|
|
73
|
+
{ id: "call_1", type: "function", function: { name: "read_file", arguments: "{}" } },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
finish_reason: "tool_calls",
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
const result = detectDegradedSuccessResponse(payload);
|
|
81
|
+
expect(result).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
it("does not flag empty turn when finish_reason is not stop", () => {
|
|
84
|
+
const payload = JSON.stringify({
|
|
85
|
+
choices: [{ message: { role: "assistant", content: "" }, finish_reason: "length" }],
|
|
86
|
+
});
|
|
87
|
+
const result = detectDegradedSuccessResponse(payload);
|
|
88
|
+
expect(result).toBeUndefined();
|
|
89
|
+
});
|
|
90
|
+
it("does not flag normal assistant responses", () => {
|
|
91
|
+
const payload = JSON.stringify({
|
|
92
|
+
choices: [
|
|
93
|
+
{
|
|
94
|
+
message: {
|
|
95
|
+
role: "assistant",
|
|
96
|
+
content: "Paris is the capital of France.",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
});
|
|
101
|
+
const result = detectDegradedSuccessResponse(payload);
|
|
102
|
+
expect(result).toBeUndefined();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=proxy.degraded-response.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.degraded-response.test.js","sourceRoot":"","sources":["../src/proxy.degraded-response.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.image-download.test.d.ts","sourceRoot":"","sources":["../src/proxy.image-download.test.ts"],"names":[],"mappings":""}
|