@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 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 (14 dimensions)
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 | **FREE** | **FREE** | 128K | |
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
- │ │ (14 dimensions)│ │ (cheapest tier) │ │ (USDC) │ │
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.0+)
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.** ClawRouter reads `BLOCKRUN_WALLET_KEY` to sign x402 payment transactions this is required and intentional:
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
- - This is standard x402 payment protocol behavior
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: [{ index, delta: { tool_calls: toolCalls }, logprobs: null, finish_reason: null }]
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: [{ index, delta: {}, logprobs: null, finish_reason: choice.finish_reason ?? "stop" }]
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