Rate limiting package
Project description
Rate Limiting Algorithms
This project adheres to Semantic Versioning
Algorithms
| Algorithms | Sync | Async |
|---|---|---|
| Leaky Bucket | Yes | Yes |
| Token Bucket | Yes | Yes |
| Generic Cell Rate Algorithm | Yes | Yes |
| LLM-Token | Yes | Yes |
[!NOTE]
Implementations will be single-threaded, blocking requests (or the equivalent) with burst capabilities. With asyncio, we use non-blocking cooperative multitasking, not preemptive multi-threading
Development
Setup uv-based virtual environment
# Install uv
# for a mac or linux
brew install uv
# OPTIONAL: or
curl -LsSf https://astral.sh/uv/install.sh | sh
# python version are automatically downloaded as needed or: uv python install 3.12
uv venv rate --python 3.12
# to activate the virtual environment
source .venv/bin/activate
# to deactivate the virtual environment
deactivate
Create lock file + requirements.txt
# after pyproject.toml is created
uv lock
uv export -o requirements.txt --quiet
Upgrade dependencies
# can use sync or lock
uv sync --upgrade
or
# to upgrade a specific package
uv lock --upgrade-package requests
Usage
[!IMPORTANT] These are special use cases. The general use cases are in the
examples/folder
LLM Token-Based Rate Limiting
import random
import time
from typing import Callable
from limitor.base import SyncRateLimit
from limitor.configs import BucketConfig
from limitor.leaky_bucket.core import SyncLeakyBucket
def rate_limit(capacity: int = 10, seconds: float = 1, bucket_cls: type[SyncRateLimit] = SyncLeakyBucket) -> Callable:
bucket = bucket_cls(BucketConfig(capacity=capacity, seconds=seconds))
def decorator(func):
def wrapper(*args, **kwargs):
amount = kwargs.get("amount", 1)
bucket.acquire(amount=amount)
return func(*args, **kwargs)
return wrapper
return decorator
# limit of 100,000 tokens per second
@rate_limit(capacity=100_000, seconds=1)
def process_request(amount=1):
print(f"This is a rate-limited function: {time.strftime('%X')} - {amount} tokens")
for _ in range(100):
# generate random prompt tokens between 5,000 and 30,000 for 100 sample requests
llm_prompt_tokens = random.randint(5_000, 30_000)
try:
process_request(amount=llm_prompt_tokens)
except Exception as error:
print(f"Rate limit exceeded: {error}")
With User-Specific Rate Limits + Cache
import time
from typing import Optional
from cachetools import LRUCache, TTLCache
from limitor.base import SyncRateLimit
from limitor.configs import BucketConfig
from limitor.leaky_bucket.core import (
AsyncLeakyBucket,
SyncLeakyBucket,
)
def _get_user_cache(max_users, ttl):
if ttl is not None:
return TTLCache(maxsize=max_users, ttl=ttl)
return LRUCache(maxsize=max_users)
def rate_limit_per_user(capacity=10, seconds=1, max_users=1000, ttl=None, bucket_cls: type[SyncRateLimit] = SyncLeakyBucket):
buckets = _get_user_cache(max_users, ttl)
global_bucket = bucket_cls(BucketConfig(capacity=capacity, seconds=seconds))
def decorator(func):
# optional use_id. if not set, it will default to a regular global rate limiter
# if user_id is not set, this means the max_users / ttl parameters will be ignored
def wrapper(*args, user_id=None, **kwargs):
if user_id is None:
bucket = global_bucket
else:
if user_id not in buckets:
buckets[user_id] = bucket_cls(BucketConfig(capacity=capacity, seconds=seconds))
bucket = buckets[user_id]
with bucket:
return func(user_id, *args, **kwargs)
return wrapper
return decorator
@rate_limit_per_user(capacity=2, seconds=1, max_users=3, ttl=600) # TTLCache: 10 min/user
def something_user(user_id):
print(f"User {user_id} called at {time.strftime('%X')}")
for _ in range(20):
try:
x = 1 if _ % 2 == 0 else 0
something_user(user_id=x)
except Exception as error:
print(f"Rate limit exceeded: {error}")
Project details
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 limitor-0.3.2.tar.gz.
File metadata
- Download URL: limitor-0.3.2.tar.gz
- Upload date:
- Size: 110.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.8.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f91e5358ae548a5dc1881a49cd96b652f4693294654ea470a2d410b01cc226bd
|
|
| MD5 |
ce85ab3e63110471f284f8e65ad5c407
|
|
| BLAKE2b-256 |
5a2f470d177c7af1b29122980139c016eea1a4cf45462d51f31dcb64cf3996bc
|
File details
Details for the file limitor-0.3.2-py3-none-any.whl.
File metadata
- Download URL: limitor-0.3.2-py3-none-any.whl
- Upload date:
- Size: 20.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.8.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49854d403500dbc8e063318b7f87226a987ef2b89b1283c4d29ac47a9031f3d6
|
|
| MD5 |
aad0d86e0aaf65d3b149890fd9a9c087
|
|
| BLAKE2b-256 |
5b7b2951abaf47ac818c6ef7bcfe4e3367f16fd1d78793d6c2aae95efb3ac610
|