Download services/rate_limiter.py from JohnAlbarkaIbrahim/SentimentScope: direct link, hf CLI and curl.
- Browser
- Download file 2.73 kB
-
https://huggingface.co/spaces/JohnAlbarkaIbrahim/SentimentScope/resolve/main/services/rate_limiter.py
- Command line
-
hf download hf://spaces/JohnAlbarkaIbrahim/SentimentScope/services/rate_limiter.py
-
curl -L -o rate_limiter.py https://huggingface.co/spaces/JohnAlbarkaIbrahim/SentimentScope/resolve/main/services/rate_limiter.py
2.73 kB
| """ | |
| services/rate_limiter.py | |
| ββββββββββββββββββββββββ | |
| In-memory session rate limiter enforcing minute and daily rate limits. | |
| """ | |
| import time | |
| from collections import defaultdict | |
| from fastapi import HTTPException | |
| def _is_idle_session(times: list, cutoff: float) -> bool: | |
| """Returns True if the request timestamps indicate idle session beyond cutoff.""" | |
| return not times or max(times) < cutoff | |
| class SessionRateLimiter: | |
| """Tracks per-session requests against RPM and daily quotas.""" | |
| def __init__(self, requests_per_minute: int = 10, max_daily: int = 50): | |
| self.rpm = requests_per_minute | |
| self.max_daily = max_daily | |
| self.requests = defaultdict(list) | |
| self.daily_counts = defaultdict(lambda: {"count": 0, "reset_at": 0}) | |
| def evict_stale(self, max_idle_seconds: int = 90_000) -> None: | |
| """Evicts sessions idle for longer than max_idle_seconds to prevent memory leaks.""" | |
| cutoff = time.time() - max_idle_seconds | |
| stale_req = [sid for sid, times in self.requests.items() if _is_idle_session(times, cutoff)] | |
| stale_daily = [sid for sid, data in self.daily_counts.items() if data["reset_at"] < cutoff] | |
| for sid in stale_req: | |
| del self.requests[sid] | |
| for sid in stale_daily: | |
| del self.daily_counts[sid] | |
| def _check_daily_limit(self, client_id: str, now: float): | |
| """Verifies and updates the daily 24-hour request quota.""" | |
| daily = self.daily_counts[client_id] | |
| if now > daily["reset_at"]: | |
| daily["count"] = 0 | |
| daily["reset_at"] = now + 86400 | |
| if daily["count"] >= self.max_daily: | |
| raise HTTPException( | |
| status_code=429, | |
| detail="Daily limit of 50 messages reached for this session." | |
| ) | |
| def _check_rpm_limit(self, client_id: str, now: float): | |
| """Verifies and updates the rolling 60-second window quota.""" | |
| window = [t for t in self.requests[client_id] if now - t < 60] | |
| self.requests[client_id] = window | |
| if len(window) >= self.rpm: | |
| raise HTTPException( | |
| status_code=429, | |
| detail="Rate limit exceeded. Please wait 1 minute before sending another question." | |
| ) | |
| def check_rate_limit(self, client_id: str): | |
| """Validates incoming request against session eviction, daily, and RPM limits.""" | |
| now = time.time() | |
| self.evict_stale() | |
| self._check_daily_limit(client_id, now) | |
| self._check_rpm_limit(client_id, now) | |
| self.requests[client_id].append(now) | |
| self.daily_counts[client_id]["count"] += 1 | |
| limiter = SessionRateLimiter() | |