SuperAI_Forecast / backend /startup_utils.py
Thang6822
Update branding to SuperAI Forecast
9734b71
Raw
History Blame
3.52 kB
from __future__ import annotations
from datetime import datetime, timezone
from typing import Any, Awaitable, Callable, Dict, List, MutableMapping, Protocol, Sequence, Tuple
class LoggerLike(Protocol):
def info(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
def warning(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
class AsyncHttpClientLike(Protocol):
async def get(self, url: str) -> Any: ...
class ForecasterLike(Protocol):
is_ready: bool
device: str
def _lazy_load(self) -> Awaitable[None]: ...
def clear_stale_ip_limits(
ip_limits: MutableMapping[str, List[float]],
*,
now: float,
window_seconds: int = 60,
) -> int:
stale_ips = [
ip
for ip, timestamps in ip_limits.items()
if not any(now - timestamp < window_seconds for timestamp in timestamps)
]
for ip in stale_ips:
del ip_limits[ip]
return len(stale_ips)
def build_source_selftest_urls(
twelvedata_api_key: str | None,
finnhub_api_key: str | None,
) -> List[Tuple[str, str]]:
return [
("binance", "https://api.binance.com/api/v3/ping"),
("bybit", "https://api.bybit.com/v5/market/time"),
("coingecko", "https://api.coingecko.com/api/v3/ping"),
("twelvedata", f"https://api.twelvedata.com/api_usage?apikey={twelvedata_api_key or ''}"),
("finnhub", f"https://finnhub.io/api/v1/quote?symbol=AAPL&token={finnhub_api_key or ''}"),
]
async def run_source_selftest(
*,
client: AsyncHttpClientLike,
tests: Sequence[Tuple[str, str]],
startup_sources: MutableMapping[str, Dict[str, Any]],
logger: LoggerLike,
timestamp_provider: Callable[[], str] | None = None,
) -> None:
get_timestamp = timestamp_provider or (lambda: datetime.now(timezone.utc).isoformat())
for name, url in tests:
try:
response = await client.get(url)
startup_sources[name] = {
"reachable": True,
"status_code": response.status_code,
"checked_at": get_timestamp(),
}
logger.info("[selftest] %-15s HTTP %d", name, response.status_code)
except Exception as ex:
startup_sources[name] = {
"reachable": False,
"error": str(ex),
"checked_at": get_timestamp(),
}
logger.warning("[selftest] %-15s FAILED: %s", name, ex)
async def warmup_timesfm(
*,
forecaster: ForecasterLike,
startup_timesfm_state: MutableMapping[str, Any],
logger: LoggerLike,
) -> None:
await warmup_forecaster(
forecaster=forecaster,
startup_state=startup_timesfm_state,
logger=logger,
label="TimesFM",
)
async def warmup_forecaster(
*,
forecaster: ForecasterLike,
startup_state: MutableMapping[str, Any],
logger: LoggerLike,
label: str,
) -> None:
startup_state["warming"] = True
startup_state["last_error"] = None
try:
await forecaster._lazy_load()
startup_state["loaded"] = forecaster.is_ready
startup_state["device"] = forecaster.device
logger.info("[startup] %s warmup finished on %s", label, forecaster.device)
except Exception as ex:
startup_state["loaded"] = False
startup_state["device"] = forecaster.device
startup_state["last_error"] = str(ex)
logger.warning("[startup] %s warmup failed: %s", label, ex)
finally:
startup_state["warming"] = False