From 976b86664faaf1ff8c1c19a42d3a5f5e452fcce3 Mon Sep 17 00:00:00 2001 From: Linlang <30293408+SunsetWolf@users.noreply.github.com> Date: Sat, 28 Jun 2025 20:01:14 +0800 Subject: [PATCH] fix: fix mount (#1001) * docs: document extra_volumes dict format in DockerConf * feat: accept dict values in extra_volumes to specify bind and mode * fix: skip invalid PDF reports to prevent infinite loop * from break to raise self.LoopTerminationError * format with black --------- Co-authored-by: Young --- rdagent/app/qlib_rd_loop/factor_from_report.py | 9 ++++++++- rdagent/utils/env.py | 12 +++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/rdagent/app/qlib_rd_loop/factor_from_report.py b/rdagent/app/qlib_rd_loop/factor_from_report.py index de60190c..f2b35bd1 100644 --- a/rdagent/app/qlib_rd_loop/factor_from_report.py +++ b/rdagent/app/qlib_rd_loop/factor_from_report.py @@ -105,14 +105,21 @@ class FactorReportLoop(FactorRDLoop, metaclass=LoopMeta): self.judge_pdf_data_items = [i for i in Path(report_folder).rglob("*.pdf")] self.loop_n = min(len(self.judge_pdf_data_items), FACTOR_FROM_REPORT_PROP_SETTING.report_limit) + self.shift_report = ( + 0 # some reports does not contain viable factor, so we ship some of them to avoid infinite loop + ) async def direct_exp_gen(self, prev_out: dict[str, Any]): while True: if self.get_unfinished_loop_cnt(self.loop_idx) < RD_AGENT_SETTINGS.get_max_parallel(): - report_file_path = self.judge_pdf_data_items[self.loop_idx] + report_file_path = self.judge_pdf_data_items[self.loop_idx + self.shift_report] logger.info(f"Processing number {self.loop_idx} report: {report_file_path}") exp = extract_hypothesis_and_exp_from_reports(str(report_file_path)) if exp is None: + self.shift_report += 1 + self.loop_n -= 1 + if self.loop_n < 0: # NOTE: on every step, we self.loop_n -= 1 at first. + raise self.LoopTerminationError("Reach stop criterion and stop loop") continue exp.based_experiments = [QlibFactorExperiment(sub_tasks=[], hypothesis=exp.hypothesis)] + [ t[0] for t in self.trace.hist if t[1] diff --git a/rdagent/utils/env.py b/rdagent/utils/env.py index f76abe1a..709a6631 100644 --- a/rdagent/utils/env.py +++ b/rdagent/utils/env.py @@ -602,6 +602,10 @@ class DockerConf(EnvConf): default_entry: str # the entry point of the image extra_volumes: dict = {} + """It accept a dict of volumes, which can be either + {: } or + {: {"bind": , "mode": }} + """ extra_volume_mode: str = "ro" # by default. only the mount_path should be writable, others are changed to read-only # Sometime, we need maintain some extra data for the workspace. # And the extra data may be shared and the downloading can be time consuming. @@ -662,7 +666,9 @@ class QlibDockerConf(DockerConf): image: str = "local_qlib:latest" mount_path: str = "/workspace/qlib_workspace/" default_entry: str = "qrun conf.yaml" - extra_volumes: dict = {str(Path("~/.qlib/").expanduser().resolve().absolute()): "/root/.qlib/"} + extra_volumes: dict = { + str(Path("~/.qlib/").expanduser().resolve().absolute()): {"bind": "/root/.qlib/", "mode": "rw"} + } shm_size: str | None = "16g" enable_gpu: bool = True enable_cache: bool = False @@ -854,12 +860,12 @@ class DockerEnv(Env[DockerConf]): if self.conf.extra_volumes is not None: for lp, rp in self.conf.extra_volumes.items(): - volumes[lp] = {"bind": rp, "mode": self.conf.extra_volume_mode} + volumes[lp] = rp if isinstance(rp, dict) else {"bind": rp, "mode": self.conf.extra_volume_mode} cache_path = "/tmp/sample" if "/sample/" in "".join(self.conf.extra_volumes.keys()) else "/tmp/full" Path(cache_path).mkdir(parents=True, exist_ok=True) volumes[cache_path] = {"bind": T("scenarios.data_science.share:scen.cache_path").r(), "mode": "rw"} for lp, rp in running_extra_volume.items(): - volumes[lp] = {"bind": rp, "mode": self.conf.extra_volume_mode} + volumes[lp] = rp if isinstance(rp, dict) else {"bind": rp, "mode": self.conf.extra_volume_mode} volumes = normalize_volumes(cast(dict[str, str | dict[str, str]], volumes), self.conf.mount_path)