npm package: @shawnmandel/openclaw-tier-router
Description: Rules-based complexity routing across model tiers for OpenClaw
Author: Shawn Mandel
Latest version: 2.2.0

# Tier Router — OpenClaw Plugin

Rules-based complexity routing across model tiers. Routes simple tasks to cheap/fast local models and complex tasks to capable cloud models, with deterministic classification — no LLM needed for routing decisions.

## Why

OpenClaw's native fallback system is failure-recovery only — it tries the next model when the current one fails. This plugin adds **pre-routing**: every inbound message is classified by complexity and routed to the appropriate model tier *before* inference begins.

## How It Works

The plugin hooks into `before_model_resolve` — the first hook in OpenClaw's pipeline. It extracts the user's message text, classifies it using deterministic regex patterns, and returns `modelOverride` + `providerOverride` to route to the correct tier.

No tokens are consumed for routing. Classification is instant (~1ms).

## Routing Rules

Priority order (first match wins):

| Rule | Routes To | Examples |
|---|---|---|
| Manual override keywords | Specified tier | "use opus", "use groq", "use local" |
| File paths detected | Tier 3 | `/home/user/config.json`, `~/project/` |
| Skill patterns | Tier 3 | "generate image", "run command", "web search" |
| Complex patterns | Tier 2 (short) or Tier 3 (50+ words) | "debug", "architect", "plan", "reason" |
| Debug patterns | Tier 2 | "fix this broken", "not working", "crashed" |
| Moderate patterns | Tier 1 | "code", "script", "install", "git" |
| Long unmatched (80+ words) | Tier 2 | Any long message without keyword matches |
| System messages | Tier 1 | Session startup, heartbeats |
| Everything else | Tier 1 | Default — cheapest model |

## Manual Overrides

Include these phrases anywhere in your message to force a specific tier:

- **Tier 1:** "use local", "use ollama", "use qwen", "use tier1"
- **Tier 2:** "use groq", "use tier2"
- **Tier 3:** "use opus", "use claude", "use anthropic", "use tier3"

## Installation

Copy the `tier-router/` directory into your OpenClaw extensions folder:
```
~/.openclaw/extensions/tier-router/
├── openclaw.plugin.json
├── index.ts
└── README.md
```

## Configuration

Add to your `openclaw.json`:
```json
{
  "plugins": {
    "allow": ["tier-router"],
    "entries": {
      "tier-router": {
        "enabled": true,
        "config": {
          "tier1": { "model": "your-local-model", "provider": "ollama" },
          "tier2": { "model": "your-mid-model", "provider": "groq" },
          "tier3": { "model": "your-best-model", "provider": "anthropic" },
          "complexWordThreshold": 50,
          "longPromptThreshold": 80
        }
      }
    }
  }
}
```

### Config Fields

| Field | Type | Default | Description |
|---|---|---|---|
| `tier1.model` | string | *required* | Model ID for simple/moderate tasks |
| `tier1.provider` | string | *required* | Provider name for Tier 1 |
| `tier2.model` | string | *required* | Model ID for complex tasks |
| `tier2.provider` | string | *required* | Provider name for Tier 2 |
| `tier3.model` | string | *required* | Model ID for skills/tool-calling/deep reasoning |
| `tier3.provider` | string | *required* | Provider name for Tier 3 |
| `complexWordThreshold` | number | 50 | Word count above which complex matches escalate to Tier 3 |
| `longPromptThreshold` | number | 80 | Word count above which unmatched messages route to Tier 2 |

If no config is provided, the plugin falls back to built-in defaults. For production use, always provide explicit config.

## Logs

The plugin logs every routing decision to stdout with the `[TIER-ROUTER]` prefix:
```
[TIER-ROUTER] === ROUTING DECISION ===
[TIER-ROUTER] user text: "Debug this config issue"
[TIER-ROUTER] classification: TIER2-COMPLEX
[TIER-ROUTER] model: groq/llama-3.3-70b-versatile
[TIER-ROUTER] reason: complex keyword: "Debug"
```

Filter logs: `docker logs <container> --since 5m 2>&1 | grep "TIER-ROUTER"`

## Known Limitations

- **Groq/Llama models cannot do tool-calling in OpenClaw** (bug #17598). Skill patterns route to Tier 3 for this reason.
- **extractUserText** relies on OpenClaw's prompt envelope format. If the format changes, classification may degrade.
- **File path detection** may false-positive on casual path mentions. URLs are excluded.
- **`/new` session label** shows the config default model, not the router override. This is cosmetic — actual inference uses the routed tier.

## Requirements

- OpenClaw v2026.3.x or later
- `configSchema` support in plugin manifests

## License

MIT