Skip to content

Repository files navigation

FreeRouter — self-hosted AI model router

FreeRouter Logo

Route requests to configured AI providers using your own API keys. FreeRouter keeps provider selection and credentials on your machine.

OpenClawLicense: MITTests


Why FreeRouter?

FreeRouter is intended for people who already have provider accounts and want routing decisions to stay in their own deployment.

PainHow FreeRouter Fixes It
💸 Middleman markup — OpenRouter and similar services charge on top of provider pricesZero markup. Self-hosted, runs locally. You pay providers directly.
🔥 Every message hits your expensive model — Opus at $75/M output tokens for "hello"?14-dimension classifier routes simple messages to cheap models automatically. Save 60-80%.
🎰 No control over routing — auto-classifiers get it wrong sometimesMode overrides. Prefix /max or [simple] to force a tier when you know better.
Proxies that hang — upstream is slow, your app freezesRequest timeouts + auto-fallback. Times out → retries with fallback model.
🔧 Hardcoded configs — want to change a model? Edit source code, recompile, restartExternal config file. Edit JSON, hit /reload-config. No restart needed.

Features

  • Smart routing — 14-dimension weighted classifier scores every request and picks the best model
  • Mode overrides(new in v1.3.0) — force a tier with /max, /simple, [complex], deep mode: etc.
  • Zero cost — no subscription, no per-token fees, no payment layer
  • External configfreerouter.config.json for providers, tiers, boundaries, auth
  • Request timeouts — per-tier timeouts with automatic fallback to secondary model
  • Tool call translation — bidirectional Anthropic ↔ OpenAI format translation
  • OpenAI-compatible API — drop-in replacement; works with any client that speaks /v1/chat/completions
  • 75/75 test suite — core routing, streaming, tool calls, unicode, concurrency, mode overrides

How It Works

Your App → FreeRouter (:18800) → Classifier → Best Model
├── SIMPLE → Kimi K2.5 (near-zero cost)
├── MEDIUM → Sonnet 4.5 (balanced)
├── COMPLEX → Opus 4.6 (powerful)
└── REASONING → Opus 4.6 (max thinking)

The classifier scores each message on 14 dimensions (vocabulary complexity, reasoning depth, code complexity, domain specificity, etc.) and routes to the cheapest model that can handle it. Context-aware — includes last 3 messages in scoring.

Mode Overrides (v1.3.0)

Sometimes you know better than the classifier. Prefix your prompt to force a tier:

Slash Prefix

/simple What's 2+2?
/max Analyze this distributed system architecture for race conditions
/reasoning Prove that P(A|B) = P(B|A)P(A)/P(B)

Bracket Prefix

[complex] Refactor this module to use dependency injection
[simple] Translate "hello" to French

Word Prefix

deep mode: Why does this recursive CTE produce duplicates?
basic mode, What time is it in Tokyo?

Alias Table

AliasesRoutes to
simple, basic, cheapSIMPLE — cheapest model
medium, balancedMEDIUM — general purpose
complex, advancedCOMPLEX — powerful model
max, reasoning, think, deepREASONING — maximum thinking

The prefix is stripped before forwarding — the LLM never sees it. When no prefix is detected, normal classification runs.

Quick Start

1. Clone & Build

git clone https://github.com/openfreerouter/freerouter.git
cd freerouter
npm install
npx tsc

2. Configure

Copy and edit the config file:

cp freerouter.config.json ~/.config/freerouter/config.json
# Edit providers, API keys, tier mappings

Or set API keys via environment variables. See Configuration below.

3. Run

node dist/src/server.js
# Listening on http://localhost:18800

4. Use

Point any OpenAI-compatible client at http://localhost:18800/v1/chat/completions.

# Health check
curl http://localhost:18800/health
# Chat
curl http://localhost:18800/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"auto","messages":[{"role":"user","content":"Hello!"}]}'

Configuration

FreeRouter looks for config in this order:

  1. FREEROUTER_CONFIG environment variable
  2. ./freerouter.config.json (working directory)
  3. ~/.config/freerouter/config.json

If no config file exists, built-in defaults apply.

Config File Structure

{
"providers": {
"anthropic": { "baseUrl": "https://api.anthropic.com", "api": "anthropic" },
"kimi": { "baseUrl": "https://api.moonshot.cn", "api": "openai" }
},
"tiers": {
"SIMPLE": { "model": "kimi-for-coding", "provider": "kimi", "fallback": "claude-haiku-4-5-20250315" },
"MEDIUM": { "model": "claude-sonnet-4-5-20250514", "provider": "anthropic" },
"COMPLEX": { "model": "claude-opus-4-0-20250115", "provider": "anthropic" },
"REASONING": { "model": "claude-opus-4-0-20250115", "provider": "anthropic" }
}
}

Reload without restart: curl http://localhost:18800/reload-config

OpenClaw Integration

Add to your openclaw.json:

{
"providers": {
"freerouter": {
"baseUrl": "http://localhost:18800",
"api": "openai-completions",
"models": [{ "id": "auto" }]
}
},
"agents": {
"defaults": { "model": "freerouter/auto" }
}
}

Endpoints

EndpointDescription
POST /v1/chat/completionsMain chat endpoint (OpenAI-compatible)
GET /healthHealth check with uptime and timeout count
GET /statsRequest statistics by tier
GET /v1/modelsList available models
GET /configView current config (secrets redacted)
POST /reloadReload auth keys
POST /reload-configReload config file

The 14-Dimension Classifier

Each message is scored across 14 dimensions:

DimensionWhat It Measures
Token countMessage length
Vocabulary complexityRare/technical words
Syntax complexityNested clauses, conditionals
Domain specificitySpecialized knowledge needed
AmbiguityHow open-ended the request is
Context dependencyNeeds prior conversation
Reasoning depthLogical steps required
Creativity levelOriginal generation needed
Emotional complexityNuance in tone/sentiment
MultimodalityReferences to images/files
Instruction complexityMulti-step instructions
Knowledge recencyNeeds current information
Code complexityProgramming difficulty
Mathematical complexityFormal math/proofs

Scores are weighted and combined. Tier boundaries are configurable.

Cost Impact

ScenarioEstimated Daily Cost
All Opus (no routing)~$50/day
With FreeRouter~$10-15/day
Savings60-80%

Most messages are simple. Those go to Kimi at near-zero cost. Only complex work hits Opus.

Project Structure

freerouter/
├── src/
│ ├── server.ts # HTTP server + mode override detection
│ ├── provider.ts # Multi-provider forwarding + SSE translation
│ ├── auth.ts # API key management
│ ├── config.ts # External config loader
│ ├── logger.ts # Request logging
│ └── router/
│ ├── index.ts # 14-dimension classifier
│ ├── config.ts # Tier mappings + scoring weights
│ └── rules.ts # Keyword-based overrides
├── tests/
│ ├── test-proxy.sh # Core tests (33 + 5 mode override tests)
│ └── test-proxy-extended.sh # Extended tests (37)
├── freerouter.config.json # Example config
├── tsconfig.json
└── package.json

Credits

Forked from BlockRunAI/ClawRouter (MIT License). Routing engine preserved; x402 payment protocol removed entirely. Credit to BlockRunAI for the original classifier design.

License

MIT

About

Self-hosted AI model router that selects providers using your own API keys.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages