From 6e52c8a15d08cc74401df987599a05c723514dfb Mon Sep 17 00:00:00 2001 From: TPTBusiness Date: Sat, 18 Apr 2026 10:00:45 +0200 Subject: [PATCH] fix(kronos): lazy torch import to fix CI ModuleNotFoundError Move top-level `import torch` into _cuda_available() helper so kronos_adapter.py can be imported in CI environments without torch. All device defaults resolved at runtime via lazy detection. Co-Authored-By: Claude Sonnet 4.6 --- predix.py | 8 ++++---- rdagent/components/coder/kronos_adapter.py | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/predix.py b/predix.py index a4ec9892..f7032973 100644 --- a/predix.py +++ b/predix.py @@ -1615,8 +1615,8 @@ def kronos_factor( predix kronos-eval - Evaluate Kronos as model and compute IC vs LightGBM predix top - Show top factors by IC """ - import torch as _torch - _device = device or ("cuda" if _torch.cuda.is_available() else "cpu") + from rdagent.components.coder.kronos_adapter import _cuda_available + _device = device or ("cuda" if _cuda_available() else "cpu") _stride = stride or pred data_path = Path("git_ignore_folder/factor_implementation_source_data/intraday_pv.h5") @@ -1697,8 +1697,8 @@ def kronos_eval( predix kronos-factor - Generate Kronos factor for the factor pipeline predix best - Show top strategies """ - import torch as _torch - _device = device or ("cuda" if _torch.cuda.is_available() else "cpu") + from rdagent.components.coder.kronos_adapter import _cuda_available + _device = device or ("cuda" if _cuda_available() else "cpu") _stride = stride or pred data_path = Path("git_ignore_folder/factor_implementation_source_data/intraday_pv.h5") diff --git a/rdagent/components/coder/kronos_adapter.py b/rdagent/components/coder/kronos_adapter.py index b7b15d55..ab115786 100644 --- a/rdagent/components/coder/kronos_adapter.py +++ b/rdagent/components/coder/kronos_adapter.py @@ -18,10 +18,17 @@ from typing import Optional import numpy as np import pandas as pd -import torch from rdagent.log import rdagent_logger as logger + +def _cuda_available() -> bool: + try: + import torch + return torch.cuda.is_available() + except ImportError: + return False + KRONOS_REPO = Path.home() / "Kronos" _KRONOS_AVAILABLE: Optional[bool] = None @@ -67,8 +74,8 @@ class KronosAdapter: MODEL_ID = "NeoQuasar/Kronos-mini" TOKENIZER_ID = "NeoQuasar/Kronos-Tokenizer-2k" - def __init__(self, device: str = "cuda" if torch.cuda.is_available() else "cpu", max_context: int = 512): - self.device = device + def __init__(self, device: Optional[str] = None, max_context: int = 512): + self.device = device or ("cuda" if _cuda_available() else "cpu") self.max_context = max_context self._predictor = None @@ -152,7 +159,7 @@ def build_kronos_factor( context_bars: int = 512, pred_bars: int = 96, stride_bars: int = 96, - device: str = "cuda" if torch.cuda.is_available() else "cpu", + device: Optional[str] = None, ) -> pd.DataFrame: """ Generate the Kronos predicted-return factor for all EUR/USD 1-min bars. @@ -165,6 +172,7 @@ def build_kronos_factor( Returns: MultiIndex (datetime, instrument) DataFrame with column "KronosPredReturn". """ + device = device or ("cuda" if _cuda_available() else "cpu") logger.info(f"Loading data from {hdf5_path}...") raw = pd.read_hdf(hdf5_path, key="data") @@ -217,7 +225,7 @@ def evaluate_kronos_model( context_bars: int = 512, pred_bars: int = 30, stride_bars: int = 30, - device: str = "cuda" if torch.cuda.is_available() else "cpu", + device: Optional[str] = None, ) -> dict: """ Evaluate Kronos as a standalone model (Option B, alongside LightGBM). @@ -228,6 +236,7 @@ def evaluate_kronos_model( Returns: dict with keys: IC_mean, IC_std, IC_IR (IC / std), hit_rate, n_predictions """ + device = device or ("cuda" if _cuda_available() else "cpu") raw = pd.read_hdf(hdf5_path, key="data") instrument = raw.index.get_level_values("instrument").unique()[0] df = raw.xs(instrument, level="instrument")