Spaces:
Running
Running
| from __future__ import annotations | |
| import threading | |
| import time | |
| from collections import defaultdict, deque | |
| from .config import get_settings | |
| class RateLimitExceeded(ValueError): | |
| pass | |
| class SlidingWindowLimiter: | |
| def __init__(self): | |
| self.limit = get_settings().queries_per_hour_per_ip | |
| self.events: dict[str, deque[float]] = defaultdict(deque) | |
| self.lock = threading.Lock() | |
| def check(self, key: str) -> None: | |
| now = time.time() | |
| cutoff = now - 3600 | |
| with self.lock: | |
| q = self.events[key] | |
| while q and q[0] < cutoff: | |
| q.popleft() | |
| if len(q) >= self.limit: | |
| raise RateLimitExceeded(f"Rate limit reached ({self.limit} operations/hour for this client).") | |
| q.append(now) | |
| limiter = SlidingWindowLimiter() | |