Free-first LLM routing for Python: local Ollama -> OpenRouter free models -> paid Anthropic API, with automatic fallback and a savings ledger.
Project description
llm-cascade
Free-first LLM routing for Python: local Ollama ($0) -> OpenRouter free models ($0) -> paid Anthropic API, with automatic fallback and a savings ledger. Zero required dependencies.
Why
Most teams route every prompt to the same frontier model regardless of how hard the task actually is, and the API bill grows with usage instead of with value delivered. llm-cascade flips that: it tries the cheapest tier that can plausibly do the job first, and only escalates when that tier is unreachable or genuinely too weak for the task. In production, that means classification, extraction, and summarization run for $0 on a local model or a free cloud model, while code review, planning, and long-form writing still get a frontier model -- and if the paid API is ever down or out of credits, your scheduled jobs degrade to a free tier instead of crashing.
Install
pip install llm-cascade
The base package has zero required third-party dependencies. To use the paid Anthropic tier, install the extra:
pip install "llm-cascade[anthropic]"
Without it, the local (Ollama) and free-cloud (OpenRouter) tiers still work end to end -- you'll only see an error if a task actually routes to the paid tier and the SDK isn't installed.
Quickstart
from llm_cascade import route
result = route("summarize", "Paste a long document here...")
print(result.text) # the completion
print(result.tier) # which tier served it: trivial/local/cheap/light/heavy
print(result.model) # e.g. "local:ollama", "openrouter:qwen/...", "claude-sonnet-5"
print(result.cost_usd) # 0.0 for local/free-cloud tiers
Or from the command line:
llm-cascade "Summarize this text: ..." --task summarize
llm-cascade --routes # show the tier ladder and task -> tier table
llm-cascade --stats # show cumulative cost and free-vs-paid call counts
How routing works
Each call resolves a task name (or a tier name directly) to a tier, then runs that tier's provider chain in order until one succeeds:
| Tier | Chain | Use for |
|---|---|---|
trivial |
local Ollama only ($0) | high-volume micro-tasks, never worth the network |
local |
local Ollama ($0) -> OpenRouter free ($0) -> paid | mechanical NLP: classify, extract, summarize, translate |
cheap |
OpenRouter free ($0) -> paid | manual opt-in tier, invoke directly with task="cheap" |
light |
paid (Sonnet); free-cloud only if LLM_CASCADE_LIGHT_FREE=1 |
customer-facing copy that needs reliable polish |
heavy |
paid (Opus), adaptive thinking, high effort | real reasoning: code, review, planning, long-form |
If the paid API call fails for any reason -- bad or revoked key, no credits, a provider outage, a network blip -- llm-cascade degrades to the free tiers instead of raising, so scheduled or automated callers don't break outright. It only raises once every tier has been tried and failed.
The default task -> tier table:
classify, extract, summarize, translate, chat -> local
rewrite -> light
code, review, plan, creative -> heavy
Anything not in that table falls back to default_task_tier (light by
default). See config/routes.example.toml for the full, commented schema.
Configuration
Configuration is resolved with this precedence (lowest to highest):
- Built-in defaults.
- An optional TOML file:
~/.llm_cascade/routes.toml, or the path given inLLM_CASCADE_CONFIG. - Environment variables, for the handful of settings that make sense as env vars.
Copy config/routes.example.toml to
~/.llm_cascade/routes.toml to add your own task names, retune which tier
handles a task, or override model ids and pricing.
Environment variables:
| Variable | Purpose |
|---|---|
ANTHROPIC_API_KEY |
Enables the paid tier (light/heavy, and the final safety net). |
OPENROUTER_API_KEY |
Enables the free-cloud tier (OpenRouter :free models). |
LLM_CASCADE_CONFIG |
Path to a TOML config file, instead of ~/.llm_cascade/routes.toml. |
LLM_CASCADE_LOG_PATH |
Path to the savings ledger, instead of ~/.llm_cascade/log.jsonl. |
LLM_CASCADE_LIGHT_FREE |
Set to 1 to try free-cloud first on the light tier too. |
LLM_CASCADE_NO_FREE_CLOUD |
Set to any value to disable the free-cloud tier globally. |
LLM_CASCADE_HTTP_REFERER / LLM_CASCADE_APP_TITLE |
Sent as HTTP-Referer / X-Title on OpenRouter requests (optional; omitted if unset). |
LLM_CASCADE_OPENROUTER_MODEL / LLM_CASCADE_OPENROUTER_MODELS |
Override the free model (singular) or ordered list (comma-separated) tried on OpenRouter. |
LLM_CASCADE_OLLAMA_BASE_URL |
Ollama server URL (default http://localhost:11434). |
See .env.example for a copy-pasteable starting point.
Savings ledger
Every call -- free or paid -- appends one line to a JSONL ledger
(~/.llm_cascade/log.jsonl by default). stats() / llm-cascade --stats
summarize it:
$ llm-cascade --stats
{
"paid_calls": 3,
"free_calls": 41,
"total_cost_usd": 0.0842,
"by_model": {
"local:ollama": 30,
"openrouter:qwen/qwen3-next-80b-a3b-instruct:free": 11,
"claude-sonnet-5": 3
}
}
Comparison
Unlike heavier multi-provider routers, llm-cascade ships with zero required dependencies and a single opinionated cascade -- it's a routing policy you can read in one file, not a platform.
Contributing
Issues and pull requests are welcome at github.com/luandv92/llm-cascade. Before opening a PR:
pip install -e ".[dev,anthropic]"
ruff check .
pytest tests/ -v
Tests never touch real networks -- Ollama, OpenRouter, and the Anthropic SDK
are all mocked (see tests/conftest.py).
License
MIT -- see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llm_cascade-0.1.0.tar.gz.
File metadata
- Download URL: llm_cascade-0.1.0.tar.gz
- Upload date:
- Size: 23.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
12cefcbb259fa1eac006c066b4782df4b3da94e906b0b62d28d47a91f8d4569f
|
|
| MD5 |
7f9ce669061257c0d91a26eea6dea46a
|
|
| BLAKE2b-256 |
b9558008886b8ce0457dd2fa93a9a910fbc29aad178eecacddbd6865474e9dc4
|
File details
Details for the file llm_cascade-0.1.0-py3-none-any.whl.
File metadata
- Download URL: llm_cascade-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fad0346dde1842ecbf0cbcb22606a4667535cd2fed4d89cf691cea12af50190
|
|
| MD5 |
1051f38ed198607607c01f34eb3b983a
|
|
| BLAKE2b-256 |
b12fc7bb463b82dd2b535c3ff2cf2de53f6e7cf9b867a932b3b7008c4655b53d
|