Add src/selectel_ml_tui/api/gateway.py
This commit is contained in:
@@ -0,0 +1,98 @@
|
|||||||
|
"""Клиент Gateway API ИИ-роутера Selectel (/aig/gateway/v1/*).
|
||||||
|
|
||||||
|
Аутентификация: IAM-токен проекта (X-Auth-Token) + X-Project-Id и др.
|
||||||
|
Данные: потребление (overview), метрики 24ч, модели, ключи.
|
||||||
|
|
||||||
|
Токен берётся из IamTokenManager; при HTTP 401 выполняется его
|
||||||
|
автоматическое обновление и один повтор запроса.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, cast
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from selectel_ml_tui.api.client import BaseApiClient, SelectelApiError
|
||||||
|
from selectel_ml_tui.auth import IamTokenManager
|
||||||
|
from selectel_ml_tui.models import RouterMetrics, RouterOverview
|
||||||
|
|
||||||
|
GatewayApiError = SelectelApiError
|
||||||
|
|
||||||
|
|
||||||
|
class GatewayClient(BaseApiClient):
|
||||||
|
"""Клиент /aig/gateway/v1/*."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
api_base_url: str,
|
||||||
|
token_provider: IamTokenManager,
|
||||||
|
project_id: str,
|
||||||
|
domain_id: str,
|
||||||
|
timeout: float = 30.0,
|
||||||
|
max_retries: int = 2,
|
||||||
|
transport: httpx.AsyncBaseTransport | None = None,
|
||||||
|
sleep: Any = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(
|
||||||
|
timeout=timeout,
|
||||||
|
max_retries=max_retries,
|
||||||
|
transport=transport,
|
||||||
|
sleep=sleep,
|
||||||
|
)
|
||||||
|
self._base_url = api_base_url.rstrip("/")
|
||||||
|
self._token_provider = token_provider
|
||||||
|
self._project_id = project_id
|
||||||
|
self._domain_id = domain_id
|
||||||
|
|
||||||
|
def _headers(self, token: str) -> dict[str, str]:
|
||||||
|
return {
|
||||||
|
"X-Auth-Token": token,
|
||||||
|
"X-Project-Id": self._project_id,
|
||||||
|
"X-Project-Domain-Id": self._domain_id,
|
||||||
|
"X-Project-Domain-Name": self._domain_id,
|
||||||
|
"X-Roles": "reader",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def _get(self, path: str) -> dict[str, Any]:
|
||||||
|
url = f"{self._base_url}{path}"
|
||||||
|
token = await self._token_provider.get_token()
|
||||||
|
try:
|
||||||
|
response = await self.request("GET", url, headers=self._headers(token))
|
||||||
|
except SelectelApiError as exc:
|
||||||
|
if exc.status_code == 401:
|
||||||
|
self._token_provider.invalidate()
|
||||||
|
token = await self._token_provider.get_token()
|
||||||
|
response = await self.request("GET", url, headers=self._headers(token))
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
data = response.json()
|
||||||
|
if isinstance(data, dict) and data.get("error"):
|
||||||
|
raise GatewayApiError(f"Gateway API: {data['error']}")
|
||||||
|
return cast(dict[str, Any], data)
|
||||||
|
|
||||||
|
async def list_gateways(self) -> list[dict[str, Any]]:
|
||||||
|
"""Список ИИ-роутеров."""
|
||||||
|
data = await self._get("/aig/gateway/v1/gateways")
|
||||||
|
return cast(list[dict[str, Any]], data.get("data", data.get("gateways", [])))
|
||||||
|
|
||||||
|
async def get_overview(self, router_id: str) -> RouterOverview:
|
||||||
|
"""Потребление роутера (вкладка «Обзор»)."""
|
||||||
|
data = await self._get(f"/aig/gateway/v1/gateways/{router_id}/overview")
|
||||||
|
return RouterOverview(raw=data)
|
||||||
|
|
||||||
|
async def get_metrics(self, router_id: str) -> RouterMetrics:
|
||||||
|
"""Метрики роутера за 24 часа (вкладка «Метрики»)."""
|
||||||
|
data = await self._get(f"/aig/gateway/v1/gateways/{router_id}/metrics")
|
||||||
|
return RouterMetrics(raw=data)
|
||||||
|
|
||||||
|
async def list_models(self) -> list[dict[str, Any]]:
|
||||||
|
"""Каталог моделей."""
|
||||||
|
data = await self._get("/aig/gateway/v1/models")
|
||||||
|
return cast(list[dict[str, Any]], data.get("data", data.get("models", [])))
|
||||||
|
|
||||||
|
async def list_keys(self) -> list[dict[str, Any]]:
|
||||||
|
"""Список API-ключей роутера с лимитами."""
|
||||||
|
data = await self._get("/aig/gateway/v1/keys")
|
||||||
|
return cast(list[dict[str, Any]], data.get("data", data.get("keys", [])))
|
||||||
Reference in New Issue
Block a user