A real API request must clear several orthogonal admission gates at once: a rate ceiling (requests per minute), a concurrency ceiling (slots in flight), and a cost budget (tokens or compute units per window). WithoutDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/AmeyaBorkar/throttlekit/llms.txt
Use this file to discover all available pages before exploring further.
unifiedAdmission, you must chain these checks manually and carefully manage concurrency slot leaks when a downstream axis denies. The TALE engine collapses all three into one admit() call with a single combined Decision, a single release hook, and a single observable bindingAxis that tells you which constraint actually bit.
unifiedAdmission()
UnifiedAdmissionOptions
The rate axis. Checked as
rate.check(key, 1).The concurrency axis, from
adaptiveConcurrency(...). State is in-process; the slot is acquired synchronously.The cost axis. Checked as
cost.check(key, opts.cost)."sequential" (default) evaluates axes in order; first deny short-circuits. "lua-fused" collapses rate + cost into one Redis EVALSHA for atomic evaluation. Concurrency always remains in-process. Requires the fused option group; supports GCRA + token-bucket only."marginal" (default): admit when every axis independently has slack. "joint-lp": additionally apply a bid-price filter — see the joint-LP section below.Injected time source, forwarded to the concurrency-lease shim. Defaults to the system clock.
The Decision Algebra
combineDecisions composes any two Decision objects into one via:
ALLOW_FULL as the identity element. These properties license the lua-fused path to reorder its checks and still produce a result byte-identical to sequential.
The evaluation order — concurrency → rate → cost — is chosen purely to minimize short-circuit cost (concurrency is in-process, the cheapest to evaluate). The combined result is order-independent by commutativity.
UnifiedAdmitOptions
Key passed to the rate and cost axes. Defaults to
"" (a single global bucket).Cost weight passed to the cost axis. Defaults to
1.Request value for the joint-LP bid-price test. Ignored unless
policy: "joint-lp". Defaults to 1.Expected service time for the 3-axis joint-LP concurrency term. Ignored unless
policy: "joint-lp" with a concurrency budget configured. Defaults to 0 (no concurrency term).UnifiedAdmitter Interface
admit()— async; works for any backend mix.admitSync()— synchronous; throws when any configured axis uses an async-only store, or whenbackend: "lua-fused"is active.lastDecisions()— returns a snapshot of the most recentadmitcall’s per-axisDecisionobjects. Unconfigured or short-circuited axes areundefined, letting you identify the binding axis precisely.
UnifiedAxis type is "rate" | "concurrency" | "cost".
Concurrency Lifecycle
When the concurrency axis admits, the returnedrelease function holds the concurrency slot. You must call release() when the work finishes — from a finally block, a response finish event, or similar. release is idempotent.
dropped: false) and the returned release becomes a no-op.
Token Budget
tokenBudget is the post-hoc cost meter for LLM streaming: debit actual tokens as they are produced, not reserves at admission.
tokens = 1) achieves zero overshoot — the budget stops precisely at L. The debit that crosses L is admitted in full; the next debit is refused.
distributedTokenBudget is the fleet-shared, Store-backed version: the same stop-at-boundary rule run as an atomic read-modify-write against a shared counter, enforcing one budget L across every gateway with a per-token overshoot of 0 independent of fleet size.
learnedReservation and predictiveReservation
These TALE Layer 2 and 3 primitives learn the per-request token reservation that paces admission over a tokenBudget, minimizing the asymmetric newsvendor / pinball loss.
predictiveReservation extends this with a Hedge meta-learner that blends a per-request output-length prediction against the robust learnedReservation quantile. Accurate predictions drive cost to the clairvoyant optimum (consistency); adversarial predictions fall back to the no-regret quantile (robustness). Safety is unchanged in both cases.
Fairness Primitives
weightedMaxMin
Batch allocation of an integer limit across tenants with per-tenant demands and weights. Returns the exact, work-conserving weighted max-min split as an array of integer credits.
weightedFairShare
The streaming, per-arrival face of weightedMaxMin: a global fixed-window budget split so each tenant’s ceiling is proportional to its weight. Returns a WeightedFairShareLimiter.
adaptiveThrottle — Google SRE Client-Side Load Shedding
adaptiveThrottle implements the Google SRE Book Chapter 21 client-side adaptive throttling formula. A client that keeps hammering an overloaded backend only deepens the overload; this sheds a growing fraction of requests locally (before they leave the client) based on the backend’s recent accept rate:
k parameter controls aggressiveness: K = 2 (the SRE Book default) begins shedding once the backend is rejecting more than 50% of requests. The priority argument to request(priority) scales the shed probability by (1 − priority) — a priority of 1 is never shed.
Joint-LP Bid-Price Filter (policy: "joint-lp")
The default "marginal" policy admits whenever each axis independently has slack, but is blind to the joint value of spending a scarce cost unit on a low-value request. The "joint-lp" policy prices the scarce budgets and rejects requests whose value doesn’t clear the bid price:
p_R (rate) and p_C (cost) are the LP dual variables of the revenue-management fluid relaxation. ThrottleKit solves this zero-dependency via solveFluidLp:
"marginal" and cannot break any safety property.
Online dual refinement is available with
jointLp.adaptive: { sampleWindow: N }. During the first N policy-evaluated requests the filter prices with the construction prior while tallying the observed (cost, value) mixture; at the window boundary it re-solves and adopts the learned duals only if they strictly beat the prior on the buffered sample — otherwise keeps the prior. This guarantees never-worse-than-prior on the observed sample.Full LLM Gateway Example
FAQ: When should I use admitSync vs admit?
FAQ: When should I use admitSync vs admit?
Use
admitSync only when every configured axis uses an in-process synchronous store (e.g. MemoryStore), and backend is "sequential". With a Redis-backed rate or cost limiter, use the async admit(). admitSync with backend: "lua-fused" always throws.FAQ: What does policyDenied mean on the result?
FAQ: What does policyDenied mean on the result?
When
policy: "joint-lp" is configured and the bid-price filter rejects a request (all axes had slack, but value < p_R + p_C × cost), the returned UnifiedAdmission has policyDenied: true and bindingAxis is absent. No axis budget was consumed. Use this flag to distinguish a policy filter rejection from a true resource exhaustion.