agentleFS
lauren-frameworklauren-framework/lauren-ai/skills/llm-rate-limiting/SKILL.md

llm-rate-limiting

Skill0 starsChanged 6 months agoMITView on GitHub

Enforces per-minute request and token-per-minute limits on LLM API calls with automatic retry and exponential backoff. Use when deploying multi-user applications that share an API key, when provider rate limits must not be exceeded, or when per-user quotas are needed.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    When you need per-user limits or a limiter outside LLMConfig:

    line 47

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Use codemap find "SymbolName" to locate any symbol before reading.

Rate Limiting for LLM API Calls

Built-in: RateLimiter

Lauren-AI ships RateLimiter in lauren_ai._cost._rate. Attach it to LLMConfig and the transport automatically respects the limits:

# NOTE: future annotations are allowed, but tool signature types must resolve at import time.
from lauren_ai import RateLimiter, LLMConfig

config = LLMConfig(
    provider="anthropic",
    model="claude-haiku-4-5",
    api_key="...",
    rate_limiter=RateLimiter(
        requests_per_minute=60,
        tokens_per_minute=100_000,
        max_retries=5,
        initial_backoff_s=1.0,
        max_backoff_s=60.0,
        jitter=True,
    ),
)

RateLimiter fields

FieldDefaultDescription
requests_per_minuteNoneMax API calls per 60-second sliding window
tokens_per_minuteNoneMax tokens per 60-second sliding window (token bucket)
max_retries5Retries before raising RateLimitExhaustedError
initial_backoff_s1.0Starting backoff in seconds
max_backoff_s60.0Ceiling for exponential backoff
jitterTrueAdd random jitter to prevent thundering herd

Custom in-process rate limiter

When you need per-user limits or a limiter outside LLMConfig:

import asyncio
import time


class LLMRateLimiter:
    def __init__(self, requests_per_minute: int = 60, tokens_per_minute: int = 100_000):
        self._rpm = requests_per_minute
        self._tpm = tokens_per_minute
        self._request_times: list[float] = []
        self._token_counts: list[tuple[float, int]] = []

    def _clean_old(self, window: float = 60.0) -> None:
        now = time.monotonic()
        self._request_times = [t for t in self._request_times if now - t < window]
        self._token_counts = [(t, c) for t, c in self._token_counts if now - t < window]

    def can_proceed(self, estimated_tokens: int = 0) -> bool:
        self._clean_old()
        if len(self._request_times) >= self._rpm:
            return False
        token_sum = sum(c for _, c in self._token_counts) + estimated_tokens
        if token_sum > self._tpm:
            return False
        return True

    def record_call(self, tokens_used: int = 0) -> None:
        now = time.monotonic()
        self._request_times.append(now)
        self._token_counts.append((now, tokens_used))

    async def acquire(self, estimated_tokens: int = 0) -> None:
        while not self.can_proceed(estimated_tokens):
            await asyncio.sleep(0.1)
        self.record_call(estimated_tokens)

Per-user limits

class PerUserRateLimiter:
    def __init__(self, rpm_per_user: int = 10, tpm_per_user: int = 20_000):
        self._limiters: dict[str, LLMRateLimiter] = {}
        self._rpm = rpm_per_user
        self._tpm = tpm_per_user

    def for_user(self, user_id: str) -> LLMRateLimiter:
        if user_id not in self._limiters:
            self._limiters[user_id] = LLMRateLimiter(self._rpm, self._tpm)
        return self._limiters[user_id]

    async def acquire(self, user_id: str, estimated_tokens: int = 0) -> None:
        await self.for_user(user_id).acquire(estimated_tokens)

Handling RateLimitExhaustedError

from lauren_ai import RateLimitExhaustedError

try:
    result = await runner.run(agent, message)
except RateLimitExhaustedError as exc:
    print(f"Rate limit hit: {exc.limit} RPM, retry after {exc.retry_after}s")
    # Return 429 to caller or queue the request

Pitfalls

  • The built-in RateLimiter uses a sliding window and token-bucket algorithm; it does not coordinate across multiple processes. For multi-worker deployments, use a Redis-backed distributed rate limiter.
  • estimated_tokens in acquire() is a pre-call estimate. For accurate token-per-minute enforcement, call record_call(tokens_used=actual_tokens) after the response returns.
  • The custom LLMRateLimiter above polls every 100ms — acceptable for low concurrency. For high throughput, use asyncio.Event or asyncio.Condition for efficient wake-up.
  • jitter=True in the built-in RateLimiter prevents thundering-herd retries when many concurrent requests all back off and retry simultaneously.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.