import threading import time import uuid import requests from app.modules.shared.gigachat.errors import GigaChatError from app.modules.shared.gigachat.settings import GigaChatSettings class GigaChatTokenProvider: def __init__(self, settings: GigaChatSettings) -> None: self._settings = settings self._lock = threading.Lock() self._token: str | None = None self._expires_at_ms: float = 0 def get_access_token(self) -> str: now_ms = time.time() * 1000 with self._lock: if self._token and self._expires_at_ms - 300_000 > now_ms: return self._token token, expires_at = self._fetch_token() with self._lock: self._token = token self._expires_at_ms = expires_at return token def _fetch_token(self) -> tuple[str, float]: if not self._settings.credentials: raise GigaChatError("GIGACHAT_TOKEN is not set") headers = { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json", "Authorization": f"Basic {self._settings.credentials}", "RqUID": str(uuid.uuid4()), } try: response = requests.post( self._settings.auth_url, headers=headers, data=f"scope={self._settings.scope}", timeout=30, verify=self._settings.ssl_verify, ) except requests.RequestException as exc: raise GigaChatError(f"GigaChat auth request failed: {exc}") from exc if response.status_code >= 400: raise GigaChatError(f"GigaChat auth error {response.status_code}: {response.text}") payload = response.json() token = payload.get("access_token") expires_at = float(payload.get("expires_at", 0)) if not token: raise GigaChatError("GigaChat auth: no access_token in response") return token, expires_at