Gateways you already run
Import your LiteLLM config
Read what does not carry across.
One command reads your config.yaml and writes a TokenTriage config. The risk report names what will not survive. Nothing is applied.
# 1 · Import. Config on stdout, risk report beside it.
tokentriage migrate --from litellm config.yaml \
--report-md risk-report.md > tokentriage.yaml
# 2 · Boot it.
tokentriage run -c tokentriage.yaml
# 3 · Point your clients at :8787 instead of LiteLLM's :4000.
# openai.OpenAI(api_key="…", base_url="http://127.0.0.1:8787/v1")- The setting
tokentriage migrate --from litellm config.yaml- The base URL
http://127.0.0.1:8787/v1 — LiteLLM’s own quick-start uses a bare root
How to tell it worked
Send one request through the alias your clients already use. Then read the ledger by model. A row under that alias means it arrived; a dash in COST is the alias caveat below.
tokentriage top --by modelWhat you should see
prod-fast 2 2048 1024 … - · COST excludes 2 unpriced rows of 7
How sure we are
Verified
A named test in the product repo exercises this integration and asserts the result. What it drives is our side of it: no test here installs the third-party client.
Verified against LiteLLM v1.98.0 on
The gotcha
With --keys-export, the config validates and then refuses to boot
tokentriage validate accepts the file. tokentriage run stops on pinned node "org:imported/team:payments" not in tenancy tree. Each imported key attaches its budget to a team node the emitted tenancy tree never declares. Declare them, or import without the flag.
# Declare the nodes the mint plan attached your budgets to.
governance:
tenants:
- org: imported
teams: [payments, default]
users:
default: [ada_example_com]“Need a postgres database (e.g. Supabase, Neon, etc)”
docs.litellm.ai/docs/proxy/virtual_keys · read 2026-08-25
What you get now
What lands in the ledger once traffic flows.
Three things the import switches on the first time a request arrives. Each one links to the page that evidences it. Read those before you trust this one.
A priced row for every request
TokenTriage writes one row per call, with the model, the token counts and a price from a dated table. Unpriced rows are counted, never folded in at zero. The snapshot id is on the row.
What the ledger recordsBudgets and keys in the same binary
TokenTriage attaches the imported budgets and rate limits to tenancy nodes. Keys are minted here and stored as hashes. The store is an embedded SQLite file.
How governance worksLedger first, and nothing routes yet
TokenTriage starts the emitted config in mode: ledger, so every response stays the upstream response. You promote to shadow and then to route when the ledger has told you something. Nothing moves on day one.
Ledger, shadow, route
- 5 buckets
- imported, approximated, not expressible, not in source, unknown
- 0 coverage percentages
- a percentage answers a question nobody has
- 18 tests
- twelve on the importer, six on the command, none skipped
- 1 base URL
- the only client-side change, from :4000 to :8787/v1
What does not carry across
Six things the import will not do for you.
The risk report names most of these, with the construct that produced each one. Read it before you cut over. The last one is about our evidence.
Your model_name lands as a tier name, not a rule
The importer emits no rule matching the alias, and TokenTriage never rewrites a model in ledger mode. The call succeeds. COST shows a dash, not a zero. One routing.rules[] entry with match: { model: prod-fast } fixes it.
Routing on the model your clients sendA base URL without /v1 is a different request
LiteLLM’s own quick-start points clients at http://0.0.0.0:4000, and LiteLLM serves both paths. Here the match is exact. A bare-root call is relayed, unparsed and unpriced, to whichever upstream the runtime hands back. It carries that upstream’s credential.
Base URL and path, per clientNo retry chain, and no fallback chain
router_settings.fallbacks, context_window_fallbacks, num_retries and allowed_fails all come back as blockers. TokenTriage has no retry chain to import them into. A failed upstream returns its failure to your client. Cooldown is a different mechanism.
The fallbacks and cooldown rowsVirtual keys, budgets and rate limits are not in the file
LiteLLM keeps them in Postgres, so config.yaml carries none of them. --keys-export reads your dump of them and writes a mint plan, never a secret. A migration is a key rotation.
Where keys and budgets landA load-balanced group imports its first entry only
Several model_list entries sharing one model_name is LiteLLM’s deployment group. The first entry becomes the tier. The rest are reported as blockers, and a provider outside the four-entry host table is skipped unless it carries api_base.
The chip covers the importer, not the swap
Eighteen tests and one end-to-end boot stand behind the import. The swap is a different claim. Nothing here drives an OpenAI client against an imported LiteLLM config, and the chip above covers the importer only. The swap was run by hand on 2026-08-25.
Import it, then read the report.
The import is one command and it changes nothing you did not redirect. The reading is the work. Move one base URL when the report has stopped surprising you.
Names and trademarks
These are independent integrations. No product named here has endorsed, reviewed or sponsored TokenTriage; every name is its owner's trademark, used only to say what works with what.