@blockrun/clawrouter 0.12.62 → 0.12.63

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.
Files changed (31) hide show
  1. package/docs/anthropic-cost-savings.md +349 -0
  2. package/docs/architecture.md +559 -0
  3. package/docs/assets/blockrun-248-day-cost-overrun-problem.png +0 -0
  4. package/docs/assets/blockrun-clawrouter-7-layer-token-compression-openclaw.png +0 -0
  5. package/docs/assets/blockrun-clawrouter-observation-compression-97-percent-token-savings.png +0 -0
  6. package/docs/assets/blockrun-clawrouter-openclaw-agentic-proxy-architecture.png +0 -0
  7. package/docs/assets/blockrun-clawrouter-openclaw-automatic-tier-routing-model-selection.png +0 -0
  8. package/docs/assets/blockrun-clawrouter-openclaw-error-classification-retry-storm-prevention.png +0 -0
  9. package/docs/assets/blockrun-clawrouter-openclaw-session-memory-journaling-vs-context-compounding.png +0 -0
  10. package/docs/assets/blockrun-clawrouter-vs-openclaw-standalone-comparison-production-safety.png +0 -0
  11. package/docs/assets/blockrun-clawrouter-x402-usdc-micropayment-wallet-budget-control.png +0 -0
  12. package/docs/assets/blockrun-openclaw-inference-layer-blind-spots.png +0 -0
  13. package/docs/blog-benchmark-2026-03.md +184 -0
  14. package/docs/blog-openclaw-cost-overruns.md +197 -0
  15. package/docs/clawrouter-savings.png +0 -0
  16. package/docs/configuration.md +512 -0
  17. package/docs/features.md +257 -0
  18. package/docs/image-generation.md +380 -0
  19. package/docs/plans/2026-02-03-smart-routing-design.md +267 -0
  20. package/docs/plans/2026-02-13-e2e-docker-deployment.md +1260 -0
  21. package/docs/plans/2026-02-28-worker-network.md +947 -0
  22. package/docs/plans/2026-03-18-error-classification.md +574 -0
  23. package/docs/plans/2026-03-19-exclude-models.md +538 -0
  24. package/docs/routing-profiles.md +81 -0
  25. package/docs/subscription-failover.md +320 -0
  26. package/docs/technical-routing-2026-03.md +322 -0
  27. package/docs/troubleshooting.md +159 -0
  28. package/docs/vision.md +49 -0
  29. package/docs/vs-openrouter.md +157 -0
  30. package/docs/worker-network.md +1241 -0
  31. package/package.json +2 -1
@@ -0,0 +1,538 @@
1
+ # Exclude Models Feature — Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Let users exclude specific models from routing via `/exclude` Telegram command, persisted to disk.
6
+
7
+ **Architecture:** New `exclude-models.json` file at `~/.openclaw/blockrun/` stores the exclusion list. A `filterByExcludeList()` function in `selector.ts` filters the fallback chain (same safety pattern as existing filters). The `/exclude` command manages the list via add/remove/clear subcommands. The proxy loads the list at startup and re-reads on each request (hot-reload).
8
+
9
+ **Tech Stack:** TypeScript, Node.js fs, existing ClawRouter command pattern
10
+
11
+ ---
12
+
13
+ ### Task 1: Exclude List Persistence Module
14
+
15
+ **Files:**
16
+ - Create: `src/exclude-models.ts`
17
+ - Test: `src/exclude-models.test.ts`
18
+
19
+ **Step 1: Write the failing test**
20
+
21
+ ```typescript
22
+ // src/exclude-models.test.ts
23
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
24
+ import { join } from "node:path";
25
+ import { mkdirSync, rmSync, existsSync } from "node:fs";
26
+ import { tmpdir } from "node:os";
27
+ import {
28
+ loadExcludeList,
29
+ addExclusion,
30
+ removeExclusion,
31
+ clearExclusions,
32
+ } from "./exclude-models.js";
33
+
34
+ const TEST_DIR = join(tmpdir(), "clawrouter-test-exclude-" + Date.now());
35
+ const TEST_FILE = join(TEST_DIR, "exclude-models.json");
36
+
37
+ describe("exclude-models", () => {
38
+ beforeEach(() => {
39
+ mkdirSync(TEST_DIR, { recursive: true });
40
+ });
41
+
42
+ afterEach(() => {
43
+ rmSync(TEST_DIR, { recursive: true, force: true });
44
+ });
45
+
46
+ it("returns empty set when file does not exist", () => {
47
+ const list = loadExcludeList(TEST_FILE);
48
+ expect(list.size).toBe(0);
49
+ });
50
+
51
+ it("adds a model and persists to disk", () => {
52
+ addExclusion("nvidia/gpt-oss-120b", TEST_FILE);
53
+ const list = loadExcludeList(TEST_FILE);
54
+ expect(list.has("nvidia/gpt-oss-120b")).toBe(true);
55
+ });
56
+
57
+ it("removes a model", () => {
58
+ addExclusion("nvidia/gpt-oss-120b", TEST_FILE);
59
+ addExclusion("xai/grok-4-0709", TEST_FILE);
60
+ removeExclusion("nvidia/gpt-oss-120b", TEST_FILE);
61
+ const list = loadExcludeList(TEST_FILE);
62
+ expect(list.has("nvidia/gpt-oss-120b")).toBe(false);
63
+ expect(list.has("xai/grok-4-0709")).toBe(true);
64
+ });
65
+
66
+ it("clears all exclusions", () => {
67
+ addExclusion("nvidia/gpt-oss-120b", TEST_FILE);
68
+ addExclusion("xai/grok-4-0709", TEST_FILE);
69
+ clearExclusions(TEST_FILE);
70
+ const list = loadExcludeList(TEST_FILE);
71
+ expect(list.size).toBe(0);
72
+ });
73
+
74
+ it("deduplicates entries", () => {
75
+ addExclusion("nvidia/gpt-oss-120b", TEST_FILE);
76
+ addExclusion("nvidia/gpt-oss-120b", TEST_FILE);
77
+ const list = loadExcludeList(TEST_FILE);
78
+ expect(list.size).toBe(1);
79
+ });
80
+
81
+ it("resolves aliases before storing", () => {
82
+ // "free" alias → "nvidia/gpt-oss-120b"
83
+ addExclusion("free", TEST_FILE);
84
+ const list = loadExcludeList(TEST_FILE);
85
+ expect(list.has("nvidia/gpt-oss-120b")).toBe(true);
86
+ });
87
+ });
88
+ ```
89
+
90
+ **Step 2: Run test to verify it fails**
91
+
92
+ Run: `npx vitest run src/exclude-models.test.ts`
93
+ Expected: FAIL — module `./exclude-models.js` does not exist
94
+
95
+ **Step 3: Write minimal implementation**
96
+
97
+ ```typescript
98
+ // src/exclude-models.ts
99
+ /**
100
+ * Exclude Models — persistent user-configurable model exclusion list.
101
+ *
102
+ * Stores excluded model IDs in ~/.openclaw/blockrun/exclude-models.json.
103
+ * Models in this list are filtered out of routing fallback chains.
104
+ */
105
+
106
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
107
+ import { dirname, join } from "node:path";
108
+ import { homedir } from "node:os";
109
+ import { resolveModelAlias } from "./models.js";
110
+
111
+ const DEFAULT_EXCLUDE_FILE = join(homedir(), ".openclaw", "blockrun", "exclude-models.json");
112
+
113
+ /**
114
+ * Load the exclude list from disk. Returns empty Set if file missing.
115
+ */
116
+ export function loadExcludeList(filePath: string = DEFAULT_EXCLUDE_FILE): Set<string> {
117
+ try {
118
+ const raw = readFileSync(filePath, "utf-8");
119
+ const arr = JSON.parse(raw);
120
+ if (Array.isArray(arr)) return new Set(arr);
121
+ return new Set();
122
+ } catch {
123
+ return new Set();
124
+ }
125
+ }
126
+
127
+ function save(models: Set<string>, filePath: string): void {
128
+ mkdirSync(dirname(filePath), { recursive: true });
129
+ writeFileSync(filePath, JSON.stringify([...models].sort(), null, 2) + "\n");
130
+ }
131
+
132
+ /**
133
+ * Add a model to the exclude list. Resolves aliases (e.g. "free" → "nvidia/gpt-oss-120b").
134
+ * Returns the resolved model ID.
135
+ */
136
+ export function addExclusion(model: string, filePath: string = DEFAULT_EXCLUDE_FILE): string {
137
+ const resolved = resolveModelAlias(model);
138
+ const list = loadExcludeList(filePath);
139
+ list.add(resolved);
140
+ save(list, filePath);
141
+ return resolved;
142
+ }
143
+
144
+ /**
145
+ * Remove a model from the exclude list. Returns true if it was present.
146
+ */
147
+ export function removeExclusion(model: string, filePath: string = DEFAULT_EXCLUDE_FILE): boolean {
148
+ const resolved = resolveModelAlias(model);
149
+ const list = loadExcludeList(filePath);
150
+ const had = list.delete(resolved);
151
+ if (had) save(list, filePath);
152
+ return had;
153
+ }
154
+
155
+ /**
156
+ * Clear all exclusions.
157
+ */
158
+ export function clearExclusions(filePath: string = DEFAULT_EXCLUDE_FILE): void {
159
+ save(new Set(), filePath);
160
+ }
161
+ ```
162
+
163
+ **Step 4: Run test to verify it passes**
164
+
165
+ Run: `npx vitest run src/exclude-models.test.ts`
166
+ Expected: PASS
167
+
168
+ **Step 5: Commit**
169
+
170
+ ```bash
171
+ git add src/exclude-models.ts src/exclude-models.test.ts
172
+ git commit -m "feat: add exclude-models persistence module"
173
+ ```
174
+
175
+ ---
176
+
177
+ ### Task 2: Filter Function in Selector
178
+
179
+ **Files:**
180
+ - Modify: `src/router/selector.ts` — add `filterByExcludeList()`
181
+ - Test: `src/router/selector.test.ts` — add tests
182
+
183
+ **Step 1: Write the failing test**
184
+
185
+ Add to `src/router/selector.test.ts`:
186
+
187
+ ```typescript
188
+ import { filterByExcludeList } from "./selector.js";
189
+
190
+ describe("filterByExcludeList", () => {
191
+ it("removes excluded models from chain", () => {
192
+ const chain = ["a/model-1", "b/model-2", "c/model-3"];
193
+ const excluded = new Set(["b/model-2"]);
194
+ expect(filterByExcludeList(chain, excluded)).toEqual(["a/model-1", "c/model-3"]);
195
+ });
196
+
197
+ it("returns original chain if all models excluded (safety net)", () => {
198
+ const chain = ["a/model-1", "b/model-2"];
199
+ const excluded = new Set(["a/model-1", "b/model-2"]);
200
+ expect(filterByExcludeList(chain, excluded)).toEqual(["a/model-1", "b/model-2"]);
201
+ });
202
+
203
+ it("returns original chain for empty exclude set", () => {
204
+ const chain = ["a/model-1", "b/model-2"];
205
+ expect(filterByExcludeList(chain, new Set())).toEqual(["a/model-1", "b/model-2"]);
206
+ });
207
+ });
208
+ ```
209
+
210
+ **Step 2: Run test to verify it fails**
211
+
212
+ Run: `npx vitest run src/router/selector.test.ts`
213
+ Expected: FAIL — `filterByExcludeList` not exported
214
+
215
+ **Step 3: Write minimal implementation**
216
+
217
+ Add to `src/router/selector.ts`:
218
+
219
+ ```typescript
220
+ /**
221
+ * Filter a model list to remove user-excluded models.
222
+ * When all models are excluded, returns the full list as a fallback
223
+ * (same safety pattern as filterByToolCalling/filterByVision).
224
+ */
225
+ export function filterByExcludeList(models: string[], excludeList: Set<string>): string[] {
226
+ if (excludeList.size === 0) return models;
227
+ const filtered = models.filter((m) => !excludeList.has(m));
228
+ return filtered.length > 0 ? filtered : models;
229
+ }
230
+ ```
231
+
232
+ **Step 4: Run test to verify it passes**
233
+
234
+ Run: `npx vitest run src/router/selector.test.ts`
235
+ Expected: PASS
236
+
237
+ **Step 5: Commit**
238
+
239
+ ```bash
240
+ git add src/router/selector.ts src/router/selector.test.ts
241
+ git commit -m "feat: add filterByExcludeList to router selector"
242
+ ```
243
+
244
+ ---
245
+
246
+ ### Task 3: Wire Exclude Filter into Proxy Fallback Chain
247
+
248
+ **Files:**
249
+ - Modify: `src/proxy.ts` — add exclude filter step, accept excludeList in ProxyOptions, load at startup
250
+
251
+ **Step 1: Add `excludeModels` to ProxyOptions**
252
+
253
+ In `src/proxy.ts` at line ~1174, add to `ProxyOptions`:
254
+
255
+ ```typescript
256
+ /**
257
+ * Set of model IDs to exclude from routing.
258
+ * Excluded models are filtered out of fallback chains.
259
+ * Loaded from ~/.openclaw/blockrun/exclude-models.json
260
+ */
261
+ excludeModels?: Set<string>;
262
+ ```
263
+
264
+ **Step 2: Wire filter into fallback chain building**
265
+
266
+ In `src/proxy.ts` around line 3606 (inside the `if (routingDecision)` block), after the context filter and before the tool-calling filter, add:
267
+
268
+ ```typescript
269
+ // Filter out user-excluded models
270
+ const excludeFiltered = filterByExcludeList(contextFiltered, options.excludeModels ?? new Set());
271
+ const excludeExcluded = contextFiltered.filter((m) => !excludeFiltered.includes(m));
272
+ if (excludeExcluded.length > 0) {
273
+ console.log(
274
+ `[ClawRouter] Exclude filter: excluded ${excludeExcluded.join(", ")} (user preference)`,
275
+ );
276
+ }
277
+ ```
278
+
279
+ Then update the next filter to chain from `excludeFiltered` instead of `contextFiltered`:
280
+
281
+ ```typescript
282
+ // Change: filterByToolCalling now takes excludeFiltered instead of contextFiltered
283
+ let toolFiltered = filterByToolCalling(excludeFiltered, hasTools, supportsToolCalling);
284
+ const toolExcluded = excludeFiltered.filter((m) => !toolFiltered.includes(m));
285
+ ```
286
+
287
+ **Step 3: Also filter the FREE_MODEL fallback at line 3674**
288
+
289
+ Change the free model fallback to respect exclusions:
290
+
291
+ ```typescript
292
+ // Ensure free model is the last-resort fallback for non-tool requests — unless user excluded it.
293
+ if (!hasTools && !modelsToTry.includes(FREE_MODEL) && !(options.excludeModels?.has(FREE_MODEL))) {
294
+ modelsToTry.push(FREE_MODEL);
295
+ }
296
+ ```
297
+
298
+ **Step 4: Add import for filterByExcludeList**
299
+
300
+ At the top of `proxy.ts`, add `filterByExcludeList` to the selector import:
301
+
302
+ ```typescript
303
+ import { selectModel, getFallbackChain, getFallbackChainFiltered, calculateModelCost, filterByToolCalling, filterByVision, filterByExcludeList } from "./router/selector.js";
304
+ ```
305
+
306
+ **Step 5: Load exclude list at proxy startup**
307
+
308
+ In `startProxy()` (around line 1426), load the exclude list and pass it through:
309
+
310
+ ```typescript
311
+ import { loadExcludeList } from "./exclude-models.js";
312
+
313
+ // Inside startProxy(), before creating the server:
314
+ const excludeModels = options.excludeModels ?? loadExcludeList();
315
+ // Pass excludeModels into the options object used by request handlers
316
+ ```
317
+
318
+ Note: Re-read from disk on each request for hot-reload (the file is tiny, cost is negligible):
319
+
320
+ ```typescript
321
+ // In the request handler, before building fallback chain:
322
+ const currentExcludeList = loadExcludeList();
323
+ ```
324
+
325
+ **Step 6: Run existing tests**
326
+
327
+ Run: `npx vitest run src/proxy.*.test.ts`
328
+ Expected: PASS (existing tests should still pass)
329
+
330
+ **Step 7: Commit**
331
+
332
+ ```bash
333
+ git add src/proxy.ts
334
+ git commit -m "feat: wire excludeModels filter into proxy fallback chain"
335
+ ```
336
+
337
+ ---
338
+
339
+ ### Task 4: `/exclude` Telegram Command
340
+
341
+ **Files:**
342
+ - Modify: `src/index.ts` — add `createExcludeCommand()` + register it
343
+
344
+ **Step 1: Create the command function**
345
+
346
+ Add to `src/index.ts` (after `createStatsCommand`):
347
+
348
+ ```typescript
349
+ import { loadExcludeList, addExclusion, removeExclusion, clearExclusions } from "./exclude-models.js";
350
+
351
+ async function createExcludeCommand(): Promise<OpenClawPluginCommandDefinition> {
352
+ return {
353
+ name: "exclude",
354
+ description: "Manage excluded models — /exclude add|remove|clear <model>",
355
+ acceptsArgs: true,
356
+ requireAuth: true,
357
+ handler: async (ctx: PluginCommandContext) => {
358
+ const args = ctx.args?.trim() || "";
359
+ const parts = args.split(/\s+/);
360
+ const subcommand = parts[0]?.toLowerCase() || "";
361
+ const modelArg = parts.slice(1).join(" ").trim();
362
+
363
+ // /exclude (no args) — show current list
364
+ if (!subcommand) {
365
+ const list = loadExcludeList();
366
+ if (list.size === 0) {
367
+ return {
368
+ text: "No models excluded.\n\nUsage:\n /exclude add <model> — block a model\n /exclude remove <model> — unblock\n /exclude clear — remove all",
369
+ };
370
+ }
371
+ const models = [...list].sort().map((m) => ` • ${m}`).join("\n");
372
+ return {
373
+ text: `Excluded models (${list.size}):\n${models}\n\nUse /exclude remove <model> to unblock.`,
374
+ };
375
+ }
376
+
377
+ // /exclude add <model>
378
+ if (subcommand === "add") {
379
+ if (!modelArg) {
380
+ return { text: "Usage: /exclude add <model>\nExample: /exclude add nvidia/gpt-oss-120b", isError: true };
381
+ }
382
+ const resolved = addExclusion(modelArg);
383
+ const list = loadExcludeList();
384
+ return {
385
+ text: `Excluded: ${resolved}\n\nActive exclusions (${list.size}):\n${[...list].sort().map((m) => ` • ${m}`).join("\n")}`,
386
+ };
387
+ }
388
+
389
+ // /exclude remove <model>
390
+ if (subcommand === "remove") {
391
+ if (!modelArg) {
392
+ return { text: "Usage: /exclude remove <model>", isError: true };
393
+ }
394
+ const removed = removeExclusion(modelArg);
395
+ if (!removed) {
396
+ return { text: `Model "${modelArg}" was not in the exclude list.` };
397
+ }
398
+ const list = loadExcludeList();
399
+ return {
400
+ text: `Unblocked: ${modelArg}\n\nActive exclusions (${list.size}):\n${list.size > 0 ? [...list].sort().map((m) => ` • ${m}`).join("\n") : " (none)"}`,
401
+ };
402
+ }
403
+
404
+ // /exclude clear
405
+ if (subcommand === "clear") {
406
+ clearExclusions();
407
+ return { text: "All model exclusions cleared." };
408
+ }
409
+
410
+ return {
411
+ text: `Unknown subcommand: ${subcommand}\n\nUsage:\n /exclude — show list\n /exclude add <model>\n /exclude remove <model>\n /exclude clear`,
412
+ isError: true,
413
+ };
414
+ },
415
+ };
416
+ }
417
+ ```
418
+
419
+ **Step 2: Register the command**
420
+
421
+ Add after the `/stats` command registration block (~line 971):
422
+
423
+ ```typescript
424
+ // Register /exclude command for model exclusion management
425
+ createExcludeCommand()
426
+ .then((excludeCommand) => {
427
+ api.registerCommand(excludeCommand);
428
+ })
429
+ .catch((err) => {
430
+ api.logger.warn(
431
+ `Failed to register /exclude command: ${err instanceof Error ? err.message : String(err)}`,
432
+ );
433
+ });
434
+ ```
435
+
436
+ **Step 3: Log active exclusions at startup**
437
+
438
+ In the startup section (after wallet info logging), add:
439
+
440
+ ```typescript
441
+ const startupExclusions = loadExcludeList();
442
+ if (startupExclusions.size > 0) {
443
+ api.logger.info(`Model exclusions active (${startupExclusions.size}): ${[...startupExclusions].join(", ")}`);
444
+ }
445
+ ```
446
+
447
+ **Step 4: Run all tests**
448
+
449
+ Run: `npx vitest run`
450
+ Expected: PASS
451
+
452
+ **Step 5: Commit**
453
+
454
+ ```bash
455
+ git add src/index.ts
456
+ git commit -m "feat: add /exclude command for model exclusion management"
457
+ ```
458
+
459
+ ---
460
+
461
+ ### Task 5: Integration Test
462
+
463
+ **Files:**
464
+ - Create: `src/exclude-models.integration.test.ts`
465
+
466
+ **Step 1: Write integration test**
467
+
468
+ ```typescript
469
+ // src/exclude-models.integration.test.ts
470
+ import { describe, it, expect } from "vitest";
471
+ import { filterByExcludeList } from "./router/selector.js";
472
+ import { DEFAULT_ROUTING_CONFIG } from "./router/config.js";
473
+ import { getFallbackChain } from "./router/selector.js";
474
+
475
+ describe("excludeModels integration", () => {
476
+ it("filters nvidia/gpt-oss-120b from eco SIMPLE chain", () => {
477
+ const chain = getFallbackChain("SIMPLE", DEFAULT_ROUTING_CONFIG.ecoTiers!);
478
+ const excluded = new Set(["nvidia/gpt-oss-120b"]);
479
+ const filtered = filterByExcludeList(chain, excluded);
480
+
481
+ expect(filtered).not.toContain("nvidia/gpt-oss-120b");
482
+ expect(filtered.length).toBeGreaterThan(0); // safety: still has models
483
+ });
484
+
485
+ it("excludes multiple models across eco tiers", () => {
486
+ const exclude = new Set(["nvidia/gpt-oss-120b", "xai/grok-4-0709"]);
487
+
488
+ for (const tier of ["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"] as const) {
489
+ const chain = getFallbackChain(tier, DEFAULT_ROUTING_CONFIG.ecoTiers!);
490
+ const filtered = filterByExcludeList(chain, exclude);
491
+ for (const model of exclude) {
492
+ if (chain.includes(model)) {
493
+ // Only check if the model was in the chain to begin with
494
+ expect(filtered).not.toContain(model);
495
+ }
496
+ }
497
+ expect(filtered.length).toBeGreaterThan(0);
498
+ }
499
+ });
500
+
501
+ it("gracefully handles excluding ALL models in a tier", () => {
502
+ const chain = getFallbackChain("SIMPLE", DEFAULT_ROUTING_CONFIG.ecoTiers!);
503
+ const excludeAll = new Set(chain);
504
+ const filtered = filterByExcludeList(chain, excludeAll);
505
+ // Safety net: returns original chain when all excluded
506
+ expect(filtered).toEqual(chain);
507
+ });
508
+ });
509
+ ```
510
+
511
+ **Step 2: Run integration test**
512
+
513
+ Run: `npx vitest run src/exclude-models.integration.test.ts`
514
+ Expected: PASS
515
+
516
+ **Step 3: Run full test suite**
517
+
518
+ Run: `npx vitest run`
519
+ Expected: ALL PASS
520
+
521
+ **Step 4: Commit**
522
+
523
+ ```bash
524
+ git add src/exclude-models.integration.test.ts
525
+ git commit -m "test: add exclude-models integration tests"
526
+ ```
527
+
528
+ ---
529
+
530
+ ### Summary
531
+
532
+ | Task | What | Files |
533
+ |------|------|-------|
534
+ | 1 | Persistence module (load/add/remove/clear) | `src/exclude-models.ts`, test |
535
+ | 2 | `filterByExcludeList()` in selector | `src/router/selector.ts`, test |
536
+ | 3 | Wire into proxy fallback chain | `src/proxy.ts` |
537
+ | 4 | `/exclude` Telegram command | `src/index.ts` |
538
+ | 5 | Integration tests | `src/exclude-models.integration.test.ts` |
@@ -0,0 +1,81 @@
1
+ # Routing Profiles & Pricing
2
+
3
+ ClawRouter offers four routing profiles to balance cost vs quality. Prices are in **$/M tokens** (input/output).
4
+
5
+ ## ECO (Absolute Cheapest)
6
+
7
+ Use `blockrun/eco` for maximum cost savings.
8
+
9
+ | Tier | Primary Model | Input | Output |
10
+ | --------- | ---------------------------- | ----- | ------ |
11
+ | SIMPLE | nvidia/gpt-oss-120b | $0.00 | $0.00 |
12
+ | MEDIUM | google/gemini-2.5-flash-lite | $0.10 | $0.40 |
13
+ | COMPLEX | google/gemini-2.5-flash-lite | $0.10 | $0.40 |
14
+ | REASONING | xai/grok-4-1-fast-reasoning | $0.20 | $0.50 |
15
+
16
+ ---
17
+
18
+ ## AUTO (Balanced - Default)
19
+
20
+ Use `blockrun/auto` for the best quality/price balance.
21
+
22
+ | Tier | Primary Model | Input | Output |
23
+ | --------- | ----------------------------- | ----- | ------ |
24
+ | SIMPLE | moonshot/kimi-k2.5 | $0.60 | $3.00 |
25
+ | MEDIUM | xai/grok-code-fast-1 | $0.20 | $1.50 |
26
+ | COMPLEX | google/gemini-3.1-pro | $2.00 | $12.00 |
27
+ | REASONING | xai/grok-4-1-fast-reasoning | $0.20 | $0.50 |
28
+
29
+ ---
30
+
31
+ ## PREMIUM (Best Quality)
32
+
33
+ Use `blockrun/premium` for maximum quality.
34
+
35
+ | Tier | Primary Model | Input | Output |
36
+ | --------- | -------------------- | ----- | ------ |
37
+ | SIMPLE | moonshot/kimi-k2.5 | $0.60 | $3.00 |
38
+ | MEDIUM | openai/gpt-5.2-codex | $1.75 | $14.00 |
39
+ | COMPLEX | claude-opus-4.6 | $5.00 | $25.00 |
40
+ | REASONING | claude-sonnet-4.6 | $3.00 | $15.00 |
41
+
42
+ ---
43
+
44
+ ## AGENTIC (Multi-Step Tasks)
45
+
46
+ Use `blockrun/agentic` for autonomous multi-step tasks, or let ClawRouter auto-detect agentic patterns.
47
+
48
+ | Tier | Primary Model | Input | Output |
49
+ | --------- | -------------------- | ----- | ------ |
50
+ | SIMPLE | moonshot/kimi-k2.5 | $0.60 | $3.00 |
51
+ | MEDIUM | xai/grok-code-fast-1 | $0.20 | $1.50 |
52
+ | COMPLEX | claude-sonnet-4.6 | $3.00 | $15.00 |
53
+ | REASONING | claude-sonnet-4.6 | $3.00 | $15.00 |
54
+
55
+ ---
56
+
57
+ ## ECO vs AUTO Savings
58
+
59
+ | Tier | ECO | AUTO | Savings |
60
+ | --------- | ----- | ------ | -------- |
61
+ | SIMPLE | FREE | $3.60 | **100%** |
62
+ | MEDIUM | $0.50 | $1.70 | **71%** |
63
+ | COMPLEX | $0.50 | $14.00 | **96%** |
64
+ | REASONING | $0.70 | $0.70 | 0% |
65
+
66
+ ---
67
+
68
+ ## How Tiers Work
69
+
70
+ ClawRouter automatically classifies your query into one of four tiers:
71
+
72
+ - **SIMPLE**: Basic questions, short responses, simple lookups
73
+ - **MEDIUM**: Code generation, moderate complexity tasks
74
+ - **COMPLEX**: Large context, multi-step reasoning, complex code
75
+ - **REASONING**: Logic puzzles, math, chain-of-thought tasks
76
+
77
+ The router picks the cheapest model capable of handling your query's tier.
78
+
79
+ ---
80
+
81
+ _Last updated: v0.12.24_