"""Простой in-process rate limit для POST /api/admin/llm/* по логину сессии.""" from __future__ import annotations import time from typing import MutableMapping _LAST: MutableMapping[str, float] = {} def reset_llm_rate_limit_for_tests() -> None: _LAST.clear() def llm_post_rate_allow( login: str, interval_sec: float, *, bucket: str = "default" ) -> tuple[bool, float]: """ Возвращает (разрешено, сек_до_следующего_слота). При interval_sec <= 0 ограничения нет. bucket — отдельный счётчик на пользователя (например «chat» и «heavy» не мешают друг другу). """ login_k = (login or "").strip() if interval_sec <= 0 or not login_k: return True, 0.0 key = f"{login_k}\x00{bucket}" now = time.monotonic() last = _LAST.get(key, 0.0) wait = interval_sec - (now - last) if wait > 0: return False, wait _LAST[key] = now return True, 0.0