@blockrun/clawrouter 0.5.7 → 0.5.9
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/README.md +53 -8
- package/dist/index.js +41 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,6 +56,8 @@ Done! Smart routing (`blockrun/auto`) is now your default model.
|
|
|
56
56
|
### Tips
|
|
57
57
|
|
|
58
58
|
- **Use `/model blockrun/auto`** in any conversation to switch on the fly
|
|
59
|
+
- **Free tier?** Use `/model free` — routes to nvidia/gpt-oss-120b at $0
|
|
60
|
+
- **Model aliases:** `/model sonnet`, `/model grok`, `/model deepseek`, `/model kimi`
|
|
59
61
|
- **Want a specific model?** Use `blockrun/openai/gpt-4o` or `blockrun/anthropic/claude-sonnet-4`
|
|
60
62
|
- **Already have a funded wallet?** `export BLOCKRUN_WALLET_KEY=0x...`
|
|
61
63
|
|
|
@@ -83,7 +85,7 @@ No API keys. No accounts. Just fund and go.
|
|
|
83
85
|
**100% local, <1ms, zero API calls.**
|
|
84
86
|
|
|
85
87
|
```
|
|
86
|
-
Request → Weighted Scorer (
|
|
88
|
+
Request → Weighted Scorer (15 dimensions)
|
|
87
89
|
│
|
|
88
90
|
├── High confidence → Pick model from tier → Done
|
|
89
91
|
│
|
|
@@ -206,6 +208,42 @@ ClawRouter automatically filters out models that can't handle your context size:
|
|
|
206
208
|
|
|
207
209
|
This prevents wasted API calls and faster fallback to capable models.
|
|
208
210
|
|
|
211
|
+
### Model Aliases (v0.5)
|
|
212
|
+
|
|
213
|
+
Use short aliases instead of full model paths:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
/model free # nvidia/gpt-oss-120b (FREE!)
|
|
217
|
+
/model sonnet # anthropic/claude-sonnet-4
|
|
218
|
+
/model opus # anthropic/claude-opus-4
|
|
219
|
+
/model haiku # anthropic/claude-haiku-4.5
|
|
220
|
+
/model gpt # openai/gpt-4o
|
|
221
|
+
/model gpt5 # openai/gpt-5.2
|
|
222
|
+
/model deepseek # deepseek/deepseek-chat
|
|
223
|
+
/model reasoner # deepseek/deepseek-reasoner
|
|
224
|
+
/model kimi # moonshot/kimi-k2.5
|
|
225
|
+
/model gemini # google/gemini-2.5-pro
|
|
226
|
+
/model flash # google/gemini-2.5-flash
|
|
227
|
+
/model grok # xai/grok-3
|
|
228
|
+
/model grok-fast # xai/grok-4-fast-reasoning
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
All aliases work with `/model blockrun/xxx` or just `/model xxx`.
|
|
232
|
+
|
|
233
|
+
### Free Tier Fallback (v0.5)
|
|
234
|
+
|
|
235
|
+
When your wallet balance hits $0, ClawRouter automatically falls back to the free model (`nvidia/gpt-oss-120b`):
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
Wallet: $0.00
|
|
239
|
+
Request: "Help me write a function"
|
|
240
|
+
→ Routes to nvidia/gpt-oss-120b (FREE)
|
|
241
|
+
→ No "insufficient funds" error
|
|
242
|
+
→ Keep building while you top up
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
You'll never get blocked by an empty wallet — the free tier keeps you running.
|
|
246
|
+
|
|
209
247
|
### Session Persistence (v0.5)
|
|
210
248
|
|
|
211
249
|
For multi-turn conversations, ClawRouter pins the model to prevent mid-task switching:
|
|
@@ -262,8 +300,10 @@ Compared to **$75/M** for Claude Opus = **96% savings** on a typical workload.
|
|
|
262
300
|
| grok-code-fast-1 | $0.20 | $1.50 | 131K | |
|
|
263
301
|
| **Moonshot** | | | | |
|
|
264
302
|
| kimi-k2.5 | $0.50 | $2.40 | 262K | \* |
|
|
265
|
-
| **NVIDIA**
|
|
266
|
-
| gpt-oss-120b | **
|
|
303
|
+
| **NVIDIA (Free)** | | | | |
|
|
304
|
+
| gpt-oss-120b | **$0** | **$0** | 128K | |
|
|
305
|
+
|
|
306
|
+
> **Free tier:** `nvidia/gpt-oss-120b` costs nothing and serves as automatic fallback when wallet is empty.
|
|
267
307
|
|
|
268
308
|
Full list: [`src/models.ts`](src/models.ts)
|
|
269
309
|
|
|
@@ -408,7 +448,7 @@ If you lose your wallet key, **there is no way to recover it**. The wallet is se
|
|
|
408
448
|
│ ClawRouter (localhost) │
|
|
409
449
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
|
|
410
450
|
│ │ Weighted Scorer │→ │ Model Selector │→ │ x402 Signer │ │
|
|
411
|
-
│ │ (
|
|
451
|
+
│ │ (15 dimensions)│ │ (cheapest tier) │ │ (USDC) │ │
|
|
412
452
|
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
|
|
413
453
|
└─────────────────────────────────────────────────────────────┘
|
|
414
454
|
│
|
|
@@ -584,7 +624,7 @@ Agents shouldn't need a human to paste API keys. They should generate a wallet,
|
|
|
584
624
|
### Quick Checklist
|
|
585
625
|
|
|
586
626
|
```bash
|
|
587
|
-
# 1. Check your version (should be 0.5.
|
|
627
|
+
# 1. Check your version (should be 0.5.7+)
|
|
588
628
|
cat ~/.openclaw/extensions/clawrouter/package.json | grep version
|
|
589
629
|
|
|
590
630
|
# 2. Check proxy is running
|
|
@@ -644,14 +684,17 @@ OpenClaw's security scanner may flag ClawRouter with:
|
|
|
644
684
|
[env-harvesting] Environment variable access combined with network send
|
|
645
685
|
```
|
|
646
686
|
|
|
647
|
-
**This is a false positive.**
|
|
687
|
+
**This is a false positive.** The scanner's heuristic (`env variable + network request = suspicious`) flags all payment plugins, but this pattern is inherently required for non-custodial payments.
|
|
688
|
+
|
|
689
|
+
ClawRouter reads `BLOCKRUN_WALLET_KEY` to sign x402 payment transactions — this is required and intentional:
|
|
648
690
|
|
|
649
691
|
- The wallet key is used **locally** for cryptographic signing (EIP-712)
|
|
650
692
|
- The **signature** is transmitted, not the private key itself
|
|
651
|
-
-
|
|
693
|
+
- The key **never leaves the machine** — only cryptographic proofs are sent
|
|
694
|
+
- This is standard [x402 payment protocol](https://x402.org) behavior
|
|
652
695
|
- Source code is [MIT licensed and fully auditable](https://github.com/BlockRunAI/ClawRouter)
|
|
653
696
|
|
|
654
|
-
See [`openclaw.security.json`](openclaw.security.json) for detailed security documentation.
|
|
697
|
+
See [`openclaw.security.json`](openclaw.security.json) for detailed security documentation and [this discussion](https://x.com/bc1beat/status/2020158972561428686) for more context.
|
|
655
698
|
|
|
656
699
|
### Port 8402 already in use
|
|
657
700
|
|
|
@@ -724,6 +767,8 @@ BLOCKRUN_WALLET_KEY=0x... npx tsx test-e2e.ts
|
|
|
724
767
|
- [x] Context-aware routing — filter out models that can't handle context size
|
|
725
768
|
- [x] Session persistence — pin model for multi-turn conversations
|
|
726
769
|
- [x] Cost tracking — /stats command with savings dashboard
|
|
770
|
+
- [x] Model aliases — `/model free`, `/model sonnet`, `/model grok`, etc.
|
|
771
|
+
- [x] Free tier — nvidia/gpt-oss-120b for $0 when wallet is empty
|
|
727
772
|
- [ ] Cascade routing — try cheap model first, escalate on low quality
|
|
728
773
|
- [ ] Spend controls — daily/monthly budgets
|
|
729
774
|
- [ ] Remote analytics — cost tracking at blockrun.ai
|
package/dist/index.js
CHANGED
|
@@ -2384,6 +2384,28 @@ function isProviderError(status, body) {
|
|
|
2384
2384
|
}
|
|
2385
2385
|
return PROVIDER_ERROR_PATTERNS.some((pattern) => pattern.test(body));
|
|
2386
2386
|
}
|
|
2387
|
+
var VALID_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool", "function"]);
|
|
2388
|
+
var ROLE_MAPPINGS = {
|
|
2389
|
+
developer: "system",
|
|
2390
|
+
// OpenAI's newer API uses "developer" for system messages
|
|
2391
|
+
model: "assistant"
|
|
2392
|
+
// Some APIs use "model" instead of "assistant"
|
|
2393
|
+
};
|
|
2394
|
+
function normalizeMessageRoles(messages) {
|
|
2395
|
+
if (!messages || messages.length === 0) return messages;
|
|
2396
|
+
let hasChanges = false;
|
|
2397
|
+
const normalized = messages.map((msg) => {
|
|
2398
|
+
if (VALID_ROLES.has(msg.role)) return msg;
|
|
2399
|
+
const mappedRole = ROLE_MAPPINGS[msg.role];
|
|
2400
|
+
if (mappedRole) {
|
|
2401
|
+
hasChanges = true;
|
|
2402
|
+
return { ...msg, role: mappedRole };
|
|
2403
|
+
}
|
|
2404
|
+
hasChanges = true;
|
|
2405
|
+
return { ...msg, role: "user" };
|
|
2406
|
+
});
|
|
2407
|
+
return hasChanges ? normalized : messages;
|
|
2408
|
+
}
|
|
2387
2409
|
function normalizeMessagesForGoogle(messages) {
|
|
2388
2410
|
if (!messages || messages.length === 0) return messages;
|
|
2389
2411
|
let firstNonSystemIdx = -1;
|
|
@@ -2614,6 +2636,9 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
2614
2636
|
try {
|
|
2615
2637
|
const parsed = JSON.parse(body.toString());
|
|
2616
2638
|
parsed.model = modelId;
|
|
2639
|
+
if (Array.isArray(parsed.messages)) {
|
|
2640
|
+
parsed.messages = normalizeMessageRoles(parsed.messages);
|
|
2641
|
+
}
|
|
2617
2642
|
if (isGoogleModel(modelId) && Array.isArray(parsed.messages)) {
|
|
2618
2643
|
parsed.messages = normalizeMessagesForGoogle(parsed.messages);
|
|
2619
2644
|
}
|
|
@@ -3029,7 +3054,14 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
3029
3054
|
if (toolCalls && toolCalls.length > 0) {
|
|
3030
3055
|
const toolCallChunk = {
|
|
3031
3056
|
...baseChunk,
|
|
3032
|
-
choices: [
|
|
3057
|
+
choices: [
|
|
3058
|
+
{
|
|
3059
|
+
index,
|
|
3060
|
+
delta: { tool_calls: toolCalls },
|
|
3061
|
+
logprobs: null,
|
|
3062
|
+
finish_reason: null
|
|
3063
|
+
}
|
|
3064
|
+
]
|
|
3033
3065
|
};
|
|
3034
3066
|
const toolCallData = `data: ${JSON.stringify(toolCallChunk)}
|
|
3035
3067
|
|
|
@@ -3039,7 +3071,14 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
3039
3071
|
}
|
|
3040
3072
|
const finishChunk = {
|
|
3041
3073
|
...baseChunk,
|
|
3042
|
-
choices: [
|
|
3074
|
+
choices: [
|
|
3075
|
+
{
|
|
3076
|
+
index,
|
|
3077
|
+
delta: {},
|
|
3078
|
+
logprobs: null,
|
|
3079
|
+
finish_reason: choice.finish_reason ?? "stop"
|
|
3080
|
+
}
|
|
3081
|
+
]
|
|
3043
3082
|
};
|
|
3044
3083
|
const finishData = `data: ${JSON.stringify(finishChunk)}
|
|
3045
3084
|
|