litellm/litellm-rust
Nate Armstrong 8fb0643e35
perf(gateway): admit rate limits in one redis round trip and send scripts by hash (#47197)
* feat(gateway): hooks and observers around inference requests, with usage and cost

Adds the litellm-gateway-hooks crate: request facts built only when something
reads them, on-path hooks that declare what they read and what they may
change and run in config order, off-path observers on bounded queues, a
usage tap for SSE and JSON responses, and per-deployment prices from the
model cost map overlaid by config prices. The messages and chat handlers run
through it when anything is installed; with nothing installed requests take
the old path.

The key store now also returns the key alias, user, team alias and key and
team metadata, and router attempts carry the deployment id.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138knkqKpkoSr6PApenZan5

* fix(model-catalog): read token counts the cost map writes as whole floats

The published cost map now has prompt_cache_min_tokens: 4096.0, which the
typed u64 fields rejected, so the gateway dropped the whole map at startup
and priced every request at zero. Counts accept any whole non-negative
number, as Python reads any JSON number, and a test keeps the checked-in
map parsing.

* feat(gateway): write spend to the proxy's database from a native observer

Adds a spend writer observer that records every gateway request in the
proxy's own Postgres tables with the same statements the Python proxy
uses: spend logs, key/user/team/team member/end user spend, and the
daily user, team and end-user activity tables. Writes are batched on the
proxy's timers and are additive, so replicas and Python proxies can
write side by side. The database pool is now shared between virtual
keys and the writer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138knkqKpkoSr6PApenZan5

* feat(gateway): enforce key, team, user, member and end-user budgets on shared spend counters

Adds a built-in budgets hook that runs the proxy's budget checks, in its
order and with its messages, on the proxy's Redis spend:* counters. Each
request reads and seeds the counters, checks them and reserves its
maximum cost in one Redis script, then settles the reservation to its
real cost off the request path, so replicas and Python proxies sharing
the Redis enforce one budget. Without Redis the counters are kept per
replica. The key store now reads user, team member and end-user budgets,
and auth no longer checks budgets against stored spend.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138knkqKpkoSr6PApenZan5

* feat(gateway): enforce key, user, team, member and end-user rate limits on shared counters

Adds a built-in rate limits hook with the proxy's v3 limiter semantics:
requests per window, tokens per window and in-flight requests per key,
on the proxy's own {scope:id}:window|requests|tokens|max_parallel_requests
Redis keys and with its scripts, so replicas and Python proxies sharing
the Redis enforce one limit. Tokens are reserved from the proxy's
estimate and settled to usage when the response ends. Refusals carry the
proxy's message, body and headers on each endpoint, and complete
responses carry its x-ratelimit-* headers. Without Redis the counters are
kept per replica. The key store now reads key, user, team and member
rpm/tpm limits and the key's max_parallel_requests, and end users' budget
limits are read with their budgets; keys with plain rate limits are no
longer refused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138knkqKpkoSr6PApenZan5

* test(gateway-auth): expect the narrowed unsupported rate limit message

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138knkqKpkoSr6PApenZan5

* perf(gateway): admit rate limits in one redis round trip and send scripts by hash

A key with requests, in-flight and tokens limits made three sequential EVALs
before its request was routed, each carrying the whole Lua script. One script
now counts the request, takes the slot and reserves the tokens, stopping at the
first limit it is over (giving the slot back when tokens are over), with the
same keys and semantics as before. Every rate limit and budget script is sent
with EVALSHA and loaded on NOSCRIPT.

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 14:24:02 -07:00
..
.agents/skills refactor(rust): derive strum VariantArray and string conversions (#45434) 2026-10-08 21:24:49 +00:00
crates perf(gateway): admit rate limits in one redis round trip and send scripts by hash (#47197) 2026-10-19 14:24:02 -07:00
docs feat(loadtest): chat completions parity check between the python proxy and the gateway (#47362) 2026-10-19 12:55:26 -07:00
loadtest feat(loadtest): chat completions parity check between the python proxy and the gateway (#47362) 2026-10-19 12:55:26 -07:00
.dockerignore feat(gateway): load-test kit, Docker build knobs and TCP_NODELAY for the customer proxy (#46806) 2026-10-16 12:42:59 -07:00
.gitignore feat: add LiteLLM Rust workspace with Mistral OCR bridge (#31033) 2026-06-23 13:16:47 -07:00
AGENTS.md feat(rust): add native OpenRouter Responses (#46000) 2026-10-15 11:02:03 -07:00
Cargo.lock fix(gateway): estimate stream usage an openai-compatible provider does not send (#47367) 2026-10-19 12:55:28 -07:00
Cargo.toml feat(gateway): hooks and observers around inference requests, with usage and cost (#47032) 2026-10-19 09:58:43 -07:00
clippy.toml feat(rust): add the MCP gateway (#43470) 2026-09-27 19:37:41 -07:00