@blockrun/clawrouter 0.12.247 → 0.12.249

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -89132,6 +89132,9 @@ function stripThinkingTokens(content) {
89132
89132
  cleaned = cleaned.replace(THINKING_TAG_RE, "");
89133
89133
  return cleaned;
89134
89134
  }
89135
+ function forwardToolCallProse() {
89136
+ return process.env.CLAWROUTER_TOOL_CALL_PROSE?.trim().toLowerCase() !== "off";
89137
+ }
89135
89138
  function buildModelPricing() {
89136
89139
  const map = /* @__PURE__ */ new Map();
89137
89140
  for (const m of BLOCKRUN_MODELS) {
@@ -89545,7 +89548,15 @@ async function startProxy(options) {
89545
89548
  }
89546
89549
  if (full) {
89547
89550
  try {
89548
- const balanceInfo = await balanceMonitor.checkBalance();
89551
+ const balanceInfo = await Promise.race([
89552
+ balanceMonitor.checkBalance(),
89553
+ new Promise(
89554
+ (_, reject) => setTimeout(
89555
+ () => reject(new Error("balance check timed out")),
89556
+ BALANCE_CHECK_TIMEOUT_MS
89557
+ ).unref()
89558
+ )
89559
+ ]);
89549
89560
  response.balance = balanceInfo.balanceUSD;
89550
89561
  response.isLow = balanceInfo.isLow;
89551
89562
  response.isEmpty = balanceInfo.isEmpty;
@@ -92344,13 +92355,16 @@ data: [DONE]
92344
92355
  const endsWithToolCalls = choice.finish_reason === "tool_calls";
92345
92356
  let toolCalls = choice.message?.tool_calls ?? choice.delta?.tool_calls;
92346
92357
  const rawContent = choice.message?.content ?? choice.delta?.content ?? "";
92358
+ let proseContent = rawContent;
92347
92359
  if (!endsWithToolCalls && (!toolCalls || toolCalls.length === 0) && rawContent) {
92348
92360
  const extracted = extractTextualToolCalls(rawContent, { tools: requestTools });
92349
92361
  if (extracted.toolCalls.length > 0) {
92350
92362
  toolCalls = extracted.toolCalls;
92363
+ proseContent = extracted.cleanedContent;
92351
92364
  }
92352
92365
  }
92353
- const content = endsWithToolCalls || toolCalls && toolCalls.length > 0 ? "" : stripThinkingTokens(rawContent);
92366
+ const suppressProse = (endsWithToolCalls || toolCalls && toolCalls.length > 0) && !forwardToolCallProse();
92367
+ const content = suppressProse ? "" : stripThinkingTokens(proseContent);
92354
92368
  const role = choice.message?.role ?? choice.delta?.role ?? "assistant";
92355
92369
  const index2 = choice.index ?? 0;
92356
92370
  if (content) {
@@ -92552,8 +92566,9 @@ data: [DONE]
92552
92566
  const message = choice.message;
92553
92567
  if (!message || typeof message.content !== "string") continue;
92554
92568
  if (choice.finish_reason === "tool_calls" || Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
92555
- if (message.content !== "") {
92556
- message.content = "";
92569
+ const next = forwardToolCallProse() ? stripThinkingTokens(message.content) : "";
92570
+ if (message.content !== next) {
92571
+ message.content = next;
92557
92572
  changed = true;
92558
92573
  }
92559
92574
  continue;
@@ -92561,7 +92576,7 @@ data: [DONE]
92561
92576
  const extracted = extractTextualToolCalls(message.content, { tools: requestTools });
92562
92577
  if (extracted.toolCalls.length > 0) {
92563
92578
  message.tool_calls = extracted.toolCalls;
92564
- message.content = "";
92579
+ message.content = forwardToolCallProse() ? stripThinkingTokens(extracted.cleanedContent) : "";
92565
92580
  choice.finish_reason = "tool_calls";
92566
92581
  changed = true;
92567
92582
  continue;
package/dist/index.js CHANGED
@@ -88952,6 +88952,9 @@ function stripThinkingTokens(content) {
88952
88952
  cleaned = cleaned.replace(THINKING_TAG_RE, "");
88953
88953
  return cleaned;
88954
88954
  }
88955
+ function forwardToolCallProse() {
88956
+ return process.env.CLAWROUTER_TOOL_CALL_PROSE?.trim().toLowerCase() !== "off";
88957
+ }
88955
88958
  function buildModelPricing() {
88956
88959
  const map = /* @__PURE__ */ new Map();
88957
88960
  for (const m of BLOCKRUN_MODELS) {
@@ -89365,7 +89368,15 @@ async function startProxy(options) {
89365
89368
  }
89366
89369
  if (full) {
89367
89370
  try {
89368
- const balanceInfo = await balanceMonitor.checkBalance();
89371
+ const balanceInfo = await Promise.race([
89372
+ balanceMonitor.checkBalance(),
89373
+ new Promise(
89374
+ (_, reject) => setTimeout(
89375
+ () => reject(new Error("balance check timed out")),
89376
+ BALANCE_CHECK_TIMEOUT_MS
89377
+ ).unref()
89378
+ )
89379
+ ]);
89369
89380
  response.balance = balanceInfo.balanceUSD;
89370
89381
  response.isLow = balanceInfo.isLow;
89371
89382
  response.isEmpty = balanceInfo.isEmpty;
@@ -92164,13 +92175,16 @@ data: [DONE]
92164
92175
  const endsWithToolCalls = choice.finish_reason === "tool_calls";
92165
92176
  let toolCalls = choice.message?.tool_calls ?? choice.delta?.tool_calls;
92166
92177
  const rawContent = choice.message?.content ?? choice.delta?.content ?? "";
92178
+ let proseContent = rawContent;
92167
92179
  if (!endsWithToolCalls && (!toolCalls || toolCalls.length === 0) && rawContent) {
92168
92180
  const extracted = extractTextualToolCalls(rawContent, { tools: requestTools });
92169
92181
  if (extracted.toolCalls.length > 0) {
92170
92182
  toolCalls = extracted.toolCalls;
92183
+ proseContent = extracted.cleanedContent;
92171
92184
  }
92172
92185
  }
92173
- const content = endsWithToolCalls || toolCalls && toolCalls.length > 0 ? "" : stripThinkingTokens(rawContent);
92186
+ const suppressProse = (endsWithToolCalls || toolCalls && toolCalls.length > 0) && !forwardToolCallProse();
92187
+ const content = suppressProse ? "" : stripThinkingTokens(proseContent);
92174
92188
  const role = choice.message?.role ?? choice.delta?.role ?? "assistant";
92175
92189
  const index2 = choice.index ?? 0;
92176
92190
  if (content) {
@@ -92372,8 +92386,9 @@ data: [DONE]
92372
92386
  const message = choice.message;
92373
92387
  if (!message || typeof message.content !== "string") continue;
92374
92388
  if (choice.finish_reason === "tool_calls" || Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
92375
- if (message.content !== "") {
92376
- message.content = "";
92389
+ const next = forwardToolCallProse() ? stripThinkingTokens(message.content) : "";
92390
+ if (message.content !== next) {
92391
+ message.content = next;
92377
92392
  changed = true;
92378
92393
  }
92379
92394
  continue;
@@ -92381,7 +92396,7 @@ data: [DONE]
92381
92396
  const extracted = extractTextualToolCalls(message.content, { tools: requestTools });
92382
92397
  if (extracted.toolCalls.length > 0) {
92383
92398
  message.tool_calls = extracted.toolCalls;
92384
- message.content = "";
92399
+ message.content = forwardToolCallProse() ? stripThinkingTokens(extracted.cleanedContent) : "";
92385
92400
  choice.finish_reason = "tool_calls";
92386
92401
  changed = true;
92387
92402
  continue;
@@ -51,17 +51,17 @@ The returned URL is a publicly hosted image, ready to use in Telegram, Discord,
51
51
 
52
52
  ## Models & Pricing
53
53
 
54
- | Model ID | Shorthand | Price | Sizes | Provider |
55
- | ---------------------------- | ------------------ | ------------------- | ---------------------------------------------------------------------------------- | --------------------- |
56
- | `google/nano-banana` | `nano-banana` | $0.05/image | 1024×1024 | Google Gemini Flash |
57
- | `google/nano-banana-2` | `banana-2` | $0.09/image | 1024×1024 | Google Nano Banana 2 |
58
- | `google/nano-banana-pro` | `banana-pro` | $0.10–$0.15/image | 1024×1024, 2048×2048, 4096×4096 | Google Gemini Pro |
59
- | `openai/gpt-image-1` | `gpt-image` | $0.02–$0.04/image | 1024×1024, 1536×1024, 1024×1536 | OpenAI GPT Image 1 |
60
- | `openai/gpt-image-2` | `gpt-image-2` | $0.06–$0.12/image | 1024×1024, 1536×1024, 1024×1536 | OpenAI GPT Image 2 |
61
- | `bytedance/seedream-5-pro` | `seedream` | $0.045–$0.09/image | 1024×1024, 1280×720, 2048×1024, 2048×2048, 2304×1728, 1728×2304, 2848×1600, 1600×2848 | ByteDance Seedream 5 |
62
- | `xai/grok-imagine-image` | `grok-imagine` | $0.02/image | 1024×1024 | xAI Grok Imagine |
63
- | `xai/grok-imagine-image-pro` | `grok-imagine-pro` | $0.07/image | 1024×1024 | xAI Grok Imagine Pro |
64
- | `zai/cogview-4` | `cogview` | $0.015–$0.02/image | 512×512, 768×768, 1024×1024, 768×1344, 1344×768, 1440×1440 | Zhipu CogView-4 |
54
+ | Model ID | Shorthand | Price | Sizes | Provider |
55
+ | ---------------------------- | ------------------ | ------------------ | ------------------------------------------------------------------------------------- | -------------------- |
56
+ | `google/nano-banana` | `nano-banana` | $0.05/image | 1024×1024 | Google Gemini Flash |
57
+ | `google/nano-banana-2` | `banana-2` | $0.09/image | 1024×1024 | Google Nano Banana 2 |
58
+ | `google/nano-banana-pro` | `banana-pro` | $0.10–$0.15/image | 1024×1024, 2048×2048, 4096×4096 | Google Gemini Pro |
59
+ | `openai/gpt-image-1` | `gpt-image` | $0.02–$0.04/image | 1024×1024, 1536×1024, 1024×1536 | OpenAI GPT Image 1 |
60
+ | `openai/gpt-image-2` | `gpt-image-2` | $0.06–$0.12/image | 1024×1024, 1536×1024, 1024×1536 | OpenAI GPT Image 2 |
61
+ | `bytedance/seedream-5-pro` | `seedream` | $0.045–$0.09/image | 1024×1024, 1280×720, 2048×1024, 2048×2048, 2304×1728, 1728×2304, 2848×1600, 1600×2848 | ByteDance Seedream 5 |
62
+ | `xai/grok-imagine-image` | `grok-imagine` | $0.02/image | 1024×1024 | xAI Grok Imagine |
63
+ | `xai/grok-imagine-image-pro` | `grok-imagine-pro` | $0.07/image | 1024×1024 | xAI Grok Imagine Pro |
64
+ | `zai/cogview-4` | `cogview` | $0.015–$0.02/image | 512×512, 768×768, 1024×1024, 768×1344, 1344×768, 1440×1440 | Zhipu CogView-4 |
65
65
 
66
66
  Default model: `google/nano-banana`.
67
67
 
@@ -369,18 +369,18 @@ When using ClawRouter with OpenClaw, generate and edit images directly from any
369
369
 
370
370
  ### Model shorthands
371
371
 
372
- | Shorthand | Full ID |
373
- | ------------------------------------ | ---------------------------- |
374
- | `nano-banana`, `banana` | `google/nano-banana` |
375
- | `banana-2`, `nano-banana-2` | `google/nano-banana-2` |
376
- | `banana-pro`, `nano-banana-pro` | `google/nano-banana-pro` |
377
- | `gpt-image`, `gpt-image-1` | `openai/gpt-image-1` |
378
- | `gpt-image-2` | `openai/gpt-image-2` |
379
- | `seedream` | `bytedance/seedream-5-pro` |
380
- | `grok-imagine` | `xai/grok-imagine-image` |
381
- | `grok-imagine-pro` | `xai/grok-imagine-image-pro` |
382
- | `cogview` | `zai/cogview-4` |
383
- | `dall-e-3`, `dalle3`, `dalle` (legacy) | `openai/gpt-image-2` |
372
+ | Shorthand | Full ID |
373
+ | -------------------------------------- | ---------------------------- |
374
+ | `nano-banana`, `banana` | `google/nano-banana` |
375
+ | `banana-2`, `nano-banana-2` | `google/nano-banana-2` |
376
+ | `banana-pro`, `nano-banana-pro` | `google/nano-banana-pro` |
377
+ | `gpt-image`, `gpt-image-1` | `openai/gpt-image-1` |
378
+ | `gpt-image-2` | `openai/gpt-image-2` |
379
+ | `seedream` | `bytedance/seedream-5-pro` |
380
+ | `grok-imagine` | `xai/grok-imagine-image` |
381
+ | `grok-imagine-pro` | `xai/grok-imagine-image-pro` |
382
+ | `cogview` | `zai/cogview-4` |
383
+ | `dall-e-3`, `dalle3`, `dalle` (legacy) | `openai/gpt-image-2` |
384
384
 
385
385
  ---
386
386
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/clawrouter",
3
- "version": "0.12.247",
3
+ "version": "0.12.249",
4
4
  "description": "Smart LLM router — save 88% on inference costs. 71 models (5 free), one wallet, x402 micropayments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -44,6 +44,7 @@
44
44
  "dev": "tsup --watch",
45
45
  "test": "vitest run",
46
46
  "test:watch": "vitest",
47
+ "test:integration": "vitest run --config vitest.integration.config.ts",
47
48
  "typecheck": "tsc --noEmit",
48
49
  "lint": "eslint src/",
49
50
  "format": "prettier --write .",
@@ -164,7 +164,7 @@ Realtime prices and historical OHLC across every asset class. The agent should c
164
164
 
165
165
  | Tool | Purpose | Price |
166
166
  | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
167
- | `blockrun_image_generation` | 9 image models — GPT Image 1/2, Nano Banana / 2 / Pro, Seedream 5 Pro, Grok Imagine / Pro, CogView-4 | $0.015–$0.15 / image |
167
+ | `blockrun_image_generation` | 9 image models — GPT Image 1/2, Nano Banana / 2 / Pro, Seedream 5 Pro, Grok Imagine / Pro, CogView-4 | $0.015–$0.15 / image |
168
168
  | `blockrun_image_edit` | Edit / inpaint existing image (openai/gpt-image-1) | $0.02–$0.04 / image |
169
169
  | `blockrun_video_generation` | Grok Imagine + ByteDance Seedance (1.5-pro / 2.0-fast / 2.0) at 720p with synced audio (t2v default), 5–10s, + OpenAI Sora 2 via Azure (`azure/sora-2`, 4/8/12s, t2v + i2v, rejects human faces in reference images). Seedance is token-priced upstream (~20,256 tokens/sec at the 720p+audio default — 2× the prior 480p rate); `image_url` (image-to-video) costs the same as t2v since 2026-06-01. Seedance 2.0 variants accept optional `real_face_asset_id` (`ta_…`) for BytePlus RealFace character-consistency — mutually exclusive with `image_url`. | $0.05/s (Grok); $0.10/s (Sora 2); Seedance ~$0.46–$1.49 per 5s clip |
170
170