Add RateLimiter

This commit is contained in:
itqop 2025-06-28 20:06:40 +03:00
parent acaa9cae29
commit 4585d181f8
3 changed files with 80 additions and 1 deletions

View File

@ -20,6 +20,15 @@ class CLIInterface:
print(f"📊 Максимум заявок: {settings.application.max_applications}")
ai_status = "✅ Доступен" if settings.enable_ai_matching() else "❌ Недоступен"
print(f"🤖 Gemini AI: {ai_status}")
if settings.enable_ai_matching():
try:
from ..services.gemini_service import GeminiAIService
gemini_service = GeminiAIService()
print(f" {gemini_service.get_api_status()}")
except Exception:
pass
browser_mode = "Фоновый" if settings.browser.headless else "Видимый"
print(f"🌐 Режим браузера: {browser_mode}")
@ -102,6 +111,14 @@ class CLIInterface:
else:
print("\n😕 Заявки не были отправлены")
if settings.enable_ai_matching():
try:
from ..services.gemini_service import GeminiAIService
gemini_service = GeminiAIService()
print(f"\n{gemini_service.get_api_status()}")
except Exception:
pass
print(UIFormatter.create_separator(long=True))
@staticmethod

View File

@ -76,6 +76,9 @@ class GeminiConfig:
base_url: str = AppConstants.GEMINI_BASE_URL
match_threshold: float = AppConstants.DEFAULT_AI_THRESHOLD
max_requests_per_minute: int = 15
rate_limit_window_seconds: int = 61
@dataclass
class ResumeConfig:

View File

@ -1,7 +1,9 @@
import json
import requests
import logging
import time
from typing import Dict, Optional, Tuple, List
from collections import deque
import traceback
from pathlib import Path
@ -11,6 +13,48 @@ from ..models.vacancy import Vacancy
logger = logging.getLogger(__name__)
class RateLimiter:
"""Ограничитель скорости запросов к API"""
def __init__(self, max_requests: int, window_seconds: int):
self.max_requests = max_requests
self.window_seconds = window_seconds
self.request_times = deque()
def wait_if_needed(self) -> None:
"""Ожидание если превышен лимит запросов"""
current_time = time.time()
while (self.request_times and
current_time - self.request_times[0] >= self.window_seconds):
self.request_times.popleft()
if len(self.request_times) >= self.max_requests:
wait_time = self.window_seconds - (current_time - self.request_times[0])
if wait_time > 0:
logger.info(f"⏳ Достигнут лимит {self.max_requests} запросов в минуту. "
f"Ожидание {wait_time:.1f} секунд...")
time.sleep(wait_time + 0.1)
self.request_times.append(current_time)
def get_remaining_requests(self) -> int:
"""Количество оставшихся запросов в текущем окне"""
current_time = time.time()
while (self.request_times and
current_time - self.request_times[0] >= self.window_seconds):
self.request_times.popleft()
return max(0, self.max_requests - len(self.request_times))
def get_status(self) -> str:
"""Статус rate limiter для логирования"""
remaining = self.get_remaining_requests()
return (f"📊 API лимит: {remaining}/{self.max_requests} запросов осталось "
f"(окно {self.window_seconds}с)")
class GeminiApiClient:
"""Клиент для работы с Gemini API"""
@ -19,8 +63,17 @@ class GeminiApiClient:
self.base_url = AppConstants.GEMINI_BASE_URL
self.model = AppConstants.GEMINI_MODEL
# Инициализируем rate limiter
self.rate_limiter = RateLimiter(
max_requests=settings.gemini.max_requests_per_minute,
window_seconds=settings.gemini.rate_limit_window_seconds
)
def generate_content(self, prompt: str) -> Optional[Dict]:
"""Генерация контента через Gemini API"""
# Применяем rate limiting
self.rate_limiter.wait_if_needed()
url = f"{self.base_url}/models/{self.model}:generateContent"
headers = {"Content-Type": "application/json"}
@ -35,7 +88,7 @@ class GeminiApiClient:
}
try:
logger.info("Отправка запроса к Gemini API")
logger.info(f"Отправка запроса к Gemini API. {self.rate_limiter.get_status()}")
response = requests.post(
url,
headers=headers,
@ -422,3 +475,9 @@ class GeminiAIService:
С уважением,
Telegram @itqen"""
def get_api_status(self) -> str:
"""Получение статуса API лимитов"""
if not self.api_client:
return "❌ Gemini API недоступен"
return self.api_client.rate_limiter.get_status()