@blockrun/clawrouter 0.12.64 → 0.12.66
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 +55 -55
- package/dist/cli.js +70 -25
- package/dist/cli.js.map +1 -1
- package/dist/index.js +77 -27
- package/dist/index.js.map +1 -1
- package/docs/anthropic-cost-savings.md +90 -85
- package/docs/architecture.md +12 -12
- package/docs/{blog-openclaw-cost-overruns.md → clawrouter-cuts-llm-api-costs-500x.md} +27 -27
- package/docs/clawrouter-vs-openrouter-llm-routing-comparison.md +280 -0
- package/docs/configuration.md +2 -2
- package/docs/image-generation.md +39 -39
- package/docs/{blog-benchmark-2026-03.md → llm-router-benchmark-46-models-sub-1ms-routing.md} +61 -64
- package/docs/routing-profiles.md +6 -6
- package/docs/{technical-routing-2026-03.md → smart-llm-router-14-dimension-classifier.md} +29 -28
- package/docs/worker-network.md +438 -347
- package/package.json +1 -1
- package/scripts/reinstall.sh +31 -6
- package/scripts/update.sh +6 -1
- package/docs/vs-openrouter.md +0 -157
package/README.md
CHANGED
|
@@ -108,11 +108,11 @@ Choose your routing strategy with `/model <profile>`:
|
|
|
108
108
|
Request → Weighted Scorer (15 dimensions) → Tier → Best Model → Response
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
| Tier | ECO Model | AUTO Model
|
|
112
|
-
| --------- | ----------------------------------- |
|
|
113
|
-
| SIMPLE | nvidia/gpt-oss-120b (FREE) | kimi-k2.5 ($0.60/$3.00)
|
|
114
|
-
| MEDIUM | gemini-2.5-flash-lite ($0.10/$0.40) | grok-4-0709 ($0.20/$1.50)
|
|
115
|
-
| COMPLEX | gemini-2.5-flash-lite ($0.10/$0.40) | gemini-3.1-pro ($2/$12)
|
|
111
|
+
| Tier | ECO Model | AUTO Model | PREMIUM Model |
|
|
112
|
+
| --------- | ----------------------------------- | ------------------------------------- | ---------------------------- |
|
|
113
|
+
| SIMPLE | nvidia/gpt-oss-120b (FREE) | kimi-k2.5 ($0.60/$3.00) | kimi-k2.5 |
|
|
114
|
+
| MEDIUM | gemini-2.5-flash-lite ($0.10/$0.40) | grok-4-0709 ($0.20/$1.50) | gpt-5.3-codex ($1.75/$14.00) |
|
|
115
|
+
| COMPLEX | gemini-2.5-flash-lite ($0.10/$0.40) | gemini-3.1-pro ($2/$12) | claude-opus-4.6 ($5/$25) |
|
|
116
116
|
| REASONING | grok-4-fast ($0.20/$0.50) | grok-4-1-fast-reasoning ($0.20/$0.50) | claude-sonnet-4.6 ($3/$15) |
|
|
117
117
|
|
|
118
118
|
**Blended average: $2.05/M** vs $25/M for Claude Opus = **92% savings**
|
|
@@ -165,62 +165,62 @@ Edit existing images with `/img2img`:
|
|
|
165
165
|
|
|
166
166
|
### Budget Models (under $0.001/request)
|
|
167
167
|
|
|
168
|
-
| Model
|
|
169
|
-
|
|
|
170
|
-
| nvidia/gpt-oss-120b
|
|
171
|
-
| openai/gpt-5-nano
|
|
172
|
-
| openai/gpt-4.1-nano
|
|
173
|
-
| google/gemini-2.5-flash-lite
|
|
174
|
-
| openai/gpt-4o-mini
|
|
175
|
-
| xai/grok-4-fast
|
|
176
|
-
| xai/grok-4-fast-reasoning
|
|
177
|
-
| xai/grok-4-1-fast
|
|
178
|
-
| xai/grok-4-1-fast-reasoning
|
|
179
|
-
| xai/grok-4-0709
|
|
180
|
-
| openai/gpt-5-mini
|
|
181
|
-
| deepseek/deepseek-chat
|
|
182
|
-
| deepseek/deepseek-reasoner
|
|
183
|
-
| xai/grok-3-mini
|
|
184
|
-
| minimax/minimax-m2.7
|
|
185
|
-
| minimax/minimax-m2.5
|
|
186
|
-
| google/gemini-2.5-flash
|
|
187
|
-
| openai/gpt-4.1-mini
|
|
188
|
-
| google/gemini-3-flash-preview |
|
|
189
|
-
| nvidia/kimi-k2.5
|
|
190
|
-
| moonshot/kimi-k2.5
|
|
168
|
+
| Model | Input $/M | Output $/M | ~$/request | Context | Features |
|
|
169
|
+
| ----------------------------- | --------: | ---------: | ---------: | ------- | --------------------------------- |
|
|
170
|
+
| nvidia/gpt-oss-120b | **FREE** | **FREE** | **$0** | 128K | |
|
|
171
|
+
| openai/gpt-5-nano | $0.05 | $0.40 | $0.0002 | 128K | tools |
|
|
172
|
+
| openai/gpt-4.1-nano | $0.10 | $0.40 | $0.0003 | 128K | tools |
|
|
173
|
+
| google/gemini-2.5-flash-lite | $0.10 | $0.40 | $0.0003 | 1M | tools |
|
|
174
|
+
| openai/gpt-4o-mini | $0.15 | $0.60 | $0.0004 | 128K | tools |
|
|
175
|
+
| xai/grok-4-fast | $0.20 | $0.50 | $0.0004 | 131K | tools |
|
|
176
|
+
| xai/grok-4-fast-reasoning | $0.20 | $0.50 | $0.0004 | 131K | reasoning, tools |
|
|
177
|
+
| xai/grok-4-1-fast | $0.20 | $0.50 | $0.0004 | 131K | tools |
|
|
178
|
+
| xai/grok-4-1-fast-reasoning | $0.20 | $0.50 | $0.0004 | 131K | reasoning, tools |
|
|
179
|
+
| xai/grok-4-0709 | $0.20 | $1.50 | $0.0009 | 131K | reasoning, tools |
|
|
180
|
+
| openai/gpt-5-mini | $0.25 | $2.00 | $0.0011 | 200K | tools |
|
|
181
|
+
| deepseek/deepseek-chat | $0.28 | $0.42 | $0.0004 | 128K | tools |
|
|
182
|
+
| deepseek/deepseek-reasoner | $0.28 | $0.42 | $0.0004 | 128K | reasoning, tools |
|
|
183
|
+
| xai/grok-3-mini | $0.30 | $0.50 | $0.0004 | 131K | tools |
|
|
184
|
+
| minimax/minimax-m2.7 | $0.30 | $1.20 | $0.0008 | 205K | reasoning, agentic, tools |
|
|
185
|
+
| minimax/minimax-m2.5 | $0.30 | $1.20 | $0.0008 | 205K | reasoning, agentic, tools |
|
|
186
|
+
| google/gemini-2.5-flash | $0.30 | $2.50 | $0.0014 | 1M | vision, tools |
|
|
187
|
+
| openai/gpt-4.1-mini | $0.40 | $1.60 | $0.0010 | 128K | tools |
|
|
188
|
+
| google/gemini-3-flash-preview | $0.50 | $3.00 | $0.0018 | 1M | vision |
|
|
189
|
+
| nvidia/kimi-k2.5 | $0.55 | $2.50 | $0.0015 | 262K | tools |
|
|
190
|
+
| moonshot/kimi-k2.5 | $0.60 | $3.00 | $0.0018 | 262K | reasoning, vision, agentic, tools |
|
|
191
191
|
|
|
192
192
|
### Mid-Range Models ($0.001–$0.01/request)
|
|
193
193
|
|
|
194
|
-
| Model
|
|
195
|
-
|
|
|
196
|
-
| anthropic/claude-haiku-4.5
|
|
197
|
-
| zai/glm-5
|
|
198
|
-
| openai/o1-mini
|
|
199
|
-
| openai/o3-mini
|
|
200
|
-
| openai/o4-mini
|
|
201
|
-
| zai/glm-5-turbo
|
|
202
|
-
| google/gemini-2.5-pro
|
|
203
|
-
| openai/gpt-5.2
|
|
204
|
-
| openai/gpt-5.3
|
|
205
|
-
| openai/gpt-5.3-codex
|
|
206
|
-
| openai/gpt-4.1
|
|
207
|
-
| openai/o3
|
|
208
|
-
| google/gemini-3-pro-preview |
|
|
209
|
-
| google/gemini-3.1-pro
|
|
210
|
-
| xai/grok-2-vision
|
|
211
|
-
| openai/gpt-4o
|
|
212
|
-
| openai/gpt-5.4
|
|
194
|
+
| Model | Input $/M | Output $/M | ~$/request | Context | Features |
|
|
195
|
+
| --------------------------- | --------: | ---------: | ---------: | ------- | --------------------------------- |
|
|
196
|
+
| anthropic/claude-haiku-4.5 | $1.00 | $5.00 | $0.0030 | 200K | vision, agentic, tools |
|
|
197
|
+
| zai/glm-5 | $1.00 | $3.20 | $0.0021 | 200K | tools |
|
|
198
|
+
| openai/o1-mini | $1.10 | $4.40 | $0.0028 | 128K | reasoning, tools |
|
|
199
|
+
| openai/o3-mini | $1.10 | $4.40 | $0.0028 | 128K | reasoning, tools |
|
|
200
|
+
| openai/o4-mini | $1.10 | $4.40 | $0.0028 | 128K | reasoning, tools |
|
|
201
|
+
| zai/glm-5-turbo | $1.20 | $4.00 | $0.0026 | 200K | tools |
|
|
202
|
+
| google/gemini-2.5-pro | $1.25 | $10.00 | $0.0056 | 1M | reasoning, vision, tools |
|
|
203
|
+
| openai/gpt-5.2 | $1.75 | $14.00 | $0.0079 | 400K | reasoning, vision, agentic, tools |
|
|
204
|
+
| openai/gpt-5.3 | $1.75 | $14.00 | $0.0079 | 128K | reasoning, vision, agentic, tools |
|
|
205
|
+
| openai/gpt-5.3-codex | $1.75 | $14.00 | $0.0079 | 400K | agentic, tools |
|
|
206
|
+
| openai/gpt-4.1 | $2.00 | $8.00 | $0.0050 | 128K | vision, tools |
|
|
207
|
+
| openai/o3 | $2.00 | $8.00 | $0.0050 | 200K | reasoning, tools |
|
|
208
|
+
| google/gemini-3-pro-preview | $2.00 | $12.00 | $0.0070 | 1M | reasoning, vision, tools |
|
|
209
|
+
| google/gemini-3.1-pro | $2.00 | $12.00 | $0.0070 | 1M | reasoning, vision, tools |
|
|
210
|
+
| xai/grok-2-vision | $2.00 | $10.00 | $0.0060 | 131K | vision, tools |
|
|
211
|
+
| openai/gpt-4o | $2.50 | $10.00 | $0.0063 | 128K | vision, agentic, tools |
|
|
212
|
+
| openai/gpt-5.4 | $2.50 | $15.00 | $0.0088 | 400K | reasoning, vision, agentic, tools |
|
|
213
213
|
|
|
214
214
|
### Premium Models ($0.01+/request)
|
|
215
215
|
|
|
216
|
-
| Model
|
|
217
|
-
|
|
|
218
|
-
| anthropic/claude-sonnet-4.6 |
|
|
219
|
-
| xai/grok-3
|
|
220
|
-
| anthropic/claude-opus-4.6
|
|
221
|
-
| openai/o1
|
|
222
|
-
| openai/gpt-5.2-pro
|
|
223
|
-
| openai/gpt-5.4-pro
|
|
216
|
+
| Model | Input $/M | Output $/M | ~$/request | Context | Features |
|
|
217
|
+
| --------------------------- | --------: | ---------: | ---------: | ------- | --------------------------------- |
|
|
218
|
+
| anthropic/claude-sonnet-4.6 | $3.00 | $15.00 | $0.0090 | 200K | reasoning, vision, agentic, tools |
|
|
219
|
+
| xai/grok-3 | $3.00 | $15.00 | $0.0090 | 131K | reasoning, tools |
|
|
220
|
+
| anthropic/claude-opus-4.6 | $5.00 | $25.00 | $0.0150 | 200K | reasoning, vision, agentic, tools |
|
|
221
|
+
| openai/o1 | $15.00 | $60.00 | $0.0375 | 200K | reasoning, tools |
|
|
222
|
+
| openai/gpt-5.2-pro | $21.00 | $168.00 | $0.0945 | 400K | reasoning, tools |
|
|
223
|
+
| openai/gpt-5.4-pro | $30.00 | $180.00 | $0.1050 | 400K | reasoning, tools |
|
|
224
224
|
|
|
225
225
|
> **Free tier:** `nvidia/gpt-oss-120b` costs nothing and serves as automatic fallback when wallet is empty.
|
|
226
226
|
> **Best value:** `gpt-5-nano` and `gemini-2.5-flash-lite` deliver strong results at ~$0.0003/request.
|
package/dist/cli.js
CHANGED
|
@@ -37975,7 +37975,19 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
37975
37975
|
return async (input, init) => {
|
|
37976
37976
|
const request = new Request(input, init);
|
|
37977
37977
|
const urlPath = new URL(request.url).pathname;
|
|
37978
|
-
|
|
37978
|
+
let requestModel = "";
|
|
37979
|
+
if (init?.body) {
|
|
37980
|
+
try {
|
|
37981
|
+
const bodyStr = init.body instanceof Uint8Array ? new TextDecoder().decode(init.body) : typeof init.body === "string" ? init.body : "";
|
|
37982
|
+
if (bodyStr) {
|
|
37983
|
+
const parsed = JSON.parse(bodyStr);
|
|
37984
|
+
requestModel = parsed.model ?? "";
|
|
37985
|
+
}
|
|
37986
|
+
} catch {
|
|
37987
|
+
}
|
|
37988
|
+
}
|
|
37989
|
+
const cacheKey2 = `${urlPath}:${requestModel}`;
|
|
37990
|
+
const cached = !options?.skipPreAuth ? cache2.get(cacheKey2) : void 0;
|
|
37979
37991
|
if (cached && Date.now() - cached.cachedAt < ttlMs) {
|
|
37980
37992
|
try {
|
|
37981
37993
|
const payload2 = await client.createPaymentPayload(cached.paymentRequired);
|
|
@@ -37988,9 +38000,9 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
37988
38000
|
if (response2.status !== 402) {
|
|
37989
38001
|
return response2;
|
|
37990
38002
|
}
|
|
37991
|
-
cache2.delete(
|
|
38003
|
+
cache2.delete(cacheKey2);
|
|
37992
38004
|
} catch {
|
|
37993
|
-
cache2.delete(
|
|
38005
|
+
cache2.delete(cacheKey2);
|
|
37994
38006
|
}
|
|
37995
38007
|
}
|
|
37996
38008
|
const clonedRequest = request.clone();
|
|
@@ -38013,7 +38025,7 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
|
|
|
38013
38025
|
} catch {
|
|
38014
38026
|
}
|
|
38015
38027
|
paymentRequired = httpClient.getPaymentRequiredResponse(getHeader, body);
|
|
38016
|
-
cache2.set(
|
|
38028
|
+
cache2.set(cacheKey2, { paymentRequired, cachedAt: Date.now() });
|
|
38017
38029
|
} catch (error) {
|
|
38018
38030
|
throw new Error(
|
|
38019
38031
|
`Failed to parse payment requirements: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
@@ -46367,12 +46379,7 @@ async function checkForUpdates() {
|
|
|
46367
46379
|
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
46368
46380
|
import { join as join7, dirname as dirname2 } from "path";
|
|
46369
46381
|
import { homedir as homedir4 } from "os";
|
|
46370
|
-
var DEFAULT_FILE_PATH = join7(
|
|
46371
|
-
homedir4(),
|
|
46372
|
-
".openclaw",
|
|
46373
|
-
"blockrun",
|
|
46374
|
-
"exclude-models.json"
|
|
46375
|
-
);
|
|
46382
|
+
var DEFAULT_FILE_PATH = join7(homedir4(), ".openclaw", "blockrun", "exclude-models.json");
|
|
46376
46383
|
function loadExcludeList(filePath = DEFAULT_FILE_PATH) {
|
|
46377
46384
|
try {
|
|
46378
46385
|
const raw = readFileSync(filePath, "utf-8");
|
|
@@ -46752,8 +46759,7 @@ function categorizeError(status, body) {
|
|
|
46752
46759
|
if (status === 401) return "auth_failure";
|
|
46753
46760
|
if (status === 402) return "payment_error";
|
|
46754
46761
|
if (status === 403) {
|
|
46755
|
-
if (/plan.*limit|quota.*exceeded|subscription|allowance/i.test(body))
|
|
46756
|
-
return "quota_exceeded";
|
|
46762
|
+
if (/plan.*limit|quota.*exceeded|subscription|allowance/i.test(body)) return "quota_exceeded";
|
|
46757
46763
|
return "auth_failure";
|
|
46758
46764
|
}
|
|
46759
46765
|
if (status === 429) return "rate_limited";
|
|
@@ -49040,6 +49046,7 @@ data: [DONE]
|
|
|
49040
49046
|
let upstream;
|
|
49041
49047
|
let lastError;
|
|
49042
49048
|
let actualModelUsed = modelId;
|
|
49049
|
+
const failedAttempts = [];
|
|
49043
49050
|
for (let i = 0; i < modelsToTry.length; i++) {
|
|
49044
49051
|
const tryModel = modelsToTry[i];
|
|
49045
49052
|
const isLastAttempt = i === modelsToTry.length - 1;
|
|
@@ -49088,6 +49095,31 @@ data: [DONE]
|
|
|
49088
49095
|
body: result.errorBody || "Unknown error",
|
|
49089
49096
|
status: result.errorStatus || 500
|
|
49090
49097
|
};
|
|
49098
|
+
failedAttempts.push({
|
|
49099
|
+
model: tryModel,
|
|
49100
|
+
reason: result.errorCategory || `HTTP ${result.errorStatus || 500}`,
|
|
49101
|
+
status: result.errorStatus || 500
|
|
49102
|
+
});
|
|
49103
|
+
const isPaymentErr = /payment.*verification.*failed|payment.*settlement.*failed|insufficient.*funds|transaction_simulation_failed/i.test(
|
|
49104
|
+
result.errorBody || ""
|
|
49105
|
+
);
|
|
49106
|
+
if (isPaymentErr && tryModel !== FREE_MODEL && !isLastAttempt) {
|
|
49107
|
+
failedAttempts.push({
|
|
49108
|
+
...failedAttempts[failedAttempts.length - 1],
|
|
49109
|
+
reason: "payment_error"
|
|
49110
|
+
});
|
|
49111
|
+
const freeIdx = modelsToTry.indexOf(FREE_MODEL);
|
|
49112
|
+
if (freeIdx > i + 1) {
|
|
49113
|
+
console.log(`[ClawRouter] Payment error \u2014 skipping to free model: ${FREE_MODEL}`);
|
|
49114
|
+
i = freeIdx - 1;
|
|
49115
|
+
continue;
|
|
49116
|
+
}
|
|
49117
|
+
if (freeIdx === -1) {
|
|
49118
|
+
modelsToTry.push(FREE_MODEL);
|
|
49119
|
+
console.log(`[ClawRouter] Payment error \u2014 appending free model: ${FREE_MODEL}`);
|
|
49120
|
+
continue;
|
|
49121
|
+
}
|
|
49122
|
+
}
|
|
49091
49123
|
if (result.isProviderError && !isLastAttempt) {
|
|
49092
49124
|
const isExplicitModelError = !routingDecision;
|
|
49093
49125
|
const isUnknownExplicitModel = isExplicitModelError && /unknown.*model|invalid.*model/i.test(result.errorBody || "");
|
|
@@ -49165,17 +49197,6 @@ data: [DONE]
|
|
|
49165
49197
|
`[ClawRouter] \u{1F511} ${errorCat === "auth_failure" ? "Auth failure" : "Quota exceeded"} for ${tryModel} \u2014 check provider config`
|
|
49166
49198
|
);
|
|
49167
49199
|
}
|
|
49168
|
-
const isPaymentErr = /payment.*verification.*failed|payment.*settlement.*failed|insufficient.*funds|transaction_simulation_failed/i.test(
|
|
49169
|
-
result.errorBody || ""
|
|
49170
|
-
);
|
|
49171
|
-
if (isPaymentErr && tryModel !== FREE_MODEL) {
|
|
49172
|
-
const freeIdx = modelsToTry.indexOf(FREE_MODEL);
|
|
49173
|
-
if (freeIdx > i + 1) {
|
|
49174
|
-
console.log(`[ClawRouter] Payment error \u2014 skipping to free model: ${FREE_MODEL}`);
|
|
49175
|
-
i = freeIdx - 1;
|
|
49176
|
-
continue;
|
|
49177
|
-
}
|
|
49178
|
-
}
|
|
49179
49200
|
console.log(
|
|
49180
49201
|
`[ClawRouter] Provider error from ${tryModel}, trying fallback: ${result.errorBody?.slice(0, 100)}`
|
|
49181
49202
|
);
|
|
@@ -49225,7 +49246,10 @@ data: [DONE]
|
|
|
49225
49246
|
}
|
|
49226
49247
|
}
|
|
49227
49248
|
if (!upstream) {
|
|
49228
|
-
const
|
|
49249
|
+
const attemptSummary = failedAttempts.length > 0 ? failedAttempts.map((a) => `${a.model} (${a.reason})`).join(", ") : "unknown";
|
|
49250
|
+
const structuredMessage = failedAttempts.length > 0 ? `All ${failedAttempts.length} models failed. Tried: ${attemptSummary}` : "All models in fallback chain failed";
|
|
49251
|
+
console.log(`[ClawRouter] ${structuredMessage}`);
|
|
49252
|
+
const rawErrBody = lastError?.body || structuredMessage;
|
|
49229
49253
|
const errStatus = lastError?.status || 502;
|
|
49230
49254
|
const transformedErr = transformPaymentError(rawErrBody);
|
|
49231
49255
|
if (headersSentEarly) {
|
|
@@ -49284,7 +49308,7 @@ data: [DONE]
|
|
|
49284
49308
|
id: rsp.id ?? `chatcmpl-${Date.now()}`,
|
|
49285
49309
|
object: "chat.completion.chunk",
|
|
49286
49310
|
created: rsp.created ?? Math.floor(Date.now() / 1e3),
|
|
49287
|
-
model: rsp.model
|
|
49311
|
+
model: actualModelUsed || rsp.model || "unknown",
|
|
49288
49312
|
system_fingerprint: null
|
|
49289
49313
|
};
|
|
49290
49314
|
if (rsp.choices && Array.isArray(rsp.choices)) {
|
|
@@ -49399,6 +49423,13 @@ data: [DONE]
|
|
|
49399
49423
|
responseChunks.push(Buffer.from(sseData));
|
|
49400
49424
|
}
|
|
49401
49425
|
}
|
|
49426
|
+
if (routingDecision) {
|
|
49427
|
+
const costComment = `: cost=$${routingDecision.costEstimate.toFixed(4)} savings=${(routingDecision.savings * 100).toFixed(0)}% model=${actualModelUsed} tier=${routingDecision.tier}
|
|
49428
|
+
|
|
49429
|
+
`;
|
|
49430
|
+
safeWrite(res, costComment);
|
|
49431
|
+
responseChunks.push(Buffer.from(costComment));
|
|
49432
|
+
}
|
|
49402
49433
|
safeWrite(res, "data: [DONE]\n\n");
|
|
49403
49434
|
responseChunks.push(Buffer.from("data: [DONE]\n\n"));
|
|
49404
49435
|
res.end();
|
|
@@ -49427,6 +49458,10 @@ data: [DONE]
|
|
|
49427
49458
|
responseHeaders["x-clawrouter-agentic-score"] = routingDecision.agenticScore.toFixed(2);
|
|
49428
49459
|
}
|
|
49429
49460
|
}
|
|
49461
|
+
if (routingDecision) {
|
|
49462
|
+
responseHeaders["x-clawrouter-cost"] = routingDecision.costEstimate.toFixed(6);
|
|
49463
|
+
responseHeaders["x-clawrouter-savings"] = `${(routingDecision.savings * 100).toFixed(0)}%`;
|
|
49464
|
+
}
|
|
49430
49465
|
const bodyParts = [];
|
|
49431
49466
|
if (upstream.body) {
|
|
49432
49467
|
const chunks = await readBodyWithTimeout(upstream.body);
|
|
@@ -49457,6 +49492,16 @@ data: [DONE]
|
|
|
49457
49492
|
}
|
|
49458
49493
|
budgetDowngradeNotice = void 0;
|
|
49459
49494
|
}
|
|
49495
|
+
if (actualModelUsed && responseBody.length > 0) {
|
|
49496
|
+
try {
|
|
49497
|
+
const parsed = JSON.parse(responseBody.toString());
|
|
49498
|
+
if (parsed.model !== void 0) {
|
|
49499
|
+
parsed.model = actualModelUsed;
|
|
49500
|
+
responseBody = Buffer.from(JSON.stringify(parsed));
|
|
49501
|
+
}
|
|
49502
|
+
} catch {
|
|
49503
|
+
}
|
|
49504
|
+
}
|
|
49460
49505
|
if (budgetDowngradeHeaderMode) {
|
|
49461
49506
|
responseHeaders["x-clawrouter-budget-downgrade"] = "1";
|
|
49462
49507
|
responseHeaders["x-clawrouter-budget-mode"] = budgetDowngradeHeaderMode;
|