ai-tierforge
Route LLM calls to the right model. Track what each task actually costs. Catch when your "cheap" tier isn't saving you money.
Why this exists
You set up a cheap model for simple tasks and an expensive one for hard tasks. Sounds smart. But nobody tracks what happens when the cheap model fails 3 times and escalates. That "cheap" task just cost you 5 retries plus the expensive call.
Most tools track per-call cost. ai-tierforge tracks per-task cost — the retries, the escalations, the failed attempts — so you can see what your tier routing actually costs.
| What happens | Per-call cost | Real task cost |
|---|---|---|
| Cheap model solves in 1 try | $0.001 | $0.001 |
| Cheap model retries 3x, then solves | $0.001 | $0.003 |
| Cheap model fails 3x, escalates to expensive | $0.001 + $0.05 | $0.053 |
Install
pip install ai-tierforge
Requires Python 3.11+. Only two runtime deps: pyyaml and requests.
Quick start
Write a config:
# tiers.yaml
tiers:
architect:
model: gpt-5-nano
max_tokens: 8000
use_for: [spec, review]
provider: openai-compatible
endpoint: https://opencode.ai/zen/v1
api_key_env: OPENCODE_API_KEY
workhorse:
model: deepseek-v4-flash
max_tokens: 4000
use_for: [code, chat]
provider: openai-compatible
endpoint: https://opencode.ai/zen/v1
api_key_env: OPENCODE_API_KEY
utility:
model: deepseek-v4-flash
max_tokens: 2000
use_for: [tickets, summaries]
provider: openai-compatible
endpoint: https://opencode.ai/zen/v1
api_key_env: OPENCODE_API_KEY
pricing:
deepseek-v4-flash:
input: 0.00000014
output: 0.00000028
gpt-5-nano:
input: 0.00000005
output: 0.0000004
budgets:
per_task:
limit: 0.10
on_exceed: downgrade
Use it:
from ai_tierforge import TierRouter
router = TierRouter.from_yaml("tiers.yaml")
result = router.route(task_type="code", prompt="Write a unit test for auth")
print(result.tier) # workhorse
print(result.model) # deepseek-v4-flash
print(result.response) # the model's output
# What did this task actually cost? (including retries + escalations)
report = router.cost_report()
print(report.cost_per_task("code"))
Or from the command line:
ai-tierforge validate tiers.yaml
ai-tierforge --config tiers.yaml route code "Write a unit test for auth"
ai-tierforge --config tiers.yaml report --type code
ai-tierforge --config tiers.yaml budget check --scope team:payments
What it does
-
Routes by task type —
codegoes to workhorse,specgoes to architect,ticketsgoes to utility. You define the mapping in YAML. -
Tracks real cost per task — not just the successful call, but every retry, every escalation, every failed attempt. Uses
Decimalso you don't get float rounding errors on micropayments. -
Escalates on failure — if workhorse times out or hits a rate limit, the router tries architect automatically. The escalation trace is logged so you can see exactly what happened.
-
Enforces budgets — set a per-task, per-day, or per-project limit. When exceeded: warn, downgrade to a cheaper tier, or hard stop. You pick the action per scope.
-
Separates routing from failover in logs — "routed to DeepSeek because it's cheap" is a different event than "fell back to GPT because DeepSeek was down". You can grep for one without the other.
-
Works with any OpenAI-compatible endpoint — OpenCode Zen, OpenAI, DeepSeek, vLLM, LiteLLM, Portkey.
The metric that matters: escalation rate
If 80% of your tasks escalate from the cheap tier to the expensive tier, you're paying for both on every task. That's worse than just using the expensive model from the start.
ai-tierforge tracks escalation rate per task type and per tier, and alerts when it crosses your threshold:
escalation:
default_threshold: 0.30
per_tier:
workhorse: 0.25
utility: 0.40
max_retries: 3
Field tested with real data
Routed real GitHub issues, PRs, and code from FastAPI, Pydantic, httpx, and Rich through the tier system. All 8 test scenarios passed.
| Metric | Value |
|---|---|
| API calls | 33 |
| Total cost | $0.01 |
| Scenarios passed | 8/8 |
| Cost savings | 44.8% vs single-model baseline |
What happened:
codetasks routed to DeepSeek (workhorse) at ~$0.0007/callspectasks routed to gpt-5-nano (architect) at ~$0.001/call — it found a real version mismatch in fastapi PR #16018ticketsandsummariesrouted to DeepSeek (utility) at ~$0.0001/call- Workhorse timeout triggered automatic escalation to architect — call succeeded
- Budget exceeded → HARD_STOP blocked the next call
- Budget exceeded → DOWNGRADE switched to workhorse, saving 60% on that call
- CLI round-trip worked via YAML config with custom endpoint and pricing
Full reports:
docs/pass3-real-data.md— final run, 8/8 pass, real FastAPI datadocs/pass2-synthetic-data.md— synthetic post-fix, 6/8 pass, 47.6% savingsdocs/1st-test-pass-synthetic-data.md— first run, 5 bugs found
How it works
agent calls router.route("code", prompt)
→ matches "code" to workhorse tier
→ checks budget (allowed? blocked? downgrade?)
→ calls the provider adapter
→ records cost in the ledger
→ success? return the result
→ failure? retry or escalate to architect
→ all retries exhausted? raise RouterExhaustedError
Components
| What | File | Does what |
|---|---|---|
| TierRouter | router.py |
Matches task type to tier, handles retries + escalation |
| CostLedger | cost.py |
Records every call per task, computes per-task totals |
| BudgetEnforcer | budget.py |
Checks spend against limits, triggers warn/downgrade/stop |
| EscalationTracker | slo.py |
Tracks escalation rate, alerts on threshold breach |
| RoutingLogger | slo.py |
Writes JSONL logs, separates route vs failover events |
| OpenAICompatAdapter | adapters/openai_compat.py |
Talks to any OpenAI-compatible API |
| OMLXAdapter | omlx.py |
Talks to local OMLX models (code-complete, unit-tested) |
| Config loader | config.py |
Parses YAML, validates, returns typed config |
| CLI | cli.py |
route, report, validate, budget subcommands |
Configuration reference
Tiers
tiers:
architect:
model: gpt-5-nano
max_tokens: 8000
use_for: [spec, review]
provider: openai-compatible
endpoint: https://api.example.com/v1 # optional
api_key_env: MY_API_KEY # optional
First tier in the YAML is highest priority (architect). Last is lowest (utility). This order determines escalation direction.
Pricing
Per-token costs. If you skip this, the adapter falls back to built-in pricing for common models:
pricing:
deepseek-v4-flash:
input: 0.00000014
output: 0.00000028
Budgets
Three scopes, each independent:
budgets:
per_task:
limit: 0.10
on_exceed: downgrade # warn | downgrade | hard_stop
per_day:
limit: 5.00
on_exceed: hard_stop
per_project:
limit: 50.00
on_exceed: warn
Logging
logging:
routing: true # log routing decisions
failover: true # log failover events
level: debug # info or debug (debug includes prompt/response)
output: stdout # stdout or a file path
Install from source
git clone https://github.com/deghosal-2026/ai-tierforge.git
cd ai-tierforge
pip install -e ".[dev]"
Development
pytest # 133 tests
ruff check src/ tests/ # lint
mypy src/ # type check
# Field tests with real data
OPENCODE_API_KEY=oc_zen_... python tests/field/run_field_test.py \
--data-dir tests/field/realdata --count 2 --fresh
Project layout
src/ai_tierforge/
types.py # dataclasses + enums
exceptions.py # 7 custom exceptions
config.py # YAML loader + validator
router.py # routing, retries, escalation
cost.py # per-task cost ledger
slo.py # escalation tracker + routing logger
budget.py # budget enforcer
omlx.py # local model adapter
cli.py # CLI
adapters/
base.py # ProviderAdapter protocol
openai_compat.py # OpenAI-compatible adapter
tests/
test_*.py # unit tests
field/ # field test runner + real data
How it compares
| Tool | Tier routing | Cost per task | Escalation rate | Local models |
|---|---|---|---|---|
| LiteLLM | no | no | no | no |
| Portkey | no | no | no | no |
| agent-budget-controller | no | no | no | no |
| ai-tierforge | yes | yes | yes | yes |
ai-tierforge sits above your existing gateway. It doesn't replace LiteLLM or Portkey — it adds tier routing and task-level cost tracking on top.
Related
- loopguard — detects stuck agent loops. loopguard decides when to escalate, ai-tierforge decides where and tracks the cost.
- Agent Spend Protocol (ASP) — pre-call budget enforcement. ASP asks "can this tenant afford this call?" ai-tierforge asks "was this task worth what it cost?"
License
MIT
Metadata
Release files for ai-tierforge 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ai_tierforge-0.1.0.tar.gz | 52.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ai_tierforge-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 96.9 kB
Release files / ai_tierforge-0.1.0.tar.gz
| Download URL | ai_tierforge-0.1.0.tar.gz |
|---|---|
| Size | 52.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
279cab8a17476af06ad11b9598af9e7473fd06aa62fa0a9e55c26a2cb739e4a6
|
|
BLAKE2b-256 checksum How to use checksums |
a7277bff401558c3c1e3f1d50d41f23a2b4ca6479e1a52585537fd3982e99a8b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.5
|
Release files / ai_tierforge-0.1.0-py3-none-any.whl
| Download URL | ai_tierforge-0.1.0-py3-none-any.whl |
|---|---|
| Size | 44.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e84bcd142f0bf9e7981e15e39081f3b70cf791f6f9ce56423ac553ede358e54c
|
|
BLAKE2b-256 checksum How to use checksums |
6a4bf21b92ee7bc3158563f63c55aedaee15d8a6af45612d7d0302daa476ea2c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.5
|
Security for ai-tierforge 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.